zephr: consume hover event on top-most ui elements

This commit is contained in:
2024-01-20 06:39:49 +02:00
parent 156021347f
commit 3ece69dfc6
7 changed files with 1230 additions and 1181 deletions
+15 -15
View File
@@ -12,46 +12,46 @@ create_shader :: proc(vertex_path: string, fragment_path: string) -> (Shader, bo
shader: Shader
program, success := gl.load_shaders(vertex_path, fragment_path)
shader.program = program;
shader.program = program
return shader, success;
return shader, success
}
use_shader :: proc(shader: Shader) {
gl.UseProgram(shader.program);
gl.UseProgram(shader.program)
}
set_mat4f :: proc(shader: Shader, name: cstring, mat4: Mat4, transpose: bool = false) {
mat4 := mat4
loc := gl.GetUniformLocation(shader.program, name);
loc := gl.GetUniformLocation(shader.program, name)
gl.UniformMatrix4fv(loc, 1, transpose, raw_data(&mat4))
}
set_float :: proc(shader: Shader, name: cstring, val: f32) {
loc := gl.GetUniformLocation(shader.program, name);
gl.Uniform1f(loc, val);
loc := gl.GetUniformLocation(shader.program, name)
gl.Uniform1f(loc, val)
}
set_int :: proc(shader: Shader, name: cstring, val: i32) {
loc := gl.GetUniformLocation(shader.program, name);
gl.Uniform1i(loc, val);
loc := gl.GetUniformLocation(shader.program, name)
gl.Uniform1i(loc, val)
}
set_bool :: proc(shader: Shader, name: cstring, val: bool) {
set_int(shader, name, cast(i32)val);
set_int(shader, name, cast(i32)val)
}
set_vec2f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32) {
loc := gl.GetUniformLocation(shader.program, name);
gl.Uniform2f(loc, val1, val2);
loc := gl.GetUniformLocation(shader.program, name)
gl.Uniform2f(loc, val1, val2)
}
set_vec3f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32, val3: f32) {
loc := gl.GetUniformLocation(shader.program, name);
gl.Uniform3f(loc, val1, val2, val3);
loc := gl.GetUniformLocation(shader.program, name)
gl.Uniform3f(loc, val1, val2, val3)
}
set_vec4f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32, val3: f32, val4: f32) {
loc := gl.GetUniformLocation(shader.program, name);
gl.Uniform4f(loc, val1, val2, val3, val4);
loc := gl.GetUniformLocation(shader.program, name)
gl.Uniform4f(loc, val1, val2, val3, val4)
}
+155 -155
View File
@@ -45,14 +45,14 @@ font_instance_vbo : u32
init_freetype :: proc(font_path: cstring) -> i32 {
ft: FT.Library;
ft: FT.Library
if (FT.Init_FreeType(&ft) != 0) {
return -1;
return -1
}
face: FT.Face;
face: FT.Face
if (FT.New_Face(ft, font_path, 0, &face) != 0) {
return -2;
return -2
}
// sets the variable font to be bold
@@ -66,301 +66,301 @@ init_freetype :: proc(font_path: cstring) -> i32 {
/* FT_Done_MM_Var(ft, mm); */
/* } */
FT.Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE);
FT.Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE)
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
pen_x, pen_y: u32
/* FT_UInt glyph_idx; */
/* FT_ULong c = FT_Get_First_Char(face, &glyph_idx); */
tex_width, tex_height: u32;
tex_width, tex_height: u32
for i in 32..<128 {
if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) {
fmt.printf("[ERROR]: failed to load glyph for char '0x%x'\n", i);
fmt.eprintf("[ERROR]: failed to load glyph for char '0x%x'\n", i)
}
/* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */
tex_width += face.glyph.bitmap.width + 1;
tex_height = max(tex_height, face.glyph.bitmap.rows);
tex_width += face.glyph.bitmap.width + 1
tex_height = max(tex_height, face.glyph.bitmap.rows)
}
pixels := make([dynamic]u8, tex_width * tex_height);
pixels := make([dynamic]u8, tex_width * tex_height)
defer delete(pixels)
for i in 32..<128 {
/* while (glyph_idx) { */
if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) {
fmt.printf("[ERROR]: failed to load glyph for char '0x%x'\n", i);
fmt.eprintf("[ERROR]: failed to load glyph for char '0x%x'\n", i)
}
/* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */
bmp := &face.glyph.bitmap;
bmp := &face.glyph.bitmap
if (pen_x + bmp.width >= tex_width) {
pen_x = 0;
pen_y += cast(u32)(1 + (face.size.metrics.height >> 6));
pen_x = 0
pen_y += cast(u32)(1 + (face.size.metrics.height >> 6))
}
for row in 0..<bmp.rows {
for col in 0..<bmp.width {
x := pen_x + col;
y := pen_y + row;
pixels[y * tex_width + x] = bmp.buffer[row * cast(u32)bmp.pitch + col];
x := pen_x + col
y := pen_y + row
pixels[y * tex_width + x] = bmp.buffer[row * cast(u32)bmp.pitch + col]
}
}
atlas_x0 := cast(f32)pen_x / cast(f32)tex_width;
atlas_y0 := cast(f32)pen_y / cast(f32)tex_height;
atlas_x1 := cast(f32)(pen_x + bmp.width) / cast(f32)tex_width;
atlas_y1 := cast(f32)(pen_y + bmp.rows) / cast(f32)tex_height;
atlas_x0 := cast(f32)pen_x / cast(f32)tex_width
atlas_y0 := cast(f32)pen_y / cast(f32)tex_height
atlas_x1 := cast(f32)(pen_x + bmp.width) / cast(f32)tex_width
atlas_y1 := cast(f32)(pen_y + bmp.rows) / cast(f32)tex_height
top_left := Vec2{ atlas_x0, atlas_y1 };
top_right := Vec2{ atlas_x1, atlas_y1 };
bottom_right := Vec2{ atlas_x1, atlas_y0 };
bottom_left := Vec2{ atlas_x0, atlas_y0 };
top_left := Vec2{ atlas_x0, atlas_y1 }
top_right := Vec2{ atlas_x1, atlas_y1 }
bottom_right := Vec2{ atlas_x1, atlas_y0 }
bottom_left := Vec2{ atlas_x0, atlas_y0 }
character: Character
character.tex_coords[0] = top_left;
character.tex_coords[1] = top_right;
character.tex_coords[2] = bottom_right;
character.tex_coords[3] = bottom_left;
character.advance = cast(u32)face.glyph.advance.x;
character.size = Vec2{ cast(f32)face.glyph.bitmap.width, cast(f32)face.glyph.bitmap.rows };
character.bearing = Vec2{ cast(f32)face.glyph.bitmap_left, cast(f32)face.glyph.bitmap_top };
character.tex_coords[0] = top_left
character.tex_coords[1] = top_right
character.tex_coords[2] = bottom_right
character.tex_coords[3] = bottom_left
character.advance = cast(u32)face.glyph.advance.x
character.size = Vec2{ cast(f32)face.glyph.bitmap.width, cast(f32)face.glyph.bitmap.rows }
character.bearing = Vec2{ cast(f32)face.glyph.bitmap_left, cast(f32)face.glyph.bitmap_top }
zephr_ctx.font.characters[i] = character;
zephr_ctx.font.characters[i] = character
pen_x += bmp.width + 1;
pen_x += bmp.width + 1
/* c = FT_Get_Next_Char(face, c, &glyph_idx); */
}
texture: TextureId;
gl.GenTextures(1, &texture);
gl.BindTexture(gl.TEXTURE_2D, texture);
texture: TextureId
gl.GenTextures(1, &texture)
gl.BindTexture(gl.TEXTURE_2D, texture)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, cast(i32)tex_width,
cast(i32)tex_height, 0, gl.RED, gl.UNSIGNED_BYTE,
raw_data(pixels))
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
zephr_ctx.font.atlas_tex_id = texture;
zephr_ctx.font.atlas_tex_id = texture
gl.BindTexture(gl.TEXTURE_2D, 0);
gl.BindTexture(gl.TEXTURE_2D, 0)
FT.Done_Face(face);
FT.Done_FreeType(ft);
FT.Done_Face(face)
FT.Done_FreeType(ft)
return 0;
return 0
}
init_fonts :: proc(font_path: cstring) -> i32 {
font_vbo, font_ebo: u32;
font_vbo, font_ebo: u32
res := init_freetype(font_path);
res := init_freetype(font_path)
if (res != 0) {
return res;
return res
}
l_font_shader, success := create_shader("shaders/font.vert", "shaders/font.frag");
l_font_shader, success := create_shader("shaders/font.vert", "shaders/font.frag")
if (!success) {
fmt.println("[FATAL] Failed to create font shader")
return -2;
return -2
}
font_shader = l_font_shader;
font_shader = l_font_shader
gl.GenVertexArrays(1, &font_vao);
gl.GenBuffers(1, &font_vbo);
gl.GenBuffers(1, &font_instance_vbo);
gl.GenBuffers(1, &font_ebo);
gl.GenVertexArrays(1, &font_vao)
gl.GenBuffers(1, &font_vbo)
gl.GenBuffers(1, &font_instance_vbo)
gl.GenBuffers(1, &font_ebo)
quad_vertices := [4][2]f32 {
{0.0, 1.0}, // top left
{1.0, 1.0}, // top right
{1.0, 0.0}, // bottom right
{0.0, 0.0}, // bottom left
};
}
quad_indices := [6]u32 {
0, 1, 2,
2, 3, 0,
};
}
gl.BindVertexArray(font_vao);
gl.BindVertexArray(font_vao)
// font quad vbo
gl.BindBuffer(gl.ARRAY_BUFFER, font_vbo);
gl.BufferData(gl.ARRAY_BUFFER, size_of(quad_vertices), &quad_vertices, gl.STATIC_DRAW);
gl.EnableVertexAttribArray(0);
gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 2 * size_of(f32), 0);
gl.BindBuffer(gl.ARRAY_BUFFER, font_vbo)
gl.BufferData(gl.ARRAY_BUFFER, size_of(quad_vertices), &quad_vertices, gl.STATIC_DRAW)
gl.EnableVertexAttribArray(0)
gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 2 * size_of(f32), 0)
// font instance vbo
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo);
gl.EnableVertexAttribArray(1);
gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), 0);
gl.VertexAttribDivisor(1, 1);
gl.EnableVertexAttribArray(2);
gl.VertexAttribIPointer(2, 1, gl.INT, size_of(GlyphInstance), size_of(Vec4));
gl.VertexAttribDivisor(2, 1);
gl.EnableVertexAttribArray(3);
gl.VertexAttribPointer(3, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) + size_of(i32)));
gl.VertexAttribDivisor(3, 1);
gl.EnableVertexAttribArray(4);
gl.EnableVertexAttribArray(5);
gl.EnableVertexAttribArray(6);
gl.EnableVertexAttribArray(7);
gl.VertexAttribPointer(4, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 2 + size_of(i32)));
gl.VertexAttribPointer(5, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 3 + size_of(i32)));
gl.VertexAttribPointer(6, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 4 + size_of(i32)));
gl.VertexAttribPointer(7, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 5 + size_of(i32)));
gl.VertexAttribDivisor(4, 1);
gl.VertexAttribDivisor(5, 1);
gl.VertexAttribDivisor(6, 1);
gl.VertexAttribDivisor(7, 1);
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo)
gl.EnableVertexAttribArray(1)
gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), 0)
gl.VertexAttribDivisor(1, 1)
gl.EnableVertexAttribArray(2)
gl.VertexAttribIPointer(2, 1, gl.INT, size_of(GlyphInstance), size_of(Vec4))
gl.VertexAttribDivisor(2, 1)
gl.EnableVertexAttribArray(3)
gl.VertexAttribPointer(3, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) + size_of(i32)))
gl.VertexAttribDivisor(3, 1)
gl.EnableVertexAttribArray(4)
gl.EnableVertexAttribArray(5)
gl.EnableVertexAttribArray(6)
gl.EnableVertexAttribArray(7)
gl.VertexAttribPointer(4, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 2 + size_of(i32)))
gl.VertexAttribPointer(5, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 3 + size_of(i32)))
gl.VertexAttribPointer(6, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 4 + size_of(i32)))
gl.VertexAttribPointer(7, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 5 + size_of(i32)))
gl.VertexAttribDivisor(4, 1)
gl.VertexAttribDivisor(5, 1)
gl.VertexAttribDivisor(6, 1)
gl.VertexAttribDivisor(7, 1)
// font ebo
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, font_ebo);
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(quad_indices), &quad_indices, gl.STATIC_DRAW);
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, font_ebo)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(quad_indices), &quad_indices, gl.STATIC_DRAW)
use_shader(font_shader);
use_shader(font_shader)
for i in 32..<128 {
for j in 0..<4 {
buf: [24]byte
text := fmt.bprintf(buf[:], "texcoords[%d]", (i - 32) * 4 + j);
set_vec2f(font_shader, cstring(raw_data(text)), zephr_ctx.font.characters[i].tex_coords[j].x, zephr_ctx.font.characters[i].tex_coords[j].y);
text := fmt.bprintf(buf[:], "texcoords[%d]", (i - 32) * 4 + j)
set_vec2f(font_shader, cstring(raw_data(text)), zephr_ctx.font.characters[i].tex_coords[j].x, zephr_ctx.font.characters[i].tex_coords[j].y)
}
}
gl.BindBuffer(gl.ARRAY_BUFFER, 0);
gl.BindVertexArray(0);
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
gl.BindVertexArray(0)
return 0;
return 0
}
calculate_text_size :: proc(text: string, font_size: u32) -> Vec2 {
scale := cast(f32)font_size / FONT_PIXEL_SIZE;
scale := cast(f32)font_size / FONT_PIXEL_SIZE
size : Vec2 = ---
w: f32 = 0;
h: f32 = 0;
max_bearing_h: f32 = 0;
w: f32 = 0
h: f32 = 0
max_bearing_h: f32 = 0
// NOTE: I don't like looping through the characters twice, but it's fine for now
for i in 0..<len(text) {
ch := zephr_ctx.font.characters[text[i]];
max_bearing_h = max(max_bearing_h, ch.bearing.y);
ch := zephr_ctx.font.characters[text[i]]
max_bearing_h = max(max_bearing_h, ch.bearing.y)
}
for i in 0..<len(text) {
ch := zephr_ctx.font.characters[text[i]];
w += cast(f32)ch.advance / 64;
ch := zephr_ctx.font.characters[text[i]]
w += cast(f32)ch.advance / 64
// remove bearing of first character
if (i == 0 && len(text) > 1) {
w -= ch.bearing.x;
w -= ch.bearing.x
}
// remove the trailing width of the last character
if (i == len(text) - 1) {
w -= ((cast(f32)ch.advance / 64) - (ch.bearing.x + ch.size.x));
w -= ((cast(f32)ch.advance / 64) - (ch.bearing.x + ch.size.x))
}
// if we only have one character in the text, then remove the bearing width
if (len(text) == 1) {
w -= (ch.bearing.x);
w -= (ch.bearing.x)
}
h = max(h, max_bearing_h - ch.bearing.y + ch.size.y);
h = max(h, max_bearing_h - ch.bearing.y + ch.size.y)
if (text[i] == '\n') {
w = 0;
h += max_bearing_h;
w = 0
h += max_bearing_h
}
}
size.x = w * scale;
size.y = h * scale;
size.x = w * scale
size.y = h * scale
return size;
return size
}
draw_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) {
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment);
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment)
gl.ActiveTexture(gl.TEXTURE0);
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id);
gl.BindVertexArray(font_vao);
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id)
gl.BindVertexArray(font_vao)
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo);
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(glyph_instance_list), raw_data(glyph_instance_list), gl.DYNAMIC_DRAW);
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo)
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(glyph_instance_list), raw_data(glyph_instance_list), gl.DYNAMIC_DRAW)
gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(glyph_instance_list))
gl.BindVertexArray(0);
gl.BindTexture(gl.TEXTURE_2D, 0);
gl.BindVertexArray(0)
gl.BindTexture(gl.TEXTURE_2D, 0)
delete(glyph_instance_list)
}
draw_text_batch :: proc(batch: ^GlyphInstanceList) {
gl.ActiveTexture(gl.TEXTURE0);
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id);
gl.BindVertexArray(font_vao);
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id)
gl.BindVertexArray(font_vao)
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo);
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(batch), raw_data(batch^), gl.DYNAMIC_DRAW);
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo)
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(batch), raw_data(batch^), gl.DYNAMIC_DRAW)
gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(batch))
gl.BindVertexArray(0);
gl.BindTexture(gl.TEXTURE_2D, 0);
gl.BindVertexArray(0)
gl.BindTexture(gl.TEXTURE_2D, 0)
delete(batch^)
}
get_glyph_instance_list_from_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) -> GlyphInstanceList {
constraints := constraints
use_shader(font_shader);
text_color := Vec4{ cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255 };
use_shader(font_shader)
text_color := Vec4{ cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255 }
set_mat4f(font_shader, "projection", zephr_ctx.projection)
rect: Rect
apply_constraints(&constraints, &rect.pos, &rect.size);
apply_constraints(&constraints, &rect.pos, &rect.size)
text_size := calculate_text_size(text, FONT_PIXEL_SIZE);
font_scale := cast(f32)font_size / FONT_PIXEL_SIZE * rect.size.x;
text_size := calculate_text_size(text, FONT_PIXEL_SIZE)
font_scale := cast(f32)font_size / FONT_PIXEL_SIZE * rect.size.x
apply_alignment(alignment, &constraints, Vec2{ text_size.x * font_scale, text_size.y * font_scale }, &rect.pos);
apply_alignment(alignment, &constraints, Vec2{ text_size.x * font_scale, text_size.y * font_scale }, &rect.pos)
model := identity()
scale(&model, Vec3{font_scale, font_scale, 1});
scale(&model, Vec3{font_scale, font_scale, 1})
// rotate around the center point of the text
translate(&model, Vec3{-text_size.x * font_scale / 2, -text_size.y * font_scale / 2, 0});
rotate(&model, constraints.rotation, Vec3{0, 0, 1});
translate(&model, Vec3{text_size.x *font_scale / 2, text_size.y * font_scale / 2, 0});
translate(&model, Vec3{-text_size.x * font_scale / 2, -text_size.y * font_scale / 2, 0})
rotate(&model, constraints.rotation, Vec3{0, 0, 1})
translate(&model, Vec3{text_size.x *font_scale / 2, text_size.y * font_scale / 2, 0})
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0});
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0})
max_bearing_h: f32 = 0;
max_bearing_h: f32 = 0
for i in 0..<len(text) {
ch := zephr_ctx.font.characters[text[i]];
max_bearing_h = max(max_bearing_h, ch.bearing.y);
ch := zephr_ctx.font.characters[text[i]]
max_bearing_h = max(max_bearing_h, ch.bearing.y)
}
first_char_bearing_w := zephr_ctx.font.characters[text[0]].bearing.x;
first_char_bearing_w := zephr_ctx.font.characters[text[0]].bearing.x
glyph_instance_list: GlyphInstanceList
reserve(&glyph_instance_list, 16)
@@ -369,21 +369,21 @@ get_glyph_instance_list_from_text :: proc(text: string, font_size: u32, constrai
// scale up or down the model matrix to get the desired font size.
// this way everything works out fine and we get to transform the text using the
// model matrix
c := 0;
x: u32 = 0;
y: f32 = 0;
c := 0
x: u32 = 0
y: f32 = 0
for c != len(text) {
ch := zephr_ctx.font.characters[text[c]]
// subtract the bearing width of the first character to remove the extra space
// at the start of the text and move every char to the left by that width
xpos := (cast(f32)x + (ch.bearing.x - first_char_bearing_w));
ypos := y + (text_size.y - ch.bearing.y - (text_size.y - max_bearing_h));
xpos := (cast(f32)x + (ch.bearing.x - first_char_bearing_w))
ypos := y + (text_size.y - ch.bearing.y - (text_size.y - max_bearing_h))
if (text[c] == '\n') {
x = 0;
y += max_bearing_h + (36 * LINE_HEIGHT);
c += 1;
continue;
x = 0
y += max_bearing_h + (36 * LINE_HEIGHT)
c += 1
continue
}
test_model := Mat4 {
@@ -402,18 +402,18 @@ get_glyph_instance_list_from_text :: proc(text: string, font_size: u32, constrai
append(&glyph_instance_list, instance)
x += (ch.advance >> 6);
c += 1;
x += (ch.advance >> 6)
c += 1
}
return glyph_instance_list;
return glyph_instance_list
}
add_text_instance :: proc(batch: ^GlyphInstanceList, text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) {
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment);
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment)
append(batch, ..glyph_instance_list[:])
delete(glyph_instance_list);
delete(glyph_instance_list)
}
+19 -19
View File
@@ -6,40 +6,40 @@ import stb "vendor:stb/image"
TextureId :: u32
load_texture :: proc(path: cstring) -> TextureId {
texture_id: TextureId;
texture_id: TextureId
width, height, channels: i32
data := stb.load(path, &width, &height, &channels, 4) // RGBA
defer stb.image_free(data)
format := gl.RGBA;
format := gl.RGBA
switch (channels) {
case 1:
format = gl.RED;
break;
format = gl.RED
break
case 2:
format = gl.RG;
break;
format = gl.RG
break
case 3:
format = gl.RGB;
break;
format = gl.RGB
break
case 4:
format = gl.RGBA;
break;
format = gl.RGBA
break
}
gl.GenTextures(1, &texture_id);
gl.BindTexture(gl.TEXTURE_2D, texture_id);
gl.GenTextures(1, &texture_id)
gl.BindTexture(gl.TEXTURE_2D, texture_id)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.TexImage2D(gl.TEXTURE_2D, 0, cast(i32)format, width, height, 0, cast(u32)format, gl.UNSIGNED_BYTE, data);
gl.TexImage2D(gl.TEXTURE_2D, 0, cast(i32)format, width, height, 0, cast(u32)format, gl.UNSIGNED_BYTE, data)
gl.GenerateMipmap(gl.TEXTURE_2D);
gl.GenerateMipmap(gl.TEXTURE_2D)
return texture_id;
return texture_id
}
+398 -353
View File
File diff suppressed because it is too large Load Diff
+188 -164
View File
@@ -379,6 +379,8 @@ XA_CARDINAL :: x11.Atom(6)
FNV_HASH32_INIT :: 0x811c9dc5
@private
FNV_HASH32_PRIME :: 0x01000193
@private
INIT_UI_STACK_SIZE :: 256
COLOR_BLACK :: Color{0, 0, 0, 255}
COLOR_WHITE :: Color{255, 255, 255, 255}
@@ -651,64 +653,64 @@ x11_go_fullscreen :: proc() {
// remove the resizing constraint before going fullscreen so WMs such as gnome
// can add the _NET_WM_ACTION_FULLSCREEN action to the _NET_WM_ALLOWED_ACTIONS atom
// and properly fullscreen the window
size_hints := x11.XAllocSizeHints();
size_hints := x11.XAllocSizeHints()
zephr_ctx.window.pre_fullscreen_size.x = zephr_ctx.window.size.x;
zephr_ctx.window.pre_fullscreen_size.y = zephr_ctx.window.size.y;
zephr_ctx.window.pre_fullscreen_size.x = zephr_ctx.window.size.x
zephr_ctx.window.pre_fullscreen_size.y = zephr_ctx.window.size.y
if (size_hints != nil) {
size_hints.flags = {.PPosition, .PSize};
size_hints.width = cast(i32)zephr_ctx.window.size.x;
size_hints.height = cast(i32)zephr_ctx.window.size.y;
x11.XSetWMNormalHints(x11_display, x11_window, size_hints);
x11.XFree(size_hints);
size_hints.flags = {.PPosition, .PSize}
size_hints.width = cast(i32)zephr_ctx.window.size.x
size_hints.height = cast(i32)zephr_ctx.window.size.y
x11.XSetWMNormalHints(x11_display, x11_window, size_hints)
x11.XFree(size_hints)
}
xev: x11.XEvent;
wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false);
fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false);
xev.type = .ClientMessage;
xev.xclient.window = x11_window;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
xev.xclient.data.l[1] = cast(int)fullscreen;
xev.xclient.data.l[2] = 0;
xev: x11.XEvent
wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false)
fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false)
xev.type = .ClientMessage
xev.xclient.window = x11_window
xev.xclient.message_type = wm_state
xev.xclient.format = 32
xev.xclient.data.l[0] = 1 // _NET_WM_STATE_ADD
xev.xclient.data.l[1] = cast(int)fullscreen
xev.xclient.data.l[2] = 0
x11.XSendEvent(x11_display, x11.XDefaultRootWindow(x11_display), false,
{.SubstructureNotify, .SubstructureRedirect}, &xev);
{.SubstructureNotify, .SubstructureRedirect}, &xev)
}
@private
x11_return_fullscreen :: proc() {
xev: x11.XEvent;
wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false);
fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false);
xev.type = .ClientMessage;
xev.xclient.window = x11_window;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE
xev.xclient.data.l[1] = cast(int)fullscreen;
xev.xclient.data.l[2] = 0;
xev: x11.XEvent
wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false)
fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false)
xev.type = .ClientMessage
xev.xclient.window = x11_window
xev.xclient.message_type = wm_state
xev.xclient.format = 32
xev.xclient.data.l[0] = 0 // _NET_WM_STATE_REMOVE
xev.xclient.data.l[1] = cast(int)fullscreen
xev.xclient.data.l[2] = 0
x11.XSendEvent(x11_display, x11.XDefaultRootWindow(x11_display), false,
{.SubstructureNotify, .SubstructureRedirect}, &xev);
{.SubstructureNotify, .SubstructureRedirect}, &xev)
// restore the resizing constraint as well as the pre-fullscreen window size
// when returning from fullscreen
size_hints := x11.XAllocSizeHints();
size_hints := x11.XAllocSizeHints()
if (size_hints != nil) {
size_hints.flags = {.PPosition, .PSize};
size_hints.width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x;
size_hints.height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y;
size_hints.flags = {.PPosition, .PSize}
size_hints.width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x
size_hints.height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y
if (zephr_ctx.window.non_resizable) {
size_hints.flags |= {.PMinSize, .PMaxSize};
size_hints.min_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x;
size_hints.min_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y;
size_hints.max_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x;
size_hints.max_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y;
size_hints.flags |= {.PMinSize, .PMaxSize}
size_hints.min_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x
size_hints.min_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y
size_hints.max_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x
size_hints.max_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y
}
x11.XSetWMNormalHints(x11_display, x11_window, size_hints);
x11.XFree(size_hints);
x11.XSetWMNormalHints(x11_display, x11_window, size_hints)
x11.XFree(size_hints)
}
}
@@ -727,30 +729,30 @@ x11_toggle_fullscreen :: proc(fullscreen: bool) {
@private
x11_assign_window_icon :: proc(icon_path: cstring) {
icon_width, icon_height: i32
icon_data := image.load(icon_path, &icon_width, &icon_height, nil, 4);
icon_data := image.load(icon_path, &icon_width, &icon_height, nil, 4)
defer image.image_free(icon_data)
assert(icon_data != nil, "Failed to load icon image")
target_size := 2 + icon_width * icon_height;
target_size := 2 + icon_width * icon_height
data := make([]u64, target_size * size_of(u64))
// first two elements are width and height
data[0] = cast(u64)icon_width;
data[1] = cast(u64)icon_height;
data[0] = cast(u64)icon_width
data[1] = cast(u64)icon_height
for i in 0..<(icon_width*icon_height) {
data[i + 2] = cast(u64)((icon_data[i * 4] << 16) | (icon_data[i * 4 + 1] << 8) | (icon_data[i * 4 + 2] << 0) | (icon_data[i * 4 + 3] << 24))
}
net_wm_icon := x11.XInternAtom(x11_display, "_NET_WM_ICON", false);
net_wm_icon := x11.XInternAtom(x11_display, "_NET_WM_ICON", false)
x11.XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, raw_data(data), target_size);
x11.XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, raw_data(data), target_size)
}
@private
x11_get_screen_size :: proc() -> Vec2 {
screen := x11.XDefaultScreenOfDisplay(x11_display);
screen := x11.XDefaultScreenOfDisplay(x11_display)
return Vec2{cast(f32)screen.width, cast(f32)screen.height}
}
@@ -792,7 +794,7 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
{.CWColormap, .CWEventMask}, &attributes)
if (icon_path != "") {
x11_assign_window_icon(icon_path);
x11_assign_window_icon(icon_path)
}
// Hints to the WM that the window is a normal window
@@ -803,7 +805,7 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
wm_delete_window := x11.XInternAtom(x11_display, "WM_DELETE_WINDOW", false)
x11.XSetWMProtocols(x11_display, x11_window, &wm_delete_window, 1)
zephr_ctx.window_delete_atom = wm_delete_window;
zephr_ctx.window_delete_atom = wm_delete_window
// set window name
{
@@ -840,11 +842,11 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
size_hints.width = cast(i32)window_size.x
size_hints.height = cast(i32)window_size.y
if (window_non_resizable) {
size_hints.flags |= {.PMinSize, .PMaxSize};
size_hints.min_width = cast(i32)window_size.x;
size_hints.min_height = cast(i32)window_size.y;
size_hints.max_width = cast(i32)window_size.x;
size_hints.max_height = cast(i32)window_size.y;
size_hints.flags |= {.PMinSize, .PMaxSize}
size_hints.min_width = cast(i32)window_size.x
size_hints.min_height = cast(i32)window_size.y
size_hints.max_width = cast(i32)window_size.x
size_hints.max_height = cast(i32)window_size.y
}
x11.XSetWMNormalHints(x11_display, x11_window, size_hints)
x11.XFree(size_hints)
@@ -877,16 +879,16 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
glx.CONTEXT_MINOR_VERSION_ARB, 3,
glx.CONTEXT_PROFILE_MASK_ARB, glx.CONTEXT_CORE_PROFILE_BIT_ARB,
x11.None,
};
}
glx_context = glx.CreateContextAttribsARB(x11_display, fbc[0], nil, true, raw_data(context_attributes));
glx_context = glx.CreateContextAttribsARB(x11_display, fbc[0], nil, true, raw_data(context_attributes))
if glx_context == nil {
fmt.eprintf("Failed to create GLX context\n")
return
}
glx.MakeCurrent(x11_display, x11_window, glx_context);
glx.MakeCurrent(x11_display, x11_window, glx_context)
// TODO: find a way to properly get the GL version
gl_major: i32 = ---
@@ -896,7 +898,7 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
fmt.printf("GL version: %d.%d\n", gl_major, gl_minor)
glx.SwapIntervalEXT(x11_display, x11_window, 1);
glx.SwapIntervalEXT(x11_display, x11_window, 1)
// we enable blending for text
gl.Enable(gl.BLEND)
gl.Enable(gl.MULTISAMPLE)
@@ -913,12 +915,12 @@ x11_init :: proc(window_title: cstring, window_size: Vec2, icon_path: cstring, w
@private
x11_close :: proc() {
glx.MakeCurrent(x11_display, 0, nil);
glx.DestroyContext(x11_display, glx_context);
glx.MakeCurrent(x11_display, 0, nil)
glx.DestroyContext(x11_display, glx_context)
x11.XDestroyWindow(x11_display, x11_window);
x11.XFreeColormap(x11_display, x11_colormap);
x11.XCloseDisplay(x11_display);
x11.XDestroyWindow(x11_display, x11_window)
x11.XFreeColormap(x11_display, x11_colormap)
x11.XCloseDisplay(x11_display)
}
@@ -938,22 +940,24 @@ init :: proc(font_path: cstring, icon_path: cstring, window_title: cstring, wind
ui_init(font_path)
zephr_ctx.ui.elements = make([dynamic]UiElement, INIT_UI_STACK_SIZE)
zephr_ctx.mouse.pos = Vec2{-1, -1}
zephr_ctx.window.size = window_size
zephr_ctx.window.non_resizable = window_non_resizable
zephr_ctx.projection = orthographic_projection_2d(0, window_size.x, window_size.y, 0)
zephr_ctx.cursors[.ARROW] = x11.XCreateFontCursor(x11_display, .XC_left_ptr);
zephr_ctx.cursors[.IBEAM] = x11.XCreateFontCursor(x11_display, .XC_xterm);
zephr_ctx.cursors[.CROSSHAIR] = x11.XCreateFontCursor(x11_display, .XC_crosshair);
zephr_ctx.cursors[.HAND] = x11.XCreateFontCursor(x11_display, .XC_hand1);
zephr_ctx.cursors[.HRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_h_double_arrow);
zephr_ctx.cursors[.VRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_v_double_arrow);
zephr_ctx.cursors[.ARROW] = x11.XCreateFontCursor(x11_display, .XC_left_ptr)
zephr_ctx.cursors[.IBEAM] = x11.XCreateFontCursor(x11_display, .XC_xterm)
zephr_ctx.cursors[.CROSSHAIR] = x11.XCreateFontCursor(x11_display, .XC_crosshair)
zephr_ctx.cursors[.HAND] = x11.XCreateFontCursor(x11_display, .XC_hand1)
zephr_ctx.cursors[.HRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_h_double_arrow)
zephr_ctx.cursors[.VRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_v_double_arrow)
// non-standard cursors
zephr_ctx.cursors[.DISABLED] = xcursor.LibraryLoadCursor(x11_display, "crossed_circle");
zephr_ctx.cursors[.DISABLED] = xcursor.LibraryLoadCursor(x11_display, "crossed_circle")
zephr_ctx.screen_size = x11_get_screen_size();
start_internal_timer();
zephr_ctx.screen_size = x11_get_screen_size()
start_internal_timer()
}
deinit :: proc() {
@@ -965,24 +969,42 @@ should_quit :: proc() -> bool {
gl.ClearColor(0, 0, 0, 1)
gl.Clear(gl.COLOR_BUFFER_BIT)
zephr_ctx.cursor = .ARROW;
zephr_ctx.mouse.released = false;
zephr_ctx.mouse.pressed = false;
zephr_ctx.cursor = .ARROW
zephr_ctx.mouse.released = false
zephr_ctx.mouse.pressed = false
//audio_update();
return zephr_ctx.should_quit;
return zephr_ctx.should_quit
}
quit :: proc() {
zephr_ctx.should_quit = true;
zephr_ctx.should_quit = true
}
@private
consume_mouse_events :: proc() -> bool {
defer clear(&zephr_ctx.ui.elements)
#reverse for e in zephr_ctx.ui.elements {
if (inside_rect(e.rect, zephr_ctx.mouse.pos)) {
zephr_ctx.ui.hovered_element = e.id
return false
}
}
return true
}
swap_buffers :: proc() {
if (zephr_ctx.ui.popup_open) {
draw_color_picker_popup(&zephr_ctx.ui.popup_parent_constraints);
draw_color_picker_popup(&zephr_ctx.ui.popup_parent_constraints)
}
zephr_ctx.ui.popup_open = false
if consume_mouse_events() {
zephr_ctx.ui.hovered_element = 0
}
zephr_ctx.ui.popup_open = false;
glx.SwapBuffers(x11_display, x11_window)
x11.XDefineCursor(x11_display, x11_window, zephr_ctx.cursors[zephr_ctx.cursor])
@@ -1010,77 +1032,77 @@ iter_events :: proc(e_out: ^Event) -> bool {
}
} else if xev.type == .DestroyNotify {
// window destroy event
e_out.type = .WINDOW_CLOSED;
e_out.type = .WINDOW_CLOSED
return true;
return true
} else if xev.type == .ClientMessage {
// window close event
if (cast(x11.Atom)xev.xclient.data.l[0] == zephr_ctx.window_delete_atom) {
e_out.type = .WINDOW_CLOSED;
e_out.type = .WINDOW_CLOSED
return true;
return true
}
} else if xev.type == .KeyPress {
xke := xev.xkey;
xke := xev.xkey
evdev_keycode := xke.keycode - 8;
scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode];
evdev_keycode := xke.keycode - 8
scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode]
e_out.type = .KEY_PRESSED;
e_out.key.scancode = scancode;
e_out.type = .KEY_PRESSED
e_out.key.scancode = scancode
//e_out.key.code = keycode;
e_out.key.mods = x11_mods_to_zephr_mods(scancode, true);
e_out.key.mods = x11_mods_to_zephr_mods(scancode, true)
return true
} else if xev.type == .KeyRelease {
xke := xev.xkey;
xke := xev.xkey
evdev_keycode := xke.keycode - 8;
scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode];
evdev_keycode := xke.keycode - 8
scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode]
e_out.type = .KEY_RELEASED;
e_out.type = .KEY_RELEASED
e_out.key.scancode = scancode
//e_out.key.code = keycode;
e_out.key.mods = x11_mods_to_zephr_mods(scancode, false);
e_out.key.mods = x11_mods_to_zephr_mods(scancode, false)
return true
} else if xev.type == .ButtonPress {
e_out.type = .MOUSE_BUTTON_PRESSED;
e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y};
zephr_ctx.mouse.pressed = true;
e_out.type = .MOUSE_BUTTON_PRESSED
e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y}
zephr_ctx.mouse.pressed = true
switch (xev.xbutton.button) {
case .Button1:
e_out.mouse.button = .BUTTON_LEFT;
zephr_ctx.mouse.button = .BUTTON_LEFT;
e_out.mouse.button = .BUTTON_LEFT
zephr_ctx.mouse.button = .BUTTON_LEFT
case .Button2:
e_out.mouse.button = .BUTTON_MIDDLE;
zephr_ctx.mouse.button = .BUTTON_MIDDLE;
e_out.mouse.button = .BUTTON_MIDDLE
zephr_ctx.mouse.button = .BUTTON_MIDDLE
case .Button3:
e_out.mouse.button = .BUTTON_RIGHT;
zephr_ctx.mouse.button = .BUTTON_RIGHT;
e_out.mouse.button = .BUTTON_RIGHT
zephr_ctx.mouse.button = .BUTTON_RIGHT
case .Button4:
e_out.type = .MOUSE_SCROLL;
e_out.mouse.scroll_direction = .UP;
e_out.type = .MOUSE_SCROLL
e_out.mouse.scroll_direction = .UP
case .Button5:
e_out.type = .MOUSE_SCROLL;
e_out.mouse.scroll_direction = .DOWN;
e_out.type = .MOUSE_SCROLL
e_out.mouse.scroll_direction = .DOWN
case cast(x11.MouseButton)8: // Back
e_out.mouse.button = .BUTTON_BACK;
zephr_ctx.mouse.button = .BUTTON_BACK;
e_out.mouse.button = .BUTTON_BACK
zephr_ctx.mouse.button = .BUTTON_BACK
case cast(x11.MouseButton)9: // Forward
e_out.mouse.button = .BUTTON_FORWARD;
zephr_ctx.mouse.button = .BUTTON_FORWARD;
e_out.mouse.button = .BUTTON_FORWARD
zephr_ctx.mouse.button = .BUTTON_FORWARD
case:
fmt.printf("[WARN] Unknown mouse button pressed: %d\n", xev.xbutton.button);
fmt.printf("[WARN] Unknown mouse button pressed: %d\n", xev.xbutton.button)
}
return true
} else if xev.type == .ButtonRelease {
e_out.type = .MOUSE_BUTTON_RELEASED;
e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y};
zephr_ctx.mouse.released = true;
zephr_ctx.mouse.pressed = false;
e_out.type = .MOUSE_BUTTON_RELEASED
e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y}
zephr_ctx.mouse.released = true
zephr_ctx.mouse.pressed = false
switch (xev.xbutton.button) {
case .Button1:
@@ -1090,34 +1112,34 @@ iter_events :: proc(e_out: ^Event) -> bool {
case .Button3:
e_out.mouse.button = .BUTTON_RIGHT
case .Button4:
e_out.type = .MOUSE_SCROLL;
e_out.mouse.scroll_direction = .UP;
e_out.type = .MOUSE_SCROLL
e_out.mouse.scroll_direction = .UP
case .Button5:
e_out.type = .MOUSE_SCROLL;
e_out.mouse.scroll_direction = .DOWN;
e_out.type = .MOUSE_SCROLL
e_out.mouse.scroll_direction = .DOWN
case cast(x11.MouseButton)8: // Back
e_out.mouse.button = .BUTTON_BACK;
zephr_ctx.mouse.button = .BUTTON_BACK;
e_out.mouse.button = .BUTTON_BACK
zephr_ctx.mouse.button = .BUTTON_BACK
case cast(x11.MouseButton)9: // Forward
e_out.mouse.button = .BUTTON_FORWARD;
zephr_ctx.mouse.button = .BUTTON_FORWARD;
e_out.mouse.button = .BUTTON_FORWARD
zephr_ctx.mouse.button = .BUTTON_FORWARD
}
return true;
return true
} else if xev.type == .MappingNotify {
// input device mapping changed
if (xev.xmapping.request != .MappingKeyboard) {
break;
break
}
x11.XRefreshKeyboardMapping(&xev.xmapping);
x11.XRefreshKeyboardMapping(&xev.xmapping)
/* x11_keyboard_map_update(); */
break;
break
} else if xev.type == .MotionNotify {
e_out.type = .MOUSE_MOVED;
e_out.mouse.pos = Vec2{cast(f32)xev.xmotion.x, cast(f32)xev.xmotion.y};
zephr_ctx.mouse.pos = e_out.mouse.pos;
e_out.type = .MOUSE_MOVED
e_out.mouse.pos = Vec2{cast(f32)xev.xmotion.x, cast(f32)xev.xmotion.y}
zephr_ctx.mouse.pos = e_out.mouse.pos
return true;
return true
}
}
@@ -1125,17 +1147,18 @@ iter_events :: proc(e_out: ^Event) -> bool {
}
get_window_size :: proc() -> Vec2 {
return zephr_ctx.window.size;
return zephr_ctx.window.size
}
toggle_fullscreen :: proc() {
x11_toggle_fullscreen(zephr_ctx.window.is_fullscreen);
x11_toggle_fullscreen(zephr_ctx.window.is_fullscreen)
zephr_ctx.window.is_fullscreen = !zephr_ctx.window.is_fullscreen;
zephr_ctx.window.is_fullscreen = !zephr_ctx.window.is_fullscreen
}
@private
set_cursor :: proc(cursor: Cursor) {
zephr_ctx.cursor = cursor;
zephr_ctx.cursor = cursor
}
@@ -1153,8 +1176,8 @@ fnv_hash32 :: proc(data: []byte, size: u32, hash: u32) -> u32 {
hash := hash
for i in 0..<size {
hash ~= cast(u32)data[i];
hash *= FNV_HASH32_PRIME;
hash ~= cast(u32)data[i]
hash *= FNV_HASH32_PRIME
}
return hash
@@ -1167,84 +1190,85 @@ x11_mods_to_zephr_mods :: proc(scancode: Scancode, is_press: bool) -> KeyMod {
// TODO: figure out a better way of doing this
if (is_press) {
if (scancode == .LEFT_SHIFT) {
mods |= {.LEFT_SHIFT, .SHIFT};
mods |= {.LEFT_SHIFT, .SHIFT}
}
if (scancode == .RIGHT_SHIFT) {
mods |= {.RIGHT_SHIFT, .SHIFT};
mods |= {.RIGHT_SHIFT, .SHIFT}
}
if (scancode == .LEFT_CTRL) {
mods |= {.LEFT_CTRL, .CTRL};
mods |= {.LEFT_CTRL, .CTRL}
}
if (scancode == .RIGHT_CTRL) {
mods |= {.RIGHT_CTRL, .CTRL};
mods |= {.RIGHT_CTRL, .CTRL}
}
if (scancode == .LEFT_ALT) {
mods |= {.LEFT_ALT, .ALT};
mods |= {.LEFT_ALT, .ALT}
}
if (scancode == .RIGHT_ALT) {
mods |= {.RIGHT_ALT, .ALT};
mods |= {.RIGHT_ALT, .ALT}
}
if (scancode == .LEFT_META) {
mods |= {.LEFT_META, .META};
mods |= {.LEFT_META, .META}
}
if (scancode == .RIGHT_META) {
mods |= {.RIGHT_META, .META};
mods |= {.RIGHT_META, .META}
}
if (scancode == .CAPS_LOCK) {
mods |= {.CAPS_LOCK};
mods |= {.CAPS_LOCK}
}
if (scancode == .NUM_LOCK_OR_CLEAR) {
mods |= {.NUM_LOCK};
mods |= {.NUM_LOCK}
}
} else {
if (scancode == .LEFT_SHIFT) {
mods &= ~{.LEFT_SHIFT};
mods &= ~{.LEFT_SHIFT}
}
if (scancode == .RIGHT_SHIFT) {
mods &= ~{.RIGHT_SHIFT};
mods &= ~{.RIGHT_SHIFT}
}
if (!(.RIGHT_SHIFT in mods) && !(.LEFT_SHIFT in mods)) {
mods &= ~{.SHIFT};
mods &= ~{.SHIFT}
}
if (scancode == .LEFT_CTRL) {
mods &= ~{.LEFT_CTRL};
mods &= ~{.LEFT_CTRL}
}
if (scancode == .RIGHT_CTRL) {
mods &= ~{.RIGHT_CTRL};
mods &= ~{.RIGHT_CTRL}
}
if (!(.RIGHT_CTRL in mods) && !(.LEFT_CTRL in mods)) {
mods &= ~{.CTRL};
mods &= ~{.CTRL}
}
if (scancode == .LEFT_ALT) {
mods &= ~{.LEFT_ALT};
mods &= ~{.LEFT_ALT}
}
if (scancode == .RIGHT_ALT) {
mods &= ~{.RIGHT_ALT};
mods &= ~{.RIGHT_ALT}
}
if (!(.RIGHT_ALT in mods) && !(.LEFT_ALT in mods)) {
mods &= ~{.ALT};
mods &= ~{.ALT}
}
if (scancode == .LEFT_META) {
mods &= ~{.LEFT_META};
mods &= ~{.LEFT_META}
}
if (scancode == .RIGHT_META) {
mods &= ~{.RIGHT_META};
mods &= ~{.RIGHT_META}
}
if (!(.RIGHT_META in mods) && !(.LEFT_META in mods)) {
mods &= ~{.META};
mods &= ~{.META}
}
if (scancode == .CAPS_LOCK) {
mods &= ~{.CAPS_LOCK};
mods &= ~{.CAPS_LOCK}
}
if (scancode == .NUM_LOCK_OR_CLEAR) {
mods &= ~{.NUM_LOCK};
mods &= ~{.NUM_LOCK}
}
}
zephr_ctx.keyboard.mods = mods
return mods;
return mods
}
+19 -19
View File
@@ -12,16 +12,16 @@ Mat4 :: matrix[4, 4]f32
Color :: struct { r, g, b, a: u8 }
orthographic_projection_2d :: proc(left, right, bottom, top: f32) -> Mat4 {
result := identity();
result := identity()
result[0][0] = 2 / (right - left);
result[3][0] = -(right + left) / (right - left);
result[1][1] = 2 / (top - bottom);
result[3][1] = -(top + bottom) / (top - bottom);
result[2][2] = -1;
result[3][3] = 1;
result[0][0] = 2 / (right - left)
result[3][0] = -(right + left) / (right - left)
result[1][1] = 2 / (top - bottom)
result[3][1] = -(top + bottom) / (top - bottom)
result[2][2] = -1
result[3][3] = 1
return result;
return result
}
identity :: proc() -> Mat4 {
@@ -83,9 +83,9 @@ mult_color :: proc(color: Color, scalar: f32) -> Color {
}
hsv2rgb :: proc(h: f32, s: f32, v: f32) -> Color {
c := v * s;
x := (c * (1 - abs(math.mod(h / 60.0, 2) - 1)));
m := v - c;
c := v * s
x := (c * (1 - abs(math.mod(h / 60.0, 2) - 1)))
m := v - c
r, g, b: f32
@@ -112,30 +112,30 @@ hsv2rgb :: proc(h: f32, s: f32, v: f32) -> Color {
} else {
r = c
g = 0
b = x;
b = x
}
return (Color){(u8)((r + m) * 255), (u8)((g + m) * 255), (u8)((b + m) * 255), 255};
return (Color){(u8)((r + m) * 255), (u8)((g + m) * 255), (u8)((b + m) * 255), 255}
}
determine_color_contrast :: proc(bg: Color) -> Color {
white_contrast := get_contrast(bg, COLOR_WHITE);
black_contrast := get_contrast(bg, COLOR_BLACK);
white_contrast := get_contrast(bg, COLOR_WHITE)
black_contrast := get_contrast(bg, COLOR_BLACK)
return white_contrast > black_contrast ? COLOR_WHITE : COLOR_BLACK;
return white_contrast > black_contrast ? COLOR_WHITE : COLOR_BLACK
}
@private
get_srgb :: proc(component: f32) -> f32 {
return (component / 255 <= 0.03928
? component / 255 / 12.92
: math.pow((component / 255 + 0.055) / 1.055, 2.4));
: math.pow((component / 255 + 0.055) / 1.055, 2.4))
}
get_luminance :: proc(color: Color) -> f32 {
return ((0.2126 * get_srgb(cast(f32)color.r)) +
(0.7152 * get_srgb(cast(f32)color.g)) +
(0.0722 * get_srgb(cast(f32)color.b))) / 255;
(0.0722 * get_srgb(cast(f32)color.b))) / 255
}
@private
@@ -143,5 +143,5 @@ get_contrast :: proc(fg: Color, bg: Color) -> f32 {
l1 := get_luminance(fg)
l2 := get_luminance(bg)
return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05);
return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05)
}