From a0856a50a145a33618f2c4e3adc863b9090bd08a Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Tue, 16 Nov 2021 20:55:44 -0600 Subject: [PATCH] feature dump; working on minesweeper --- include/gfx.h | 3 + include/utils.h | 13 +- misc/onyx/heartbreak_graphics.onyx | 22 ++- misc/onyx/heartbreak_input.onyx | 8 +- misc/onyx/qhb.onyx | 16 +- src/gfx.c | 22 ++- src/heartbreak.c | 8 +- src/heartbreak_graphics.c | 79 ++++++---- src/heartbreak_system.c | 2 +- src/utils.c | 26 ++++ tests/minesweeper.onyx | 225 +++++++++++++++++++++++++++++ tests/simp.onyx | 13 +- 12 files changed, 380 insertions(+), 57 deletions(-) create mode 100644 tests/minesweeper.onyx diff --git a/include/gfx.h b/include/gfx.h index 44075f0..abef4ee 100644 --- a/include/gfx.h +++ b/include/gfx.h @@ -83,6 +83,7 @@ typedef struct ImmediateRenderer { Image* active_image; Font* active_font; + Font* default_font; bh_arr(mat3) world_transforms; b32 world_transform_dirty; @@ -117,5 +118,7 @@ void gfx_image_bind(ImmediateRenderer *ir, Image *img, u32 texture_unit); void gfx_image_unbind(ImmediateRenderer *ir); Font* gfx_font_load(ImmediateRenderer *ir, char* font_path, i32 font_size); +void gfx_font_destroy(ImmediateRenderer *ir, Font *font); +void gfx_font_use(ImmediateRenderer *ir, Font *font); f32 gfx_font_get_text_width(ImmediateRenderer *ir, char* msgptr, i32 msglen); #endif diff --git a/include/utils.h b/include/utils.h index 2efe859..2891393 100644 --- a/include/utils.h +++ b/include/utils.h @@ -27,4 +27,15 @@ typedef struct mat3 { void mat3_identity(mat3 *out); void mat3_mul(mat3 *out, mat3 a, mat3 b); -#endif \ No newline at end of file +// Logging +typedef enum LogLevel { + DEBUG, INFO, WARNING, ERROR, + Log_Level_Count +} LogLevel; +extern LogLevel log_level; +extern char *log_level_strings[Log_Level_Count]; + +void log_level_set(LogLevel level); +void logprint(LogLevel level, char *format, ...); + +#endif diff --git a/misc/onyx/heartbreak_graphics.onyx b/misc/onyx/heartbreak_graphics.onyx index eef9531..dfb3721 100644 --- a/misc/onyx/heartbreak_graphics.onyx +++ b/misc/onyx/heartbreak_graphics.onyx @@ -3,7 +3,8 @@ package heartbreak.graphics // State setClearColor :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_clear_color" --- -setColor :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_color" --- +setColor :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_color" --- +setLineWidth :: (width: i32) -> void #foreign "heartbreak" "graphics_set_line_width" --- // // Drawing @@ -33,14 +34,14 @@ push :: () -> void #foreign "heartbreak" "graphics_push" --- pop :: () -> void #foreign "heartbreak" "graphics_pop" --- // -// Objects +// Images // Image :: #type i64 newImage :: (path: str) -> Image #foreign "heartbreak" "graphics_image_load" --- drawImage :: (img: Image, x, y: f32, w: f32 = -1, h: f32 = -1) -> void #foreign "heartbreak" "graphics_image_draw" --- -#private_file imageParam :: (img: Image, param: i32, value: i32) -> bool #foreign "heartbreak" "graphics_image_param" --- +#local imageParam :: (img: Image, param: i32, value: i32) -> bool #foreign "heartbreak" "graphics_image_param" --- ImageFilter :: enum { Nearest :: 0x01; @@ -71,5 +72,20 @@ ImageProperty :: enum { getImageProperty :: (img: Image, prop: ImageProperty) -> i32 #foreign "heartbreak" "graphics_image_property" --- getImageDimensions :: (img: Image) => getImageProperty(img, .Width), getImageProperty(img, .Height); +// +// Fonts +// +Font :: #type i64 +newFont :: (path: str, size: i32) -> Font #foreign "heartbreak" "graphics_font_load" --- +setFont :: (font: Font = 0) -> void #foreign "heartbreak" "graphics_font_set" --- getTextWidth :: (text: str) -> f32 #foreign "heartbreak" "graphics_get_text_width" --- + +// +// Meshes +// + + +// +// Shaders +// diff --git a/misc/onyx/heartbreak_input.onyx b/misc/onyx/heartbreak_input.onyx index e31afa0..ce48a3a 100644 --- a/misc/onyx/heartbreak_input.onyx +++ b/misc/onyx/heartbreak_input.onyx @@ -124,7 +124,7 @@ KeyConstant :: enum { Right_Alt :: 346; Right_Super :: 347; Menu :: 348; - Last :: 348; + Last :: Menu; } ButtonConstant :: enum { @@ -137,9 +137,9 @@ ButtonConstant :: enum { Button_7 :: 6; Button_8 :: 7; - Left :: 0; - Right :: 1; - Middle :: 2; + Left :: Button_1; + Right :: Button_2; + Middle :: Button_3; } keyIsDown :: (key: KeyConstant) -> bool #foreign "heartbreak" "input_key_is_down" --- diff --git a/misc/onyx/qhb.onyx b/misc/onyx/qhb.onyx index d592b2f..2236bff 100644 --- a/misc/onyx/qhb.onyx +++ b/misc/onyx/qhb.onyx @@ -6,21 +6,9 @@ package main #load "core/std" #load "./heartbreak" -#private hb :: package heartbreak +#package hb :: package heartbreak -#private_file symbol_source :: package main - -#if !#defined(symbol_source.load) { - load :: () {} -} - -#if !#defined(symbol_source.update) { - update :: (dt: f32) {} -} - -#if !#defined(symbol_source.draw) { - draw :: () {} -} +#local symbol_source :: package main // This is one place where top-level macros would be nice. #if #defined(symbol_source.keydown) { #export "__heartbreak_keydown" symbol_source.keydown } diff --git a/src/gfx.c b/src/gfx.c index 3a36e16..d78d971 100644 --- a/src/gfx.c +++ b/src/gfx.c @@ -260,7 +260,9 @@ void gfx_immediate_renderer_init(ImmediateRenderer *ir, bh_allocator allocator) ir->current_color = (ImmediateColor) { 1, 1, 1, 1 }; ir->world_transform_dirty = 1; - ir->active_font = gfx_font_load(ir, "/usr/share/fonts/nerd-fonts-complete/TTF/mononoki-Regular Nerd Font Complete Mono.ttf", 32.0f); + // @TODO Use a font that is within this project + ir->default_font = gfx_font_load(ir, "/usr/share/fonts/nerd-fonts-complete/TTF/mononoki-Regular Nerd Font Complete Mono.ttf", 32.0f); + ir->active_font = ir->default_font; } void gfx_immediate_renderer_flush(ImmediateRenderer *ir) { @@ -580,6 +582,24 @@ Font* gfx_font_load(ImmediateRenderer *ir, char* font_path, i32 font_size) { return font; } +void gfx_font_destroy(ImmediateRenderer *ir, Font *font) { + bh_free(ir->allocator, font->name); + glDeleteTextures(1, &font->texture); + bh_free(ir->allocator, font->char_data); + bh_free(ir->allocator, font); +} + +void gfx_font_use(ImmediateRenderer *ir, Font *font) { + // If the desired image is already bound, then just keep it bound. + if (ir->active_font == font) return; + + if (font == NULL) { + ir->active_font = ir->default_font; + } else { + ir->active_font = font; + } +} + f32 gfx_font_get_text_width(ImmediateRenderer *ir, char* msgptr, i32 msglen) { stbtt_aligned_quad quad; diff --git a/src/heartbreak.c b/src/heartbreak.c index 11ea7f6..c371116 100644 --- a/src/heartbreak.c +++ b/src/heartbreak.c @@ -186,6 +186,7 @@ void run_wasm_file(bh_buffer wasm_bytes) { wasm_val_vec_t results; wasm_val_vec_new_uninitialized(&args, 0); + logprint(INFO, "Calling _start in WASM module"); wasm_trap_t* result = wasm_func_call(start_func, &args, &results); if (result != NULL) { @@ -197,7 +198,7 @@ void run_wasm_file(bh_buffer wasm_bytes) { goto cleanup; error_handling: - bh_printf("An error occured trying to run the WASM module...\n"); + logprint(ERROR, "An error occured trying to run the WASM module...\n"); cleanup: if (wasm_instance) wasm_instance_delete(wasm_instance); @@ -208,6 +209,9 @@ cleanup: } int main(int argc, char* argv[]) { + log_level_set(WARNING); + logprint(INFO, "Heartbreak version " VERSION); + if (argc < 2) { bh_printf("Expected a .wasm file to run.\n"); return 1; @@ -218,8 +222,10 @@ int main(int argc, char* argv[]) { return 1; } + logprint(DEBUG, "Loading file %s", argv[1]); bh_allocator heap_allocator = bh_heap_allocator(); bh_file_contents wasm_file = bh_file_read_contents_direct(heap_allocator, argv[1]); + logprint(INFO, "Loaded file %s", argv[1]); run_wasm_file((bh_buffer) { .data = wasm_file.data, diff --git a/src/heartbreak_graphics.c b/src/heartbreak_graphics.c index 3212b41..9563ab3 100644 --- a/src/heartbreak_graphics.c +++ b/src/heartbreak_graphics.c @@ -31,6 +31,11 @@ HEARTBREAK_DEF(set_color, (WASM_F32,WASM_F32,WASM_F32,WASM_F32), ()) { return NULL; } +HEARTBREAK_DEF(set_line_width, (WASM_I32), ()) { + glLineWidth(params->data[0].of.i32); + return NULL; +} + HEARTBREAK_DEF(rectangle, (WASM_I32,WASM_F32,WASM_F32,WASM_F32,WASM_F32), ()) { gfx_image_unbind(&renderer); @@ -304,7 +309,7 @@ HEARTBREAK_DEF(image_load, (WASM_I32, WASM_I32), (WASM_I64)) { bh_printf("Loading image: %b\n", str_data, str_size); - u8 filename[512]; + char* filename = alloca(str_size + 1); strncpy(filename, str_data, str_size); filename[str_size] = '\0'; @@ -359,35 +364,15 @@ HEARTBREAK_DEF(image_param, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) { i32 param = params->data[1].of.i32; i32 value = params->data[2].of.i32; - i32 filter; + + static const GLint filters[] = { -1, GL_NEAREST, GL_LINEAR }; + static const GLint wraps[] = { -1, GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT, GL_REPEAT }; switch (param) { - case 1: - filter = -1; - if (value == 1) filter = GL_NEAREST; - if (value == 2) filter = GL_LINEAR; - if (filter > 0) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); - break; - case 2: - filter = -1; - if (value == 1) filter = GL_NEAREST; - if (value == 2) filter = GL_LINEAR; - if (filter > 0) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); - break; - case 3: - filter = -1; - if (value == 1) filter = GL_CLAMP_TO_EDGE; - if (value == 2) filter = GL_MIRRORED_REPEAT; - if (value == 3) filter = GL_REPEAT; - if (filter > 0) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, filter); - break; - case 4: - filter = -1; - if (value == 1) filter = GL_CLAMP_TO_EDGE; - if (value == 2) filter = GL_MIRRORED_REPEAT; - if (value == 3) filter = GL_REPEAT; - if (filter > 0) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, filter); - break; + case 1: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filters[value]); break; + case 2: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filters[value]); break; + case 3: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wraps[value]); break; + case 4: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wraps[value]); break; } glBindTexture(GL_TEXTURE_2D, -1); @@ -411,6 +396,37 @@ HEARTBREAK_DEF(image_property, (WASM_I64, WASM_I32), (WASM_I32)) { return NULL; } +HEARTBREAK_DEF(font_load, (WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) { + char* str_data = wasm_memory_data(wasm_memory) + params->data[0].of.i32; + u32 str_size = params->data[1].of.i32; + + char *font_name = alloca(str_size + 1); + strncpy(font_name, str_data, str_size); + font_name[str_size] = '\0'; + + Font *new_font = gfx_font_load(&renderer, font_name, params->data[2].of.i32); + wasm_val_init_ptr(&results->data[0], new_font); + return NULL; +} + +HEARTBREAK_DEF(font_destroy, (WASM_I64), ()) { + Font *font = (Font *) params->data[0].of.i64; + assert(font->magic_number == 0xbeefbeefbeefbeef); + + gfx_font_destroy(&renderer, font); + + return NULL; +} + +HEARTBREAK_DEF(font_set, (WASM_I64), ()) { + Font *font = (Font *) params->data[0].of.i64; + if (font) assert(font->magic_number == 0xbeefbeefbeefbeef); + + gfx_font_use(&renderer, font); + + return NULL; +} + HEARTBREAK_DEF(get_text_width, (WASM_I32, WASM_I32), (WASM_F32)) { char* str_data = wasm_memory_data(wasm_memory) + params->data[0].of.i32; u32 str_size = params->data[1].of.i32; @@ -422,8 +438,10 @@ HEARTBREAK_DEF(get_text_width, (WASM_I32, WASM_I32), (WASM_F32)) { HEARTBREAK_MODULE { HEARTBREAK_FUNC(set_clear_color) - HEARTBREAK_FUNC(clear) HEARTBREAK_FUNC(set_color) + HEARTBREAK_FUNC(set_line_width) + + HEARTBREAK_FUNC(clear) HEARTBREAK_FUNC(rectangle) HEARTBREAK_FUNC(circle) HEARTBREAK_FUNC(ellipse) @@ -444,6 +462,9 @@ HEARTBREAK_MODULE { HEARTBREAK_FUNC(image_param) HEARTBREAK_FUNC(image_property) + HEARTBREAK_FUNC(font_load) + HEARTBREAK_FUNC(font_destroy) + HEARTBREAK_FUNC(font_set) HEARTBREAK_FUNC(get_text_width) { NULL } diff --git a/src/heartbreak_system.c b/src/heartbreak_system.c index 5465e62..02a3ba1 100644 --- a/src/heartbreak_system.c +++ b/src/heartbreak_system.c @@ -113,6 +113,7 @@ HEARTBREAK_DEF(init, (), (WASM_I32)) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwMakeContextCurrent(glfw_window); + glfwSetWindowSizeCallback(glfw_window, __glfw_window_size_callback); glfwSetKeyCallback(glfw_window, __glfw_key_callback); glfwSetMouseButtonCallback(glfw_window, __glfw_mouse_button_callback); @@ -122,7 +123,6 @@ HEARTBREAK_DEF(init, (), (WASM_I32)) { glfwSwapInterval(1); - // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/utils.c b/src/utils.c index 6b00207..b1c145b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -60,3 +60,29 @@ void mat3_mul(mat3 *out, mat3 a, mat3 b) { } } } + +LogLevel log_level = INFO; +char *log_level_strings[Log_Level_Count] = { +" DEBUG ", +" INFO ", +"WARNING", +" ERROR ", +}; + +void log_level_set(LogLevel level) { + log_level = level; +} + +void logprint(LogLevel level, char *format, ...) { + if (level < log_level) return; + + va_list args; + va_start(args, format); + + char buf[2048]; + vsnprintf(buf, 2048, format, args); + + va_end(args); + + printf("[%s] %s\n", log_level_strings[level], buf); +} diff --git a/tests/minesweeper.onyx b/tests/minesweeper.onyx new file mode 100644 index 0000000..14f2ba0 --- /dev/null +++ b/tests/minesweeper.onyx @@ -0,0 +1,225 @@ +#load "./../misc/onyx/qhb" + +use package core +use package core.intrinsics.onyx + +Pos :: struct { x, y: i32; } + +Board :: struct { + Cell :: enum #flags { + Empty :: 0; + Covered :: 1; + + Mine :: 2; + Flag :: 3; + + Is_Number :: 0x10; + Number1 :: Is_Number | 1; + Number2 :: Is_Number | 2; + Number3 :: Is_Number | 3; + Number4 :: Is_Number | 4; + Number5 :: Is_Number | 5; + Number6 :: Is_Number | 6; + Number7 :: Is_Number | 7; + Number8 :: Is_Number | 8; + } + + width, height : i32; + visible_cells : [] Cell; + true_cells : [] Cell; +} + +init_board :: (use this: ^Board, w, h: i32) { + width = w; + height = h; + memory.alloc_slice(^visible_cells, w * h); + memory.alloc_slice(^true_cells, w * h); +} + +generate_board :: (use this: ^Board, mine_count: i32) { + // Assumes an initialized board + + for ^cell: visible_cells do *cell = .Covered; + for ^cell: true_cells do *cell = .Empty; + + mine_positions := array.make(Pos); + for i: mine_count { + while true { + mx, my := random.between(0, width - 1), random.between(0, height - 1); + if get_true_cell(this, mx, my) != .Mine { + set_true_cell(this, mx, my, .Mine); + mine_positions << .{mx, my}; + break; + } + } + } + + for ^mine_pos: mine_positions { + for neighbor: neighbors(this, mine_pos.x, mine_pos.y) { + cell := get_true_cell(this, neighbor.x, neighbor.y); + if cell == .Empty do cell = .Number1; + elseif cell&.Is_Number do cell += 1; + + set_true_cell(this, neighbor.x, neighbor.y, cell); + } + } +} + +neighbors :: (board: ^Board, x, y: i32, include_corners := true) -> Iterator(Pos) { + Context :: struct { + board: ^Board; + cx, cy: i32; + dx, dy: i32; + + include_corners: bool; + } + + context := new(Context); + *context = .{ board, x, y, -1, -1, include_corners }; + + next :: (use _: ^Context) -> (Pos, bool) { + step :: macro () { + @Gross @Cleanup + dx += 1; + if dx > 1 { dx = -1; dy += 1; } + if math.abs(dx) + math.abs(dy) == 2 && !include_corners{ + dx += 1; + if dx > 1 { dx = -1; dy += 1; } + } + } + + while true { + if dy > 1 do break; + if dx == 0 && dy == 0 { step(); continue; } + + rx, ry := cx + dx, cy + dy; + step(); + + if rx < 0 || rx >= board.width || ry < 0 || ry >= board.height do continue; + return .{rx, ry}, true; + } + + return __zero_value(Pos), false; + } + + return Iterator(Pos).{ context, next, cfree }; +} + +get_visible_cell :: (use this: ^Board, x, y: i32) => { + if x >= 0 && x < width && y >= 0 && y < height do return visible_cells[x + y * width]; + return .Empty; +} + +set_visible_cell :: (use this: ^Board, x, y: i32, cell: Board.Cell) => { + if x >= 0 && x < width && y >= 0 && y < height do visible_cells[x + y * width] = cell; +} + +get_true_cell :: (use this: ^Board, x, y: i32) => { + if x >= 0 && x < width && y >= 0 && y < height do return true_cells[x + y * width]; + return .Empty; +} + +set_true_cell :: (use this: ^Board, x, y: i32, cell: Board.Cell) => { + if x >= 0 && x < width && y >= 0 && y < height do true_cells[x + y * width] = cell; +} + +reveal_cell :: (use this: ^Board, x, y: i32) => { + set_visible_cell(this, x, y, get_true_cell(this, x, y)); +} + +flood_reveal :: (use this: ^Board, x, y: i32) => { + positions := array.make(Pos); + positions << .{x, y}; + + while positions.count != 0 { + p := positions[0]; + array.delete(^positions, 0); + + reveal_cell(this, p.x, p.y); + if get_true_cell(this, p.x, p.y) & .Is_Number do continue; + + for n: neighbors(this, p.x, p.y, include_corners=false) { + true_cell := get_true_cell(this, n.x, n.y); + visible_cell := get_visible_cell(this, n.x, n.y); + if true_cell != .Mine && visible_cell == .Covered { + positions << n; + } + } + } +} + +cell_size :: 32.0f +border :: 2.0f + +board: Board; + +load :: () { + random.set_seed(clock.time()); + init_board(^board, 20, 20); + generate_board(^board, 5); +} + +update :: (dt: f32) { + if hb.input.keyIsDown(.Escape) { + hb.window.setShouldClose(true); + } + + #persist last_mouse_down := false; + mouse_down := hb.input.mouseIsDown(.Left); + if last_mouse_down && !mouse_down { + mx, my := hb.input.mouseGetPos(); + cx, cy := cast(i32) math.floor(mx / cell_size), cast(i32) math.floor(my / cell_size); + if cx < board.width && cy < board.height { + flood_reveal(^board, cx, cy); + } + } + last_mouse_down = mouse_down; +} + +draw :: () { + size :: cell_size + + hb.graphics.setClearColor(0.05, 0.05, 0.05, 1); + hb.graphics.clear(); + + for y: board.height { + for x: board.width { + cell := get_visible_cell(^board, x, y); + if cell == .Empty { + hb.graphics.setColor(0.1, 0.1, 0.1); + hb.graphics.rectangle(.Fill, ~~x * size, ~~y * size, size - border, size - border); + } + + if cell == .Covered { + hb.graphics.setColor(0.6, 0.6, 0.6); + hb.graphics.rectangle(.Fill, ~~x * size, ~~y * size, size - border, size - border); + } + + if cell == .Mine { + hb.graphics.setColor(0.1, 0.1, 0.1); + hb.graphics.rectangle(.Fill, ~~x * size, ~~y * size, size - border, size - border); + hb.graphics.setColor(1, 0, 0); + hb.graphics.circle(.Fill, ~~x * size + 0.5 * size, ~~y * size + 0.5 * size, (size - 2 * border) / 2); + } + + if cell & .Is_Number { + buf: [2] u8; + @Bug // Unary bitwise-not does not work on enum values + s := conv.str_format(buf, "{}", cast(i32) (cell - .Is_Number)); + w := hb.graphics.getTextWidth(s); + + hb.graphics.setColor(0.3, 0.3, 0.3); + hb.graphics.rectangle(.Fill, ~~x * size, ~~y * size, size - border, size - border); + hb.graphics.setColor(0.7, 0.5, 0.5); + hb.graphics.print(s, ~~x * size + (size - w) / 2, ~~(y + 1) * size - 3 * border); + } + } + } + + mx, my := hb.input.mouseGetPos(); + mx, my = size * math.floor(mx / size), size * math.floor(my / size); + if mx < ~~board.width * size && my < ~~board.height * size { + hb.graphics.setColor(1, 1, 1, 0.3); + hb.graphics.rectangle(.Fill, ~~mx, ~~my, size - border, size - border); + } +} diff --git a/tests/simp.onyx b/tests/simp.onyx index 4c8020b..164c70b 100644 --- a/tests/simp.onyx +++ b/tests/simp.onyx @@ -26,14 +26,19 @@ load :: () { printf("Width is: {}\n", hb.graphics.getTextWidth("Hello, World!")); + better_font := hb.graphics.newFont("/usr/share/fonts/TTF/InputSerif-Bold.ttf", 32); + assert(better_font != 0, "Failed to load font!"); + hb.graphics.setFont(better_font); + // hb.graphics.setFont(); + // hb.window.setDimensions(1200, 900); } -keydown :: (key, mods: i32) { +keydown :: (key: hb.input.KeyConstant, mods: i32) { printf("Key down! {} {}\n", key, mods); } -keyup :: (key: i32) { +keyup :: (key: hb.input.KeyConstant) { printf("Key up! {}\n", key); } @@ -109,8 +114,10 @@ draw :: () { hb.graphics.arc(.Line, 300, 300, 100, math.PI / 4, math.PI * 7 / 4); hb.graphics.setColor(0, 1, 1); + hb.graphics.rectangle(.Fill, 400, 90 - 32, 200 + math.sin(t) * 100, 10); hb.graphics.print("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+`~[{}]\|<,>./?'\";:", 400, 100, 200 + math.sin(t) * 100); + hb.graphics.setLineWidth(3); hb.graphics.setColor(1, 1, 1); - hb.graphics.circle(.Line, 200, 500, 100, 100); + hb.graphics.circle(.Line, 200, 500, 50, 100); } -- 2.25.1