From 664334fd001faa1494f73d6924423b7c90425c63 Mon Sep 17 00:00:00 2001 From: koniifer Date: Wed, 6 Nov 2024 01:47:22 +0000 Subject: [PATCH 01/21] unicode stuff for psf rendering make silly keyboard app --- Cargo.lock | 312 +++++++++++++++--- sysdata/libraries/render/src/software.hb | 96 +++++- sysdata/libraries/render/src/text.hb | 103 +++++- sysdata/libraries/stn/src/buffer.hb | 2 +- sysdata/libraries/stn/src/string.hb | 1 - .../render_example/src/examples/text.hb | 176 ++++++++-- sysdata/system_config.toml | 8 +- 7 files changed, 603 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af66e89..680a1a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.34" +version = "1.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" +checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" dependencies = [ "shlex", ] @@ -173,6 +173,17 @@ dependencies = [ "logos", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -241,9 +252,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" dependencies = [ "allocator-api2", "equivalent", @@ -253,14 +264,14 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#e8f1d2af8ca7520bbb695dcaf1029a348f29592e" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#e8f1d2af8ca7520bbb695dcaf1029a348f29592e" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" dependencies = [ - "hashbrown 0.15.0", + "hashbrown 0.15.1", "hbbytecode", "hbvm", "log", @@ -270,7 +281,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#e8f1d2af8ca7520bbb695dcaf1029a348f29592e" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" dependencies = [ "hbbytecode", ] @@ -299,13 +310,142 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.5.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -315,7 +455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", ] [[package]] @@ -334,7 +474,7 @@ dependencies = [ "aarch64-cpu", "crossbeam-queue", "derive_more", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "hbvm", "limine", "log", @@ -366,6 +506,12 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02034f8f6b3e7bf050f310fbaf6db0018b8e54b75598d0a4c97172054752fede" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -663,6 +809,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "str-reader" version = "0.1.2" @@ -687,19 +839,25 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.0" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "tinyvec_macros", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] [[package]] name = "tock-registers" @@ -752,27 +910,12 @@ dependencies = [ "x86", ] -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-xid" version = "0.2.6" @@ -802,15 +945,27 @@ dependencies = [ [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "versioning" version = "0.1.3" @@ -986,6 +1141,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "x2apic" version = "0.4.3" @@ -1042,8 +1209,75 @@ dependencies = [ "serde", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 588eee2..8f15843 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -1,6 +1,6 @@ .{math, memory, dt} := @use("../../stn/src/lib.hb"); .{Color, text} := @use("lib.hb"); -.{get_glyph, Font} := text; +.{get_glyph, get_glyph_unicode, Font, UNC_TABLE_SIZE} := text; .{Vec2} := math // safety: don't use before init() or you will get a memory access violation @@ -266,29 +266,90 @@ put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: current_char := str loop if *current_char == 0 break else { - if *current_char == 10 { - cursor.x = pos.x - cursor.y += font.height + font.line_gap + glyph_data := memory.dangling(^u8) + + if font.unicode != null { + code_point := @as(uint, 0) + first_byte := *current_char + num_bytes := 1 + + if (first_byte & 0x80) == 0 { + code_point = first_byte + } else if (first_byte & 0xE0) == 0xC0 { + num_bytes = 2 + code_point = first_byte & 0x1F + } else if (first_byte & 0xF0) == 0xE0 { + num_bytes = 3 + code_point = first_byte & 0xF + } else if (first_byte & 0xF8) == 0xF0 { + // handle later + current_char += 1 + continue + } else { + current_char += 1 + continue + } + + valid_sequence := true + i := 1 + loop if i >= num_bytes break else { + current_char += 1 + // have to check twice due to odd compiler bug...? + if *current_char == 0 | (*current_char & 0xC0) != 0x80 { + valid_sequence = false + } + if valid_sequence == false { + break + } + code_point = code_point << 6 | *current_char & 0x3F + i += 1 + } + + if valid_sequence == false { + current_char += 1 + continue + } + + current_char += 1 + + if code_point == 10 { + cursor.x = pos.x + cursor.y += font.height + font.line_gap + continue + } + + if code_point < UNC_TABLE_SIZE { + glyph_index := *(font.unicode + code_point) + + if glyph_index == 0xFFFF { + continue + } + glyph_data = font.data + glyph_index * font.bytes_per_glyph + } else { + continue + } + } else { + if *current_char > font.num_glyphs { + continue + } + glyph_data = @inline(get_glyph, font, *current_char) + if *current_char == 10 { + cursor.x = pos.x + cursor.y += font.height + font.line_gap + current_char += 1 + continue + } current_char += 1 - continue } - glyph_data := @inline(get_glyph, font, @intcast(*current_char)) - // ! cool null check don't disable :dead: - // !! i disabled the cool null check because compiler - // if glyph_data == null { - // current_char += 1 - // continue - // } + if cursor.y + font.height > surface.height break if cursor.x % surface.width + font.width >= surface.width { cursor.x = pos.x cursor.y += font.height + font.line_gap } - if cursor.y + font.height > surface.height break dest := @inline(indexptr, surface, cursor.x, cursor.y) - src := glyph_data rows_remaining := font.height loop if rows_remaining == 0 break else { @@ -303,7 +364,7 @@ put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: } pixel_dest += 1 mask >>= 1 - if mask == 0 { + if mask == 0 & bits_remaining > 0 { glyph_data += 1 byte = *glyph_data mask = 0x80 @@ -311,13 +372,14 @@ put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: bits_remaining -= 1 } - glyph_data += 1 + if mask != 0x80 { + glyph_data += 1 + } dest += surface.width rows_remaining -= 1 } cursor.x += font.width + font.char_gap - current_char += 1 } return diff --git a/sysdata/libraries/render/src/text.hb b/sysdata/libraries/render/src/text.hb index 37cabf7..5a70a07 100644 --- a/sysdata/libraries/render/src/text.hb +++ b/sysdata/libraries/render/src/text.hb @@ -1,4 +1,4 @@ -.{log} := @use("../../stn/src/lib.hb") +.{log, memory} := @use("../../stn/src/lib.hb") PSF1Header := packed struct { magic: u16, @@ -23,9 +23,9 @@ Font := struct { height: uint, num_glyphs: uint, bytes_per_glyph: uint, - has_unicode_table: bool, line_gap: uint, char_gap: uint, + unicode: ?^u16, } font_from_psf1 := fn(psf: ^u8): ?Font { @@ -43,13 +43,13 @@ font_from_psf1 := fn(psf: ^u8): ?Font { header.character_size, 256, header.character_size, - false, 0, 0, + null, ) } -font_from_psf2 := fn(psf: ^u8): ?Font { +font_from_psf2 := fn(psf: ^u8, unicode: bool): ?Font { header := @as(^PSF2Header, @bitcast(psf)) if header.magic != 0x864AB572 { log.error("failed to load psf font: not a psf2 font, idiot\0") @@ -58,18 +58,105 @@ font_from_psf2 := fn(psf: ^u8): ?Font { psf += header.header_size - return .( + font := Font.( psf, header.width, header.height, header.num_glyph, header.bytes_per_glyph, - (header.flags & 1) != 0, 0, 0, + null, ) + if (header.flags & 1) != 0 & unicode { + init_unicode(&font) + } + return font } -get_glyph := fn(font: Font, index: uint): ^u8 { - return font.data + index * font.bytes_per_glyph +get_glyph := fn(font: Font, index: u8): ^u8 { + return font.data + @as(uint, index) * font.bytes_per_glyph +} + +UNC_TABLE_SIZE := 1 << 16 + +init_unicode := fn(font: ^Font): void { + font.unicode = memory.alloc(u16, UNC_TABLE_SIZE) + + @inline(memory.set, u16, &0xFFFF, font.unicode, UNC_TABLE_SIZE) + + table := font.data + font.num_glyphs * font.bytes_per_glyph + curr_glyph := @as(u16, 0) + + loop if curr_glyph >= font.num_glyphs break else { + loop { + byte := *table + table += 1 + + if byte == 0xFF break + if byte == 0xFE { + continue + } + + unicode := @as(uint, 0) + bytes_to_read := @as(uint, 1) + + if (byte & 0x80) == 0 { + unicode = byte + } else if (byte & 0xE0) == 0xC0 { + unicode = byte & 0x1F + bytes_to_read = 2 + } else if (byte & 0xF0) == 0xE0 { + unicode = byte & 0xF + bytes_to_read = 3 + } else if (byte & 0xF8) == 0xF0 { + unicode = byte & 0x7 + bytes_to_read = 4 + } else { + continue + } + + valid := true + loop if bytes_to_read <= 1 break else { + next_byte := *table + if (next_byte & 0xC0) != 0x80 { + valid = false + } + if valid == false { + break + } + unicode = unicode << 6 | next_byte & 0x3F + table += 1 + bytes_to_read -= 1 + } + + if valid == false continue + + if bytes_to_read == 4 { + if unicode < 0x10000 | unicode > 0x10FFFF continue + + if unicode <= 0xFFFF { + if unicode < UNC_TABLE_SIZE { + *(@unwrap(font.unicode) + unicode) = curr_glyph + } + } else { + unicode -= 0x10000 + high_surrogate := 0xD800 | unicode >> 10 & 0x3FF + low_surrogate := 0xDC00 | unicode & 0x3FF + + if high_surrogate < UNC_TABLE_SIZE { + *(@unwrap(font.unicode) + high_surrogate) = curr_glyph + } + if low_surrogate < UNC_TABLE_SIZE { + *(@unwrap(font.unicode) + low_surrogate) = curr_glyph + } + } + } else { + if unicode < UNC_TABLE_SIZE { + *(@unwrap(font.unicode) + unicode) = curr_glyph + } + } + } + curr_glyph += 1 + } } \ No newline at end of file diff --git a/sysdata/libraries/stn/src/buffer.hb b/sysdata/libraries/stn/src/buffer.hb index f2fad6b..680aec8 100644 --- a/sysdata/libraries/stn/src/buffer.hb +++ b/sysdata/libraries/stn/src/buffer.hb @@ -1,6 +1,6 @@ string := @use("string.hb") -recv := fn($Expr: type, buffer_id: int, memory_map_location: ^u8): ?^Expr { +recv := fn($Expr: type, buffer_id: int, memory_map_location: ^u8): void { return @eca(4, buffer_id, memory_map_location, @sizeof(Expr)) } diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 5551b5c..71a8bcb 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -1,4 +1,3 @@ - length := fn(ptr: ^u8): uint { len := 0 loop if *(ptr + len) == 0 return len else len += 1 diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index 92c66f5..f13770f 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -1,37 +1,163 @@ +.{memory, log, string} := @use("../../../../libraries/stn/src/lib.hb") render := @use("../../../../libraries/render/src/lib.hb") /* expected result: - extended unicode test */ + trash notepad app, colours should change with r,g,b,w keys */ psf := @embed("../../../../consolefonts/tamsyn/10x20r.psf") +send_byte := fn(byte: u8): u8 { + memory.outb(96, byte) + return memory.inb(96) +} + example := fn(): void { - screen := render.init(false) - font := render.text.font_from_psf2(@bitcast(&psf)) + screen := render.init(true) + font := render.text.font_from_psf2(@bitcast(&psf), false) - str := "Extended unicode test: + msg := "ableboard (tm) (patent pending):\n\0" + msg_len := string.length(msg) -ABCDEFGHIJKLMNOPQRSTUVWXYZ -abcdefghijklmnopqrstuvwxyz -0123456789 -!\"#$%&'()*+,-./:;<=>?@[]^_`{|}~ -ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞß -àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ -ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğ -ĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿ -„‟†‡•‣․‥…‧ -₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿ -∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟ -─│┌┐└┘├┤┬┴┼╋╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿ -▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟ -■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○ -←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻ - 、。〃〄々〆〇〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟 -✀✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟ -✠✡✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿\0" + buf := memory.alloc(u8, 4096) + + @inline(memory.copy, u8, msg, buf, msg_len) + cursor := buf + msg_len + + if font == null { + return + } + + _ = send_byte(238) + log.info("PS/2 Driver Loaded\0") + if send_byte(238) == 238 { + log.info("PS/2 Keyboard Echoed\0") + } + if send_byte(244) == 250 { + log.info("Enabled scanning\0") + } + + color := render.white + + prev_input := 250 + + loop { + input := memory.inb(96) + if input != prev_input { + prev_input = input + char := map_keys(input) + if char != 0 { + if char == 0xA { + *cursor = 32 + cursor += 1; + *cursor = 92 + cursor += 1 + } else if char == 0x72 { + color = render.red + } else if char == 0x67 { + color = render.green + } else if char == 0x62 { + color = render.blue + } else if char == 0x77 { + color = render.white + }; + *cursor = char + cursor += 1 + } + } + render.clear(screen, render.black) + render.put_text(screen, font, .(0, 0), color, buf) + render.sync(screen) + } - render.clear(screen, render.black) - render.put_text(screen, @unwrap(font), .(0, 0), render.white, str) - render.sync(screen) return +} + +map_keys := fn(inp: u8): u8 { + if inp == 0x29 { + return 0x7E + } else if inp == 0x2 { + return 0x31 + } else if inp == 0x3 { + return 0x32 + } else if inp == 0x4 { + return 0x33 + } else if inp == 0x5 { + return 0x34 + } else if inp == 0x6 { + return 0x35 + } else if inp == 0x7 { + return 0x36 + } else if inp == 0x8 { + return 0x37 + } else if inp == 0x9 { + return 0x38 + } else if inp == 0xA { + return 0x39 + } else if inp == 0xB { + return 0x30 + } else if inp == 0xC { + return 0x2D + } else if inp == 0xD { + return 0x3D + } else if inp == 0x1C { + return 0xA + } else if inp == 0x10 { + return 0x71 + } else if inp == 0x11 { + return 0x77 + } else if inp == 0x12 { + return 0x65 + } else if inp == 0x13 { + return 0x72 + } else if inp == 0x14 { + return 0x74 + } else if inp == 0x15 { + return 0x79 + } else if inp == 0x16 { + return 0x75 + } else if inp == 0x17 { + return 0x69 + } else if inp == 0x18 { + return 0x6F + } else if inp == 0x19 { + return 0x70 + } else if inp == 0x1E { + return 0x61 + } else if inp == 0x1F { + return 0x73 + } else if inp == 0x20 { + return 0x64 + } else if inp == 0x21 { + return 0x66 + } else if inp == 0x22 { + return 0x67 + } else if inp == 0x23 { + return 0x68 + } else if inp == 0x24 { + return 0x6A + } else if inp == 0x25 { + return 0x6B + } else if inp == 0x26 { + return 0x6C + } else if inp == 0x2C { + return 0x7A + } else if inp == 0x2D { + return 0x78 + } else if inp == 0x2E { + return 0x63 + } else if inp == 0x2F { + return 0x76 + } else if inp == 0x30 { + return 0x62 + } else if inp == 0x31 { + return 0x6E + } else if inp == 0x32 { + return 0x6D + } else if inp == 0x2B { + return 0x5C + } else if inp == 0x39 { + return 32 + } else { + return 0 + } } \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 0491a37..9a9ccbb 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -# [boot.limine.ableos.modules.render_example] -# path = "boot:///render_example.hbf" +[boot.limine.ableos.modules.render_example] +path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -58,8 +58,8 @@ path = "boot:///ps2_keyboard_driver.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -[boot.limine.ableos.modules.ps2_mouse_driver] -path = "boot:///ps2_mouse_driver.hbf" +# [boot.limine.ableos.modules.ps2_mouse_driver] +# path = "boot:///ps2_mouse_driver.hbf" # [boot.limine.ableos.modules.app_bar] # path = "boot:///app_bar.hbf" From 404ea22c465e2a796daf5fd95888b73ec7685eb3 Mon Sep 17 00:00:00 2001 From: koniifer Date: Wed, 6 Nov 2024 01:49:57 +0000 Subject: [PATCH 02/21] rebase --- sysdata/programs/horizon/src/main.hb | 2 +- sysdata/system_config.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index be9f924..5c79dfc 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -38,7 +38,7 @@ main := fn(): int { pos_inner := Vec2(uint).((window.width - side) / 2, (window.height - side) / 2) str := "Window Title Bar\0" // really we should null check but it is a bit broked - font := @unwrap(render.text.font_from_psf2(@bitcast(&psf))) + font := @unwrap(render.text.font_from_psf2(@bitcast(&psf), false)) mouse_x := 0 mouse_y := 0 diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 9a9ccbb..50020fe 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -37,8 +37,8 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver_test] # path = "boot:///serial_driver_test.hbf" -[boot.limine.ableos.modules.horizon] -path = "boot:///horizon.hbf" +# [boot.limine.ableos.modules.horizon] +# path = "boot:///horizon.hbf" # [boot.limine.ableos.modules.horizon_testing_program] # path = "boot:///horizon_testing_program.hbf" From aafe047b5108ae7c9d27d71b8ed591bcee525700 Mon Sep 17 00:00:00 2001 From: koniifer Date: Wed, 6 Nov 2024 01:50:28 +0000 Subject: [PATCH 03/21] forgor --- sysdata/system_config.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 50020fe..e9e920c 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -49,8 +49,8 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.svga_driver] # path = "boot:///svga_driver.hbf" -[boot.limine.ableos.modules.ps2_keyboard_driver] -path = "boot:///ps2_keyboard_driver.hbf" +# [boot.limine.ableos.modules.ps2_keyboard_driver] +# path = "boot:///ps2_keyboard_driver.hbf" # [boot.limine.ableos.modules.filesystem_fat32] # path = "boot:///filesystem_fat32.hbf" From 85e63eb51c6b5bf1c955104ffa97ad1298e47d9b Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 22:16:08 -0600 Subject: [PATCH 04/21] Add backspace --- sysdata/programs/render_example/src/examples/text.hb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index f13770f..d049034 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -39,7 +39,6 @@ example := fn(): void { color := render.white prev_input := 250 - loop { input := memory.inb(96) if input != prev_input { @@ -59,13 +58,20 @@ example := fn(): void { color = render.blue } else if char == 0x77 { color = render.white + } else if char == 0xE { + cursor -= 1; + *cursor = 32 + + continue }; *cursor = char cursor += 1 } } render.clear(screen, render.black) + render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) render.put_text(screen, font, .(0, 0), color, buf) + render.sync(screen) } @@ -99,6 +105,8 @@ map_keys := fn(inp: u8): u8 { return 0x2D } else if inp == 0xD { return 0x3D + } else if inp == 0xE { + return 0xE } else if inp == 0x1C { return 0xA } else if inp == 0x10 { From 795f10986f2ae55ad9d0c109059ac9d393254ae6 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 22:58:52 -0600 Subject: [PATCH 05/21] Add partial support for sending mouse position updates --- sysdata/libraries/intouch/src/lib.hb | 31 +++++++++++++++++-- sysdata/programs/horizon/src/main.hb | 14 +++++++-- sysdata/programs/ps2_mouse_driver/src/main.hb | 11 +++++-- sysdata/system_config.toml | 12 +++---- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index e592a7b..a8d3e36 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -1,15 +1,40 @@ stn := @use("../../stn/src/lib.hb"); -.{log, buffer} := stn +.{log, buffer, memory} := stn +keycodes := @use("keycodes.hb") + events := @use("events.hb"); .{KeyEvent, MouseEvent} := events recieve_key_event := fn(): ?KeyEvent { - buf := buffer.search("PS/2 Keyboard\0") + mem_page := memory.request_page(1) + + buf_id := buffer.search("PS/2 Keyboard\0") + // Read out of the keyboard buffer here + buffer.recv(KeyEvent, buf_id, mem_page) + + key_event := KeyEvent.(0, 0, 2) + // return key_event return null } -recieve_mouse_event := fn(): ?MouseEvent { +recieve_mouse_event := fn(): ?^MouseEvent { + mem_page := memory.request_page(1) + + buf_id := buffer.search("PS/2 Mouse\0") + + // Read out of the Mouse buffer here + buffer.recv(MouseEvent, buf_id, mem_page) + if *mem_page == 0 { + // log.info("Haha\0") + } else { + log.info("Mouse event recieved in horizon.\0") + dx := *mem_page + dy := *mem_page + 1 + mevent := MouseEvent.(dx, dy, 0, 0, 0) + return &mevent + } + return null } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 5c79dfc..e78a4b3 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -19,6 +19,13 @@ Window := struct { psf := @embed("../../../consolefonts/tamsyn/10x20r.psf") main := fn(): int { + I_LOOP := 10000 + loop { + if I_LOOP >= 0 { + break + } + I_LOOP += 1 + } win_buff := buffer.create("XHorizon\0") screen := render.init(true) @@ -60,9 +67,12 @@ main := fn(): int { { // get input events from drivers via intouch - key_event := intouch.recieve_key_event() + // key_event := intouch.recieve_key_event() mouse_event := intouch.recieve_mouse_event() - + if mouse_event != null { + mouse_x += mouse_event.x_change + mouse_y -= mouse_event.y_change + } // render mouse render.put_rect(screen, .(mouse_x, mouse_y), .(20, 20), render.white) // Send events to focused window diff --git a/sysdata/programs/ps2_mouse_driver/src/main.hb b/sysdata/programs/ps2_mouse_driver/src/main.hb index 62920d1..82c849e 100644 --- a/sysdata/programs/ps2_mouse_driver/src/main.hb +++ b/sysdata/programs/ps2_mouse_driver/src/main.hb @@ -1,6 +1,9 @@ .{memory, buffer, log, string, math} := @use("../../../libraries/stn/src/lib.hb") Vec2 := math.Vec2 +intouch := @use("../../../libraries/intouch/src/lib.hb"); +.{MouseEvent} := intouch.events + i9 := packed struct {sign: bool, value: u8} Button := struct {id: u8} LeftButton := Button.(1) @@ -91,10 +94,9 @@ set_up_mouse := fn(): void { button_states := @as(u8, 0) main := fn(): int { + mouse_buffer := buffer.create("PS/2 Mouse\0") format_page := memory.alloc(u8, 1024) - mouse_buffer := buffer.create("Mouse\0") - send_byte(0x64, 0xA8) log.info("Aux mouse device enabled.\0") @@ -142,7 +144,10 @@ main := fn(): int { dy.sign = (status & 0x20) == 0 if dy.value != 0 & dx.value != 0 { - mouse_moved(.(dx, dy)) + event := MouseEvent.(dx.value, dy.value, 0, 0, 0) + buffer.write(MouseEvent, &event, mouse_buffer) + + // mouse_moved(.(dx, dy)) } } diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index e9e920c..b1d8a65 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -[boot.limine.ableos.modules.render_example] -path = "boot:///render_example.hbf" +# [boot.limine.ableos.modules.render_example] +# path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -37,8 +37,8 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver_test] # path = "boot:///serial_driver_test.hbf" -# [boot.limine.ableos.modules.horizon] -# path = "boot:///horizon.hbf" +[boot.limine.ableos.modules.horizon] +path = "boot:///horizon.hbf" # [boot.limine.ableos.modules.horizon_testing_program] # path = "boot:///horizon_testing_program.hbf" @@ -58,8 +58,8 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -# [boot.limine.ableos.modules.ps2_mouse_driver] -# path = "boot:///ps2_mouse_driver.hbf" +[boot.limine.ableos.modules.ps2_mouse_driver] +path = "boot:///ps2_mouse_driver.hbf" # [boot.limine.ableos.modules.app_bar] # path = "boot:///app_bar.hbf" From 879bbfa1739e976443965ea6f918f8d0d181bd30 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 23:01:06 -0600 Subject: [PATCH 06/21] rework logic --- sysdata/libraries/intouch/src/lib.hb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index a8d3e36..d57177a 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -26,10 +26,7 @@ recieve_mouse_event := fn(): ?^MouseEvent { // Read out of the Mouse buffer here buffer.recv(MouseEvent, buf_id, mem_page) - if *mem_page == 0 { - // log.info("Haha\0") - } else { - log.info("Mouse event recieved in horizon.\0") + if *mem_page != 0 { dx := *mem_page dy := *mem_page + 1 mevent := MouseEvent.(dx, dy, 0, 0, 0) From 6a319c55b0e3ed93c0a43a9b7cdc5b69dd4198e3 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 23:01:15 -0600 Subject: [PATCH 07/21] Proper Y direction --- sysdata/programs/horizon/src/main.hb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index e78a4b3..b85acbd 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -71,7 +71,7 @@ main := fn(): int { mouse_event := intouch.recieve_mouse_event() if mouse_event != null { mouse_x += mouse_event.x_change - mouse_y -= mouse_event.y_change + mouse_y += mouse_event.y_change } // render mouse render.put_rect(screen, .(mouse_x, mouse_y), .(20, 20), render.white) From 12883ac9266892b4fae6d9078bec6aedf28f4a4d Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 23:04:29 -0600 Subject: [PATCH 08/21] Remove busy loop --- sysdata/programs/horizon/src/main.hb | 46 +--------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index b85acbd..0756293 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -19,13 +19,6 @@ Window := struct { psf := @embed("../../../consolefonts/tamsyn/10x20r.psf") main := fn(): int { - I_LOOP := 10000 - loop { - if I_LOOP >= 0 { - break - } - I_LOOP += 1 - } win_buff := buffer.create("XHorizon\0") screen := render.init(true) @@ -35,15 +28,10 @@ main := fn(): int { window := render.new_surface(screen.width / 3, screen.height / 3) - x := 0 - mem_buf := memory.request_page(1) color := random.any(render.Color) side := window.width / 8 - vel_inner := Vec2(int).(1, 1) - pos_inner := Vec2(uint).((window.width - side) / 2, (window.height - side) / 2) - str := "Window Title Bar\0" // really we should null check but it is a bit broked font := @unwrap(render.text.font_from_psf2(@bitcast(&psf), false)) @@ -78,41 +66,9 @@ main := fn(): int { // Send events to focused window } - if pos_inner.x == 0 | pos_inner.x == window.width - side { - vel_inner.x = -vel_inner.x - color = random.any(render.Color) - } - if pos_inner.y == 20 | pos_inner.y == window.height - side { - vel_inner.y = -vel_inner.y - color = random.any(render.Color) - } - // TODO: Get windows out of a collection and iter through - window_count := 0 + render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) - // loop { - // render.clear(window, render.black) - - // // Draw the decorators - // { - // render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) - // render.put_rect(window, .(0, 0), .(window.width - 1, 20), render.white) - // render.put_text(window, font, .(window.width / 2, 1), render.white, str) - // } - // render.put_filled_rect(window, pos_inner, .(side, side), color) - - // // Apply the image to the screen - // pos := Vec2(uint).(x, 100) - - // render.put_surface(screen, window, pos, false) - // if window_count >= 1 { - // x = 0 - // break - // } - // window_count += 1 - // x += screen.width / 2 - // } - pos_inner += @bitcast(vel_inner) // Sync the screen render.sync(screen) From a49ac7bca03c00eb1b6a3da16b7300ff0c1a0bf5 Mon Sep 17 00:00:00 2001 From: Able Date: Wed, 6 Nov 2024 09:36:17 -0600 Subject: [PATCH 09/21] Label Widget inside horizon --- sysdata/programs/horizon/src/main.hb | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 0756293..5535bcd 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -4,9 +4,35 @@ stn := @use("../../../libraries/stn/src/lib.hb"); horizon_api := @use("../../../libraries/horizon_api/src/lib.hb") -render := @use("../../../libraries/render/src/lib.hb") +render := @use("../../../libraries/render/src/lib.hb"); +.{Surface} := render; +.{Font} := render.text intouch := @use("../../../libraries/intouch/src/lib.hb") +Label := struct {is_dirty: bool, surface: Surface, text: ^u8, text_length: uint} +set_label_text := fn(label: Label, text: ^u8, text_length: uint): void { + label.is_dirty = true + label.text = text + label.text_length = text_length +} + +render_label_to_surface := fn(surface: Surface, label: Label, font: Font): void { + if label.is_dirty { + render.clear(label.surface, render.blue) + render.put_text(label.surface, font, .(0, 0), render.red, "hi\0") + } + pos := Vec2(uint).(100, 100) + render.put_surface(surface, label.surface, pos, false) + render.sync(surface) +} + +new_label := fn(text: ^u8): Label { + text_surface := render.new_surface(100, 16) + text_length := string.length(text) + label := Label.(true, text_surface, text, text_length) + return label +} + Window := struct { // TODO: Replace this with widgets implicit_framebuffer: render.Surface, @@ -37,6 +63,7 @@ main := fn(): int { mouse_x := 0 mouse_y := 0 + text_label := new_label("Hi\0") loop { // Clear the screen @@ -70,6 +97,8 @@ main := fn(): int { render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) + render_label_to_surface(screen, text_label, font) + // Sync the screen render.sync(screen) } From ab8522fbe1f893a7998c8d470a7bb8b0fd40c02c Mon Sep 17 00:00:00 2001 From: Able Date: Wed, 6 Nov 2024 11:43:40 -0600 Subject: [PATCH 10/21] widget UI --- sysdata/libraries/horizon_api/src/lib.hb | 1 + sysdata/libraries/horizon_api/src/ui.hb | 50 ++++++++++++++++++++ sysdata/libraries/horizon_api/src/widgets.hb | 41 ++++++++++++++-- sysdata/programs/horizon/src/main.hb | 38 ++++----------- 4 files changed, 97 insertions(+), 33 deletions(-) create mode 100644 sysdata/libraries/horizon_api/src/ui.hb diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb index 1db3411..72f58d1 100644 --- a/sysdata/libraries/horizon_api/src/lib.hb +++ b/sysdata/libraries/horizon_api/src/lib.hb @@ -6,6 +6,7 @@ render := @use("../../../libraries/render/src/lib.hb") input := @use("../../intouch/src/lib.hb") widgets := @use("widgets.hb") +ui := @use("ui.hb") WindowID := struct { host_id: int, diff --git a/sysdata/libraries/horizon_api/src/ui.hb b/sysdata/libraries/horizon_api/src/ui.hb new file mode 100644 index 0000000..93fae2d --- /dev/null +++ b/sysdata/libraries/horizon_api/src/ui.hb @@ -0,0 +1,50 @@ +stn := @use("../../../libraries/stn/src/lib.hb"); +.{string, log} := stn; +.{Vec2} := stn.math + +render := @use("../../../libraries/render/src/lib.hb"); +.{Surface} := render; +.{Font} := render.text + +UI := struct { + raw: ^u8, + raw_length: uint, + is_dirty: bool, + surface: Surface, +} + +render_ui := fn(surface: Surface, ui: UI): void { + if ui.is_dirty { + render.clear(ui.surface, render.black) + ui.is_dirty = false + } + pos := Vec2(uint).(0, 0) + render.put_surface(surface, ui.surface, pos, false) +} + +sexpr_parser := fn(sexpr: ^u8): UI { + cursor := sexpr + paren_balance := 0 + loop { + if *cursor == 0 { + if paren_balance != 0 { + log.error("Unbalanced Parens\0") + } + break + } else if *cursor == 40 { + log.info("Open paren\0") + paren_balance += 1 + } else if *cursor == 41 { + log.info("Closed paren\0") + paren_balance -= 1 + } + + cursor += 1 + } + + length := string.length(sexpr) + + ui_surface := render.new_surface(100, 100) + + return UI.(sexpr, length, true, ui_surface) +} \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets.hb b/sysdata/libraries/horizon_api/src/widgets.hb index 04574f2..9bf56f7 100644 --- a/sysdata/libraries/horizon_api/src/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets.hb @@ -1,6 +1,13 @@ // Widget types // End types +stn := @use("../../../libraries/stn/src/lib.hb"); +.{string, log} := stn; +.{Vec2} := stn.math + +render := @use("../../../libraries/render/src/lib.hb"); +.{Surface} := render; +.{Font} := render.text LayoutChildHorizontalFirst := 0 LayoutChildVerticalFirst := 1 @@ -12,9 +19,33 @@ Size := struct { max_height: int, } -Widget := struct { - size: Size, - clickable: bool, - layout: u8, - a: bool, +Label := struct { + is_dirty: bool, + surface: Surface, + text: ^u8, + text_length: uint, +} + +set_label_text := fn(label: Label, text: ^u8): void { + text_length := string.length(text) + + label.is_dirty = true + label.text = text + label.text_length = text_length +} + +render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: Vec2(uint)): void { + if label.is_dirty { + render.clear(label.surface, render.black) + render.put_text(label.surface, font, .(0, 0), render.white, label.text) + } + render.put_surface(surface, label.surface, pos, false) + render.sync(surface) +} + +new_label := fn(text: ^u8): Label { + text_surface := render.new_surface(100, 20) + text_length := string.length(text) + label := Label.(true, text_surface, text, text_length) + return label } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 5535bcd..c33882e 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -2,37 +2,15 @@ stn := @use("../../../libraries/stn/src/lib.hb"); .{string, memory, buffer, random, log} := stn; .{Vec2} := stn.math -horizon_api := @use("../../../libraries/horizon_api/src/lib.hb") +horizon_api := @use("../../../libraries/horizon_api/src/lib.hb"); +.{new_label, render_label_to_surface, set_label_text} := horizon_api.widgets; +.{sexpr_parser, render_ui} := horizon_api.ui render := @use("../../../libraries/render/src/lib.hb"); .{Surface} := render; .{Font} := render.text intouch := @use("../../../libraries/intouch/src/lib.hb") -Label := struct {is_dirty: bool, surface: Surface, text: ^u8, text_length: uint} -set_label_text := fn(label: Label, text: ^u8, text_length: uint): void { - label.is_dirty = true - label.text = text - label.text_length = text_length -} - -render_label_to_surface := fn(surface: Surface, label: Label, font: Font): void { - if label.is_dirty { - render.clear(label.surface, render.blue) - render.put_text(label.surface, font, .(0, 0), render.red, "hi\0") - } - pos := Vec2(uint).(100, 100) - render.put_surface(surface, label.surface, pos, false) - render.sync(surface) -} - -new_label := fn(text: ^u8): Label { - text_surface := render.new_surface(100, 16) - text_length := string.length(text) - label := Label.(true, text_surface, text, text_length) - return label -} - Window := struct { // TODO: Replace this with widgets implicit_framebuffer: render.Surface, @@ -64,6 +42,8 @@ main := fn(): int { mouse_x := 0 mouse_y := 0 text_label := new_label("Hi\0") + widgets := "()\0" + ui := sexpr_parser(widgets) loop { // Clear the screen @@ -87,6 +67,7 @@ main := fn(): int { if mouse_event != null { mouse_x += mouse_event.x_change mouse_y += mouse_event.y_change + set_label_text(text_label, "Mouse Moved\0") } // render mouse render.put_rect(screen, .(mouse_x, mouse_y), .(20, 20), render.white) @@ -96,9 +77,10 @@ main := fn(): int { // TODO: Get windows out of a collection and iter through render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) - - render_label_to_surface(screen, text_label, font) - + { + pos := Vec2(uint).(1, screen.height - 21) + render_label_to_surface(screen, text_label, font, pos) + } // Sync the screen render.sync(screen) } From 6a8d92d2c277c26e5823a71dbb6d98cf0ec9db83 Mon Sep 17 00:00:00 2001 From: koniifer Date: Thu, 7 Nov 2024 01:55:08 +0000 Subject: [PATCH 11/21] mostly qoi image support refactor image loader for multiple filetypes port some broken math stuff make logger more readable --- kernel/src/logger.rs | 27 +++-- repbuild/src/dev.rs | 3 - sysdata/libraries/render/src/image.hb | 102 ------------------ sysdata/libraries/render/src/image/bmp.hb | 52 +++++++++ sysdata/libraries/render/src/image/lib.hb | 34 ++++++ sysdata/libraries/render/src/image/qoi.hb | 101 +++++++++++++++++ sysdata/libraries/render/src/lib.hb | 2 +- sysdata/libraries/stn/src/math.hb | 47 ++++---- sysdata/libraries/stn/src/string.hb | 14 +++ .../src/examples/assets/mini.bmp | Bin 102454 -> 102454 bytes .../src/examples/assets/mini.qoi | Bin 0 -> 62864 bytes .../render_example/src/examples/image.hb | 45 ++++---- sysdata/programs/render_example/src/main.hb | 2 +- 13 files changed, 268 insertions(+), 161 deletions(-) delete mode 100644 sysdata/libraries/render/src/image.hb create mode 100644 sysdata/libraries/render/src/image/bmp.hb create mode 100644 sysdata/libraries/render/src/image/lib.hb create mode 100644 sysdata/libraries/render/src/image/qoi.hb create mode 100644 sysdata/programs/render_example/src/examples/assets/mini.qoi diff --git a/kernel/src/logger.rs b/kernel/src/logger.rs index fd9cc4a..d1bd6e1 100644 --- a/kernel/src/logger.rs +++ b/kernel/src/logger.rs @@ -36,13 +36,26 @@ impl log::Log for Logger { Level::Debug => "25", Level::Trace => "103", }; - let module = record.module_path().unwrap_or_default(); - let line = record.line().unwrap_or_default(); - crate::arch::log(format_args!( - "\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n", - record.args(), - )) - .expect("write to serial console"); + let module = record + .module_path() + .unwrap_or_default() + .rsplit_once(':') + .unwrap_or_default() + .1; + if module == "" { + crate::arch::log(format_args!( + "\x1b[38;5;{lvl_color}m{lvl}\x1b[0m: {}\r\n", + record.args(), + )) + .expect("write to serial console"); + } else { + let line = record.line().unwrap_or_default(); + crate::arch::log(format_args!( + "\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n", + record.args(), + )) + .expect("write to serial console"); + } } fn flush(&self) {} diff --git a/repbuild/src/dev.rs b/repbuild/src/dev.rs index d2a66e3..f93edd1 100644 --- a/repbuild/src/dev.rs +++ b/repbuild/src/dev.rs @@ -74,7 +74,6 @@ impl Package { &path, Options { fmt: true, - // optimize: true, ..Default::default() }, &mut bytes, @@ -83,7 +82,6 @@ impl Package { hblang::run_compiler( &path, Options { - // optimize: true, ..Default::default() }, &mut bytes, @@ -100,7 +98,6 @@ impl Package { &path, Options { dump_asm: true, - // optimize: true, ..Default::default() }, &mut bytes, diff --git a/sysdata/libraries/render/src/image.hb b/sysdata/libraries/render/src/image.hb deleted file mode 100644 index 249094a..0000000 --- a/sysdata/libraries/render/src/image.hb +++ /dev/null @@ -1,102 +0,0 @@ -.{Color, Surface, new_surface} := @use("./lib.hb"); -.{log, memory} := @use("../../stn/src/lib.hb") - -BitmapFileHeader := packed struct { - img_type: u16, - size: u32, - reserved_1: u16, - reserved_2: u16, - offset: u32, -} - -BitmapInfoHeader := packed struct { - size: u32, - width: i32, - height: i32, - planes: u16, - bits: u16, - compression: u32, - image_size: u32, - x_resolution: i32, - y_resolution: i32, - n_colours: u32, - important_colours: u32, -} - -BitmapColorHeader := packed struct { - red_mask: u32, - green_mask: u32, - blue_mask: u32, - alpha_mask: u32, - color_space_type: u32, - unused: u32, -} - -surface_from_bmp := fn(bmp: ^u8): ?Surface { - file_header := @as(^BitmapFileHeader, @bitcast(bmp)) - if file_header.img_type != 0x4D42 { - log.error("failed to load bmp image: not a bmp image, idiot\0") - return null - } - info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) - bmp += file_header.offset - - px := info_header.width * info_header.height - ptr := @as(^Color, @bitcast(bmp)) - tmp := @as(Color, idk) - row := @as(i32, 0) - - loop if row == info_header.height / 2 break else { - col := @as(i32, 0) - loop if col == info_header.width break else { - top_index := row * info_header.width + col - bottom_index := (info_header.height - 1 - row) * info_header.width + col - - tmp = *(ptr + top_index); - *(ptr + top_index) = *(ptr + bottom_index); - *(ptr + bottom_index) = tmp - - col += 1 - } - row += 1 - } - - return .(@bitcast(bmp), info_header.width, info_header.height) -} - -new_surface_from_bmp := fn(bmp: ^u8): ?Surface { - file_header := @as(^BitmapFileHeader, @bitcast(bmp)) - if file_header.img_type != 0x4D42 { - log.error("failed to load bmp image: not a bmp image, idiot\0") - return null - } - info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) - bmp += file_header.offset - - width := @as(uint, @intcast(info_header.width)) - height := @as(uint, @intcast(info_header.height)) - - surface := new_surface(width, height) - top_start_idx := surface.buf - bottom_start_idx := surface.buf + width * (height - 1) - rows_to_copy := height - top_cursor := @as(^Color, @bitcast(bmp)) - bottom_cursor := top_cursor + width * (height - 1) - - loop if rows_to_copy <= 1 break else { - @inline(memory.copy, Color, top_cursor, bottom_start_idx, @bitcast(width)) - @inline(memory.copy, Color, bottom_cursor, top_start_idx, @bitcast(width)) - - top_start_idx += surface.width - bottom_start_idx -= surface.width - top_cursor += width - bottom_cursor -= width - rows_to_copy -= 2 - } - - if rows_to_copy == 1 { - @inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(width)) - } - - return surface -} \ No newline at end of file diff --git a/sysdata/libraries/render/src/image/bmp.hb b/sysdata/libraries/render/src/image/bmp.hb new file mode 100644 index 0000000..690198b --- /dev/null +++ b/sysdata/libraries/render/src/image/bmp.hb @@ -0,0 +1,52 @@ +.{Color, Surface, new_surface, put_surface} := @use("../lib.hb"); +.{log, memory} := @use("../../../stn/src/lib.hb") + +BitmapFileHeader := packed struct { + magic: u16, + size: u32, + reserved_1: u16, + reserved_2: u16, + offset: u32, +} + +BitmapInfoHeader := packed struct { + size: u32, + width: i32, + height: i32, + planes: u16, + bits: u16, + compression: u32, + image_size: u32, + x_resolution: i32, + y_resolution: i32, + n_colours: u32, + important_colours: u32, +} + +BitmapColorHeader := packed struct { + red_mask: u32, + green_mask: u32, + blue_mask: u32, + alpha_mask: u32, + color_space_type: u32, + unused: u32, +} + +from := fn(bmp: ^u8): ?Surface { + file_header := @as(^BitmapFileHeader, @bitcast(bmp)) + info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) + bmp += file_header.offset + + if file_header.magic != 0x4D42 | info_header.width == 0 | info_header.height == 0 { + log.error("Invalid BMP image.\0") + return null + } + + width := @as(uint, info_header.width) + height := @as(uint, info_header.height) + lhs := Surface.(@bitcast(bmp), width, height) + rhs := new_surface(width, height) + put_surface(rhs, lhs, .(0, 0), true) + + return rhs +} \ No newline at end of file diff --git a/sysdata/libraries/render/src/image/lib.hb b/sysdata/libraries/render/src/image/lib.hb new file mode 100644 index 0000000..5b8765e --- /dev/null +++ b/sysdata/libraries/render/src/image/lib.hb @@ -0,0 +1,34 @@ +.{log} := @use("../../../stn/src/lib.hb"); +.{Surface} := @use("../lib.hb") +bmp := @use("bmp.hb") +qoi := @use("qoi.hb") + +BMP := 0x4D42 +QOI := 0x66696F71 +// stand-in for null until bugfix +DOES_NOT_EXIST := 1 << 32 + +get_format := fn(file: ^u8): uint { + if *@as(^u16, @bitcast(file)) == BMP { + return BMP + } else if *@as(^u32, @bitcast(file)) == QOI { + return QOI + } else { + return DOES_NOT_EXIST + } +} + +from := fn(file: ^u8): ?Surface { + format := get_format(file) + + if format == DOES_NOT_EXIST { + log.error("Could not detect image format.\0") + return null + } else if format == BMP { + return bmp.from(file) + } else if format == QOI { + return qoi.from(file) + } + + return null +} \ No newline at end of file diff --git a/sysdata/libraries/render/src/image/qoi.hb b/sysdata/libraries/render/src/image/qoi.hb new file mode 100644 index 0000000..ca6e8c1 --- /dev/null +++ b/sysdata/libraries/render/src/image/qoi.hb @@ -0,0 +1,101 @@ +.{Color, Surface, new_surface} := @use("../lib.hb"); +.{log, memory} := @use("../../../stn/src/lib.hb") + +/* source: + https://github.com/phoboslab/qoi/blob/master/qoi.h */ + +QOI_SRGB := 0 +QOI_LINEAR := 1 +QOI_OP_INDEX := 0x0 +QOI_OP_DIFF := 0x40 +QOI_OP_LUMA := 0x80 +QOI_OP_RUN := 0xC0 +QOI_OP_RGB := 0xFE +QOI_OP_RGBA := 0xFF +QOI_MASK_2 := 0xC0 +QOI_COLOR_HASH := fn(c: Color): u8 { + return (c.r * 3 + c.g * 5 + c.b * 7 + c.a * 11) % 64 +} +QOI_MAGIC := 0x716F6966 +QOI_PIXELS_MAX := 400000000 + +QuiteOkayHeader := packed struct { + magic: u32, + width: u32, + height: u32, + channels: u8, + colorspace: u8, +} + +be_to_le := fn(big: u32): u32 { + return (big & 0xFF000000) >> 24 | (big & 0xFF0000) >> 8 | (big & 0xFF00) << 8 | (big & 0xFF) << 24 +} + +from := fn(qoi: ^u8): ?Surface { + header := @as(^QuiteOkayHeader, @bitcast(qoi)) + + qoi += @sizeof(QuiteOkayHeader) + + width := be_to_le(header.width) + height := be_to_le(header.height) + + if be_to_le(header.magic) != QOI_MAGIC | width == 0 | height == 0 | header.channels < 3 | header.channels > 4 { + log.error("Invalid QOI image.\0") + return null + } + + surface := new_surface(width, height) + index := @as([Color; 64], idk) + + run := 0 + px := Color.(0, 0, 0, 255) + px_pos := 0 + + total_pixels := width * height + + loop if px_pos >= total_pixels break else { + if run > 0 { + run -= 1 + } else { + b1 := *qoi + qoi += 1 + + if b1 == QOI_OP_RGB { + px.r = *qoi + px.g = *(qoi + 1) + px.b = *(qoi + 2) + qoi += 3 + } else if b1 == QOI_OP_RGBA { + px.r = *qoi + px.g = *(qoi + 1) + px.b = *(qoi + 2) + px.a = *(qoi + 3) + qoi += 4 + } else if (b1 & QOI_MASK_2) == QOI_OP_INDEX { + px = index[b1 & 0x3F] + } else if (b1 & QOI_MASK_2) == QOI_OP_DIFF { + px.r = px.r + (b1 >> 4 & 0x3) - 2 & 0xFF + px.g = px.g + (b1 >> 2 & 0x3) - 2 & 0xFF + px.b = px.b + (b1 & 0x3) - 2 & 0xFF + } else if (b1 & QOI_MASK_2) == QOI_OP_LUMA { + b2 := *qoi + vg := (b1 & 0x3F) - 32 + + px.r = px.r + vg - 8 + (b2 >> 4 & 0xF) & 0xFF + px.g = px.g + vg & 0xFF + px.b = px.b + vg - 8 + (b2 & 0xF) & 0xFF + qoi += 1 + } else if (b1 & QOI_MASK_2) == QOI_OP_RUN { + run = b1 & 0x3F + } + + index[@inline(QOI_COLOR_HASH, px)] = px + }; + + *(surface.buf + px_pos) = px + + px_pos += 1 + } + + return surface +} \ No newline at end of file diff --git a/sysdata/libraries/render/src/lib.hb b/sysdata/libraries/render/src/lib.hb index 87e86c3..465a004 100644 --- a/sysdata/libraries/render/src/lib.hb +++ b/sysdata/libraries/render/src/lib.hb @@ -1,5 +1,5 @@ software := @use("software.hb") -image := @use("image.hb") +image := @use("image/lib.hb") text := @use("text.hb") // default mode diff --git a/sysdata/libraries/stn/src/math.hb b/sysdata/libraries/stn/src/math.hb index 9108919..9d0910d 100644 --- a/sysdata/libraries/stn/src/math.hb +++ b/sysdata/libraries/stn/src/math.hb @@ -30,30 +30,37 @@ Vec2 := fn($Expr: type): type { return struct {x: Expr, y: Expr} } -SIN_TABLE := @as([int; 91], @bitcast(@embed("./assets/sin_table"))) +/* source: + https://github.com/baker-Xie/FastMath/blob/master/include/fast_math.h */ -sin := fn(theta: int, amplitude: uint): int { - if theta < 0 { - theta += (-theta / 360 + 1) * 360 - } else if theta >= 360 { - theta -= theta / 360 * 360 - } +// ! heavily broken, possibly due to compiler bug... or skill issues - quadrant := theta / 90 - index := theta % 90 +PI := 3.14159265358979323846 - if @as(u8, @intcast(quadrant)) == @as(u8, 1) { - index = 90 - index - } +SIN_TABLE := [f32].(0.0, 0.02454122852291229, 0.04906767432741801, 0.07356456359966743, 0.0980171403295606, 0.1224106751992162, 0.1467304744553617, 0.1709618887603012, 0.1950903220161282, 0.2191012401568698, 0.2429801799032639, 0.2667127574748984, 0.2902846772544623, 0.3136817403988915, 0.3368898533922201, 0.3598950365349881, 0.3826834323650898, 0.4052413140049899, 0.4275550934302821, 0.4496113296546065, 0.4713967368259976, 0.492898192229784, 0.5141027441932217, 0.5349976198870972, 0.5555702330196022, 0.5758081914178453, 0.5956993044924334, 0.6152315905806268, 0.6343932841636455, 0.6531728429537768, 0.6715589548470183, 0.6895405447370668, 0.7071067811865475, 0.7242470829514669, 0.7409511253549591, 0.7572088465064845, 0.773010453362737, 0.7883464276266062, 0.8032075314806448, 0.8175848131515837, 0.8314696123025452, 0.844853565249707, 0.8577286100002721, 0.8700869911087113, 0.8819212643483549, 0.8932243011955153, 0.9039892931234433, 0.9142097557035307, 0.9238795325112867, 0.9329927988347388, 0.9415440651830208, 0.9495281805930367, 0.9569403357322089, 0.9637760657954398, 0.970031253194544, 0.9757021300385286, 0.9807852804032304, 0.9852776423889412, 0.989176509964781, 0.99247953459871, 0.9951847266721968, 0.9972904566786902, 0.9987954562051724, 0.9996988186962042, 1.0, 0.9996988186962042, 0.9987954562051724, 0.9972904566786902, 0.9951847266721969, 0.99247953459871, 0.989176509964781, 0.9852776423889412, 0.9807852804032304, 0.9757021300385286, 0.970031253194544, 0.9637760657954398, 0.9569403357322089, 0.9495281805930367, 0.9415440651830208, 0.9329927988347388, 0.9238795325112867, 0.9142097557035307, 0.9039892931234434, 0.8932243011955152, 0.881921264348355, 0.8700869911087115, 0.8577286100002721, 0.8448535652497072, 0.8314696123025455, 0.8175848131515837, 0.8032075314806449, 0.7883464276266063, 0.7730104533627371, 0.7572088465064847, 0.740951125354959, 0.7242470829514669, 0.7071067811865476, 0.6895405447370671, 0.6715589548470186, 0.6531728429537766, 0.6343932841636455, 0.6152315905806269, 0.5956993044924335, 0.5758081914178454, 0.5555702330196022, 0.5349976198870972, 0.5141027441932218, 0.4928981922297841, 0.4713967368259979, 0.4496113296546069, 0.427555093430282, 0.4052413140049899, 0.3826834323650899, 0.3598950365349883, 0.3368898533922203, 0.3136817403988914, 0.2902846772544624, 0.2667127574748985, 0.2429801799032641, 0.21910124015687, 0.1950903220161286, 0.1709618887603012, 0.1467304744553618, 0.1224106751992163, 0.09801714032956083, 0.07356456359966773, 0.04906767432741797, 0.02454122852291233, 0.0, -0.02454122852291208, -0.04906767432741772, -0.0735645635996675, -0.09801714032956059, -0.1224106751992161, -0.1467304744553616, -0.170961888760301, -0.1950903220161284, -0.2191012401568698, -0.2429801799032638, -0.2667127574748983, -0.2902846772544621, -0.3136817403988912, -0.3368898533922201, -0.3598950365349881, -0.3826834323650897, -0.4052413140049897, -0.4275550934302818, -0.4496113296546067, -0.4713967368259976, -0.4928981922297839, -0.5141027441932216, -0.5349976198870969, -0.555570233019602, -0.5758081914178453, -0.5956993044924332, -0.6152315905806267, -0.6343932841636453, -0.6531728429537765, -0.6715589548470184, -0.6895405447370668, -0.7071067811865475, -0.7242470829514668, -0.7409511253549589, -0.7572088465064842, -0.7730104533627367, -0.7883464276266059, -0.8032075314806451, -0.8175848131515838, -0.8314696123025452, -0.844853565249707, -0.857728610000272, -0.8700869911087113, -0.8819212643483549, -0.8932243011955152, -0.9039892931234431, -0.9142097557035305, -0.9238795325112865, -0.932992798834739, -0.9415440651830208, -0.9495281805930367, -0.9569403357322088, -0.9637760657954398, -0.970031253194544, -0.9757021300385285, -0.9807852804032303, -0.9852776423889411, -0.9891765099647809, -0.9924795345987101, -0.9951847266721969, -0.9972904566786902, -0.9987954562051724, -0.9996988186962042, -1.0, -0.9996988186962042, -0.9987954562051724, -0.9972904566786902, -0.9951847266721969, -0.9924795345987101, -0.9891765099647809, -0.9852776423889412, -0.9807852804032304, -0.9757021300385286, -0.970031253194544, -0.96377606579544, -0.9569403357322089, -0.9495281805930368, -0.9415440651830209, -0.9329927988347391, -0.9238795325112866, -0.9142097557035306, -0.9039892931234433, -0.8932243011955153, -0.881921264348355, -0.8700869911087115, -0.8577286100002722, -0.8448535652497072, -0.8314696123025455, -0.817584813151584, -0.8032075314806453, -0.7883464276266061, -0.7730104533627369, -0.7572088465064846, -0.7409511253549591, -0.724247082951467, -0.7071067811865477, -0.6895405447370672, -0.6715589548470187, -0.6531728429537771, -0.6343932841636459, -0.6152315905806274, -0.5956993044924332, -0.5758081914178452, -0.5555702330196022, -0.5349976198870973, -0.5141027441932219, -0.4928981922297843, -0.4713967368259979, -0.449611329654607, -0.4275550934302825, -0.4052413140049904, -0.3826834323650904, -0.359895036534988, -0.33688985339222, -0.3136817403988915, -0.2902846772544625, -0.2667127574748986, -0.2429801799032642, -0.2191012401568702, -0.1950903220161287, -0.1709618887603018, -0.1467304744553624, -0.122410675199216, -0.09801714032956051, -0.07356456359966741, -0.04906767432741809, -0.02454122852291245) +TAN_TABLE := [f32].(0.0, 0.01227246237956628, 0.02454862210892544, 0.03683218099484564, 0.04912684976946725, 0.06143635258159376, 0.07376443152244928, 0.08611485119762791, 0.09849140335716425, 0.110897911595913, 0.1233382361367387, 0.1358162787093877, 0.1483359875383474, 0.1609013624534892, 0.1735164601378558, 0.1861853995275837, 0.198912367379658, 0.2117016240239833, 0.2245575093171293, 0.2374844488160702, 0.2504869601913055, 0.263569659899918, 0.2767372701404143, 0.2899946261126061, 0.3033466836073424, 0.3167985269526038, 0.330355377344334, 0.3440226015924263, 0.3578057213145241, 0.3717104226127435, 0.3857425662711212, 0.3999081985145372, 0.414213562373095, 0.4286651096994995, 0.4432695138908643, 0.4580336833706724, 0.4729647758913199, 0.4880702137228629, 0.5033576997992942, 0.5188352348999757, 0.5345111359507916, 0.5503940555372639, 0.566493002730344, 0.5828173653349761, 0.5993769336819237, 0.616181926094866, 0.6332430161775691, 0.6505713620801533, 0.6681786379192989, 0.6860770675448629, 0.7042794608650442, 0.7227992529642059, 0.7416505462720354, 0.7608481560702512, 0.7804076596539435, 0.8003454494993202, 0.8206787908286602, 0.8414258840072547, 0.8626059322567399, 0.8842392152253498, 0.906347169019147, 0.9289524733703675, 0.9520791467009252, 0.9757526499323765, 0.9999999999999999, 1.024849894150227, 1.05033284623986, 1.076481336415266, 1.103329975733476, 1.130915687498827, 1.159277907333434, 1.188458804282966, 1.218503525587976, 1.249460468133579, 1.281381580036554, 1.31432269635108, 1.34834391348672, 1.383510007652874, 1.419890903494092, 1.457562200087105, 1.496605762665489, 1.537110389861882, 1.579172567960209, 1.622897325693455, 1.668399205583507, 1.715803370795664, 1.765246870094191, 1.816880087892402, 1.870868411789389, 1.927394156630064, 1.986658792343365, 2.04888553303075, 2.11432235754864, 2.183245547884151, 2.255963851929159, 2.33282340310135, 2.414213562373095, 2.500573890994256, 2.592402517738071, 2.690266237279613, 2.794812772490477, 2.906785761665535, 3.027043204317773, 3.156580333940787, 3.296558208938321, 3.448339762033025, 3.613535681307428, 3.7940634000883, 3.992223783770083, 4.210802033502797, 4.453202224414411, 4.723629327882301, 5.027339492125846, 5.370990435003726, 5.763142005118804, 6.21498777108904, 6.741452405414988, 7.362887641324242, 8.107785803676903, 9.017302360424724, 10.15317038760884, 11.61239886143525, 13.55666924235242, 16.27700795993539, 20.35546762498714, 27.15017066569958, 40.73548387208334, 81.48324020654604, 1633123935319537.0, -81.48324020654685, -40.73548387208354, -27.15017066569967, -20.35546762498719, -16.27700795993542, -13.55666924235244, -11.61239886143527, -10.15317038760886, -9.017302360424734, -8.10778580367691, -7.362887641324249, -6.741452405414994, -6.214987771089044, -5.763142005118809, -5.37099043500373, -5.02733949212585, -4.723629327882303, -4.453202224414413, -4.2108020335028, -3.992223783770084, -3.794063400088302, -3.61353568130743, -3.448339762033026, -3.296558208938323, -3.156580333940789, -3.027043204317775, -2.906785761665536, -2.794812772490478, -2.690266237279614, -2.592402517738072, -2.500573890994257, -2.414213562373095, -2.332823403101351, -2.25596385192916, -2.183245547884153, -2.114322357548642, -2.048885533030752, -1.986658792343365, -1.927394156630064, -1.870868411789389, -1.816880087892402, -1.765246870094192, -1.715803370795664, -1.668399205583508, -1.622897325693455, -1.57917256796021, -1.537110389861883, -1.49660576266549, -1.457562200087105, -1.419890903494092, -1.383510007652874, -1.34834391348672, -1.31432269635108, -1.281381580036555, -1.249460468133579, -1.218503525587977, -1.188458804282967, -1.159277907333435, -1.130915687498827, -1.103329975733476, -1.076481336415266, -1.05033284623986, -1.024849894150228, -1.0, -0.9757526499323768, -0.9520791467009256, -0.9289524733703679, -0.9063471690191476, -0.8842392152253504, -0.8626059322567398, -0.8414258840072547, -0.8206787908286604, -0.8003454494993202, -0.7804076596539438, -0.7608481560702515, -0.7416505462720356, -0.7227992529642062, -0.7042794608650446, -0.6860770675448633, -0.6681786379192988, -0.6505713620801532, -0.6332430161775691, -0.6161819260948661, -0.5993769336819238, -0.5828173653349762, -0.5664930027303442, -0.5503940555372643, -0.5345111359507919, -0.5188352348999761, -0.5033576997992947, -0.4880702137228627, -0.4729647758913199, -0.4580336833706724, -0.4432695138908644, -0.4286651096994996, -0.4142135623730952, -0.3999081985145373, -0.3857425662711215, -0.3717104226127437, -0.3578057213145244, -0.3440226015924267, -0.3303553773443338, -0.3167985269526037, -0.3033466836073424, -0.2899946261126062, -0.2767372701404144, -0.2635696598999182, -0.2504869601913056, -0.2374844488160704, -0.2245575093171296, -0.2117016240239837, -0.1989123673796584, -0.1861853995275837, -0.1735164601378557, -0.1609013624534892, -0.1483359875383475, -0.1358162787093878, -0.1233382361367388, -0.1108979115959132, -0.09849140335716448, -0.08611485119762818, -0.0737644315224496, -0.06143635258159368, -0.04912684976946721, -0.03683218099484564, -0.02454862210892548, -0.01227246237956636) +TABLE_SIZE := @as(i32, 256) - value := SIN_TABLE[@bitcast(index)] - if quadrant >= 2 { - value = -value - } - - return (value * @bitcast(amplitude) + 5000) / 10000 +sin := fn(theta: f32): f32 { + si := @fti(theta * 0.5 * @itf(TABLE_SIZE) / PI) + d := theta - @itf(si) * 2.0 * PI / @itf(TABLE_SIZE) + ci := si + TABLE_SIZE / 4 & TABLE_SIZE - 1 + si &= TABLE_SIZE - 1 + return SIN_TABLE[si] + (SIN_TABLE[ci] - 0.5 * SIN_TABLE[si] * d) * d } -cos := fn(theta: int, amplitude: uint): int { - return @inline(sin, theta + 90, amplitude) +cos := fn(theta: f32): f32 { + ci := @fti(theta * 0.5 * @itf(TABLE_SIZE) / PI) + d := theta - @itf(ci) * 2.0 * PI / @itf(TABLE_SIZE) + si := ci + TABLE_SIZE / 4 & TABLE_SIZE - 1 + ci &= TABLE_SIZE - 1 + return SIN_TABLE[si] + (SIN_TABLE[ci] - 0.5 * SIN_TABLE[si] * d) * d +} + +tan := fn(theta: f32): f32 { + a := @fti(theta * @itf(TABLE_SIZE) / PI) + d := theta - @itf(a) * PI / @itf(TABLE_SIZE) + d = d + 1.0 / 3.0 * d * d * d + a &= TABLE_SIZE - 1 + return (TAN_TABLE[a] + d) / (1.0 - TAN_TABLE[a] * d) } \ No newline at end of file diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 71a8bcb..41a5535 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -67,4 +67,18 @@ reverse := fn(s: ^u8): void { j -= 1 } return +} + +equals := fn(lhs: ^u8, rhs: ^u8): bool { + if lhs == rhs { + return true + } + i := 0 + loop if *(lhs + i) != *(rhs + i) { + return false + } else if *lhs == 0 { + return true + } else { + i += 1 + } } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/assets/mini.bmp b/sysdata/programs/render_example/src/examples/assets/mini.bmp index 7e0b4ceec3f9c4bdab3a64aa01c75826149183ac..49cfcd268931f7b1747923f1764860954a1df65c 100644 GIT binary patch literal 102454 zcmc$n1z;6dx3(vc0156=++Bk^g%)YC;O_3O#oY<+?!^fNh3n*24ORHhtC6(g-QxA>R(yy1c2-^^Mj?T^HW z`_qfY$0F(X$#&DmEiy=(=09pp8#L28{PeTdre!Pb%*hkx*-VTh_-^n6l$0%78ZTZv zLwtNJQc{vKNHPF}q_9ole_xaUNqj~mF;(K(#v?H%1o#rmZFyH5VnTfI=7}dBy4=Kl zryF?V>V&6uxAE%w8Z78r4exJ_N0{e4xLoLm4YSK))u0^MFd`prZfS|vmxti|^7`01 zrYKep$_>l$#j$!wK5QCY6g#Gt!TvdwabQ+u?4DWS9&CQMHnZhId`j?WJkb18AIe&fbKix{<8~;iY zlBJ)3*n|WWD^Vi#nq-2O$=Lt$Rjd5vD^}KO)T*O3YuH??RkgM@b<|{S`k1NOqFM8_ zHH(+$?`838n}Pj$bm=>=Te~h6Kb!AAbij1f*wNa^5tFou6J}{sCrmTQSZ-4c)TAda zpq&!`TVCR{5@_TkhQ708zhSFT)Aof{Jqh2-SK|DL~;7=B&?qC-Ey>#ie&y}Qmg zA?%3`eCO_h=MJ`bW4#W)^{;^!SB3+=+mYh8l}g|+5(6xi+_^adR>!;GjoVB_zFi0S z??9r@7Nq!C;>)LPd=KR_x%<3Rafgq5KcD+5o5|!RC9kbV;nb?SSTQ0u){kw2JExZ5 zv8N-R-E+aiN6!!t7$yWz64Q;h{~P`ise^?-(S!a$K`31GhcvrH-FL*iK_jht&DxrI zz4}^>>NT}_v!-chPaZWcW>(1EL^DG7;Tqa4F!K7kGaXCX6!z;-pz`8`@NmW)-I*{5KNZBb|c*oCWSO2pPm@3VDUkRwTbCUtx#%++HAHxB46?Hu-!I-xJFH;roGLKKE`7 z9@54y8c-iwCjAWOTUYSnfg5aWo$%)EN5U4x(@*|C=kJT^eR?X12KJ}VoSnuWub21z zhhDkF{??-o6cLsV?Aeh&tCrcPPMI2b6S5S~{TJe;&nmq3U4!4R%tWi7vLTCRBI3{2 zH~YWjuUV6pn()`UWm~Ob{U+K^ZQ5x)x>}4k$&@+myu|lgx_E&)bJ2Oa2#4Q#D_?0e z_$?fTuW4UHcOfNs2a*D~Ul^juc0#~TBnQ*xhV3)#58cQ8J7W|5w~Ig| z!EYN9{I@EGQ}~%l)U8SWTgc&dyuGv#s|QrZ#rOk*;>Kj#zh1@bKvoZL)S-g z-pV$Leo80Y{jdJM_Mf3=zmDgOJuTlW&%J)@&FiH6Ke=8>tJkhKuS4Bey;d!)j=8xu zbJkdG-1tFNO*JjHYpX^GjvR@Qs8L7=8;j_`5eN)fg!eu(v2=EKb#;_vkFQ?>aO5ynRJ`Ao7Pt8r%Z?qPwX zgc!ud&`(R{Pmepk_KIO-e(k@%;!k3b1O)iKQ|x)V*eLeyQ12<yyZ z7B&LF2}pa=P{zMIdYJ}3Z)7z)`up72^0fF>Eh_KWsph|2BgJY(;!1?RnIG zB-5tH1z95G!y0_LFaw+VmBNt~W8mlcJ081ODe-%731QEz)9hfH9@KSTvYe-!`@j49 z+JE2cKjBaQT9{_fsn|2lY@*syrAk@Xt5;8}S+lyUrkZu( zoEn^Ou1ECym5BMU3h#7%NB+L@7ZDz)*mH9+*xdAbPiIiKzt8M$;H|42BJLl;p@m(rWIzriKBc{-oeY1q z0xvEMRQZzCgLA65b<@Z~*u&h4jb&Q|IZQ)5?e1%@MYwRHJJv6&h5cJw;)%yB#b2bK zWd{DD$Y8k72F2ftyECwJT|KPtT^#Fr{eZ>8f5IEPBY5s{6E8e(;iKm{ymmT{x9{I8 z2IUMDga6C?iOzFoE~s7m_V}S>Nwoc`J%$b+iJUod8FZkgJ$w4}sd4k>O|=>|s%cxd zEH9iRyB1QfiiVdD4rk=gXCh*QW+UX|B>0ES!RzDKWDl&00?e@$`+Kd^~w8@hvYCU@P(?*OOr}gYJQ0q5vnAU&TaDxozHl<#@I$Dz^ zjWpknALt@z&xAV>lT1daSgtIX0|a9cj2|-=x2ZU#zI$$6M z^zMWH{kmYr^f4U&)SI7aZzHHf1D63W*m^C;xQS!*YPm*_#*<|^4!Fq{h}BY4r5aoqfwNh)J?!YP>0fu=|%c3iJ>;r zeH{D1o;`?#Rz?@~0^NI<@~f!u!o@>b_)r_W6We+jpQ<^A_mGYiMs{tM*IZq{Q+cNkzz? zxOLy~CwtW27wXTbaCm#U;kl@6SHw_*3Vcuv?><&FOPmbdnxJJ zqXR|^?}Kw&n&Z4>OWfL78)sLQ#`ZDAu%eF{Ec@5Q#$Hu$efBU!T{!@|2XArN@ge#R z9}6u@4s`ml1src)Q|vvky@W>|HcFD?Bqx*nKh9rb9HAwU5fSlNvSc;Nm8*)8BPZa> zRcqMVIm5%%O-YrC71VX3zy9jQ(- z9m#RC5gxD>NB1>XpOL3T;RS1E{iZFOGUu=1NrmT{$^P%tN z&s?!jW+TnLRH;HUmo1xDc<7LR+NhC(wJ}46q{=Y1!?Z^A>T2e-YHHiIY*ze<-fC(Ze?Uw`G@`;Hv2*(#nAdHHNfV~R>)}(R2z$C?%=YWpmDvy7W8rHO zV)5Dg4IVMC=f${I*b{k7eJ76}(iV$6aI;bJlrf3OGshcv>Tn%TJx;^XY6r#*X@^BK z`(e%8*-7;)lnbd?=O+ufO&M)C|^gNagodAm-gV4E4Uv%wh zfzB4)FtSq%jO+R%#&_v}`Gfmn(bR?5ymTYBAGnF%t-Ns2BLY?r;&9VF443Ww;OOxQ zGv@C=W~1B~KXx|mIeEgv&J}lDtnq*hdcX3bqKZo6FP`p($UoJ8U;E9_hiTpl@rz=r zXDW}cbm{7tIc+Y-zcZZd9N}#51iM?;m6Rx6NU_7*@~clDUYqvs`l~judzXe1cl2mm z8y|gVDv1b~3z3AtX-bmAW+Nqh4&FT7jQ%}x%V)$68#UB?<-B>?s`>N(mcJ3BCTcEj z_f0jUEcf_g+RXB3XkH(+YE(hq+_dG4XN-+CpHd}@47}s%Y@D9IQG^4Q}%?l_#l zkglE4u}LGvnZw-=*mBGXJI{Dv@A-Q;a_tGuUB8QC7o6d6@C;Uunu;Dh2cv7Z{))eT zg9qZ&#wBn)cM$f+4#4f~aoAqG4VUXqIC0?)E?Pgu1@~~+Jo}>fyWtvw`%lAR=lTKj z=B#A@m|^dhU2r1@!k@dN74Es)BRb6Qf1JOF2w=g2wWwOP0dnX5LGkBc>!SD*2JgB% z!|L*R6v&?wnT*u6yB8kf$GGE*md7O1kpi@*=k~2;u6jQF0_Q3A((@PTOCK;`DxTb1 zjcVo9`(K^@{Zy_MbLVQyX3zdx{$@`dsU178wX~K^3n|;G0=9j;fD_RUSQT%D;ZL@r zqOr@ak-&HpXboy4tMIi2Of5uf4d3C>*wLc+}|U6<~*FQz}fK< ztQ;LMcIFI}uhkW&t~|lAm0K`$#ANjC(bHgGyLLlQN|#^zD(U+3Scr7)HozdgdZII> zclSPgT`x@S*jvfaPW>SAYxfbb=razJRvy49`xmhG^vBIR0dT$NujG!$bBr4^3$-d# zhm*BE?z!D!41bejlzGa>_mIT-G5L#4Oh&xKzx+RgsAs?bN7sM4#gKfC{4HO91Qlzx z#qrbDC|j`sDwnAQhnx2BI zqLI0V`<~13iScew=p01)|E44{Y$9W@iAV{W!+hv09NIMpMe=AUWM*cGxHxUef*IPJ z>Er*FzbWGeX@~Y~EH3<&Zcz>gf~|2X)(I4VbrWvJ=V?1756;uDqX*L!6IbMluhxs zdEFKy#U|s~%Wy1Ox*OH%w8peKtMM@~N--GCei!~EUU~WKIqo<+;2vuUguV3q>2a>k zpUlC@Jjet3e2?h+J>_+M?rM(*_h_H*+hEzIjmZ5&G3-9(g4LE+F?iTSbhQ|SUfsGY z28F%)pJUOgZ4a1bHpQXc z2l0^e;=-Wg?durOtD8Z;@Y=O#-f(6Aem%6(`E$?B#>ew!(e}REg~*si2#uKk9e=_e z!WPi4Er%S&TGG!=o3>dzV}`bP{&XdO2jHAZGd0=)Q!R()u9;~Vcyc}t1=?aw%vEej zxs9{&w{bf5CN{erfL7HAT6QhA+nD~f`wtqV4IME;t5Ck8){t|Ck~8x4^)=0tC(kR< zfg;An#@K1OBi8n&4Q^h&j)J-KC1vA0P2I}15aJ)ox&h$5AMpBP3eH>IMdQ}J(d4J@ zxaIa3q3nDSiCtWsXs5{EefHV+{2Bbd`}Flhob9f|;r3NE-zs_D!#j6l`Md?_-TP-W zZd?Vq^OzuKuFS}ptpKW&uZ{lQJECvb_UPQXC%Sa$rCQk@V9`lQN9N8&x?A)y2>rkA z;sXwDjb8?~M8~dumHg7JH$*y+-(CX;@jZP|w^=I{b7)3}IY=3s`SWE(v7))qym2`c zE2Ks2-@PHpnGCu**(m-zJUr;HMJa|<`tKj|C#;GHdlEmtCdYN_*T=+pJ8=JX9A0@x zV9vr#YV7yx(VM=AE$%togwrh>lrB5X3v=3<@AZ;xe6H@JpvRvxFBwr<$Sk&KClsd?(U zb?Rvi8#dI+moKmN?%jKV^s_FUkHZ}|Pqb>%99fNwk(II3(cOnQZx({^pa{j^^LGh& z?i~Y1&-WNQaW0BiHpiS58{kVr%o-ZSpV(eu@bCC@!x7Z<#I`4S09tkp^&QU8!X zNgW7(lHZGnj)q0AUKluJ2=2Uy#>2M>@ctBw%hx@SD_eFHD^M67HdeUH-2CD>^VBir zC}_$Ykrr{+!@YR(CXFrfaxQM9MOOreuchu>%wzct5m9r}_=^i4ZQw6#DfTTZq-snm zlqfQD{j53Kn&~sOg}+VJmaq7$_}9Q+&;GqM5mo|eg^Cv*%F?UYEIG2G*MQ!zqHf+B z#&yF#Y^`WXi*N-7MNJ#HDD%JNM9Pkv*BO=2+8e)vc?YJAbh>`#BmrPmRLY}>iBxXc?jZq`iu`PUz{+6}5}lL}bM$?*o zhW%=xjqKV>@h1!ldm@cF7svU;%_hcKV8*(#EYvf`spGzQzM?pPOKR5cOECejT}1MiNMk8uN8kkHRynK^EblX{RUiJt-j+=#<0k(tLIRubP>g#vBYt_ zp8u9VVW&!!D#%PGbY6thj3VqB_?6dW%4C@95udkDpFU~a{Zsz3@-u`-6Mm*Jm@Qit zEMLA9UQeGQCZ5SDnYR@F)cmC=!;F8DqE4Xl6aKz1U+?tt3CcIAj}3?HVEZUsu_p{Z zc^iS}@1rrO|1jp6%`j@{Ff5!i7x}VF-i5k?*LB#Wp#z5v9@3EK72CRQQM|r;B?EuL zpzs&=d75G`HgpV@uUe)0$}~rgU9|gJ^H#02rY%}%bsN>x%$wBKN>$0PRj5@|t6aO3 zmV#);)lgEQd`+!vnQEFv-yYhCaU&Ijq6hm8>91|vwyCsH4x@EErdNzPl30vtxSnkD zV27Af|OHLNf*&Pp4YiG7?Its1vfwQn+B zKl(+&$3F^hsQ(_kjf1sUl#;8i{;+uv0VfCM;;y|$|A~8%r)*aYpRyQN>>skuz*fm? z>OGMMj<(FnIAYQ4X%K%|@;}U58uTCglv+N6r042e{ipM%zebPc^~ZnJnwgo=f9&$N z{K@fsE5e`b(?7pt$>MNua6mNeXqx^@(}CafCwnA~8XOgkUk8oFfQfV9@g@n*&tnyT zcV9&--S_%aJPsc^qj)P;uuxL*!bPZi4dX{MG4{S{Ym+NqKGOpQ3S`H@(~a;sd=UbJ zW-18{nMD0J31NOy5c+vCqCfqH*w2emqjqij7DllH1`lr7pmAfZHsgSXO`2$lDM^}} zr+e{@TQ>}B+Ommdp`wMYS*t4fW%uGG$~rYLZ*{g$uVGV!H(^lppzzmcP+!GjJmR$j zrw`<6-l0jGGBrw0DN-u$(%KEHbUJm+*8CRSeoa z0%onIj<3`ZP22bRj=zUacAO7!MX$~sl&>f3DgMZwjOl;NU-jzM4ab`;Tbgdux%jhI z^kp`_Ue|f)+i&0VEqmgh%0AJ5rY5Y};4zC1+_Pto@&lztKcf!J$e*qeV`zx4+`NU_ zt$)F-yU&&FweyO|z~39+1ibeSpk2zJf%hyXS<$|Gx0S26ZpoiHv*ur-L;*Z`H<7j4 z3p4N+6gZywKhb^D6?@O!hU#O+_SD(4#+*~u!XhJ$*Q{UPu0rLCmyC=vMaVT!lG)(< ze(ev#W8MGLTl#%U2lnbuF8lP+Mt3pD@UDF{k%1N#N_umCu%|^Atx?mas!psj^MK}! znrJm@*Vim}?CoN#nIy?~iR~2|+`Pq)@DB*ZC+4-@L`LJf_iMDDGJ(0p!q9S=L8Bk8 znemuRX$$h@#DGo~c;a*ihaE$d>^l7f)tZflxp@a{-h5ounZELJ=RATf9=O=Uyk-@t zJ>ytRmDKY#*h<}I@;M%pi0=P&7k@3C)T&i0!)wypUj4n&irrOqn)W$!7Wx`Fb22~J z?K=k3zel!BOz7{)F{U1e?hmHtQl9JGyC;1PSHwj#H_t!4h9e_=w7ayuVy69>M`+Q! z7glV#ru-L&Cs7do?lQl1|8*pue~83ezj%)QXtZkG3E8u<&r^>flTqf_^ES6C%v-an zc;+lxNX5B_ry@NowGKTPKVaLznnIO{FuB069mVxAtv{Qeb?#ne>d>^{v~ zwbG8CI#ruB4OW><4A)+xzprUhUnBWA#y9jk4c{p`SHuK5cY6D8_!9=}HE5t!tx-d( z$sAx6>OhaXkBomPSdpNszLVT6CQ8CzY?mSF5zoJ+vS`ZzlO+wf0 z>(OKPX4D@s7+S?Lj8zyP6f28aI~{S#BbsA64kvH;@cNEH<;u-rxsEmMcbF3-e-76# zqj26FO4t2w@+U0n*DS3ihepgZ<;;@@zcO}7ulrdK>eXt4$`$LPSdsF~MHuS7diU$2 zrI$Ya`)UIQ57c_Gwdm7J`{kE0O1gF#qKWjj=w%SHC;U~cQbpPPs?}<1wW#}sjvj9z z$6#bEywhJK-Lz0_~qiqrUS8 z6zDLM@2>>YlGX5=@341WA~=bsio z7<8ci{C}eV(oUT_uQbV)ElJ~8)B9Ii?nCERQ?O+E zQCvEC2P69Z#@MF-^+aC2PUgvske)r+{d z3YW(sOK04A0NnEU!uk9d%-eR{z+kt|IJJKda+pxhr2Ag|7{1!iuh(3j`)~OZ_Jper zzx<-?ckh1v*q8mj(|_OU!>*inmi4D%hvhhQ=JZzfsra4k`Lkzm*Ugo@NmiMEDh_6w zjz6)#>(*^WAB%->wDIE{b24r_CE)h`a5z7U$HUj*c=;&-AE^621*fQ5gwW7LlqpqS zeZLWHWS$&_yyB6d)o<58J9+ZBasK>y7Zf+rk{bMAj8l8e;ro0lBEv@^CTc8#y=N)u zSULyFvCf^xtJe9*aqE&hUq00*XV0IiWMtr%t2`7*p@0=R^|01w^yF~sxr4zgE2-m=KG}XS>`?3{!mtHUf>6< zc*)YN8=Ng;hdMbc?I-+@F_46pM$wp zD?--2#bWrV@n6rA`fl&B9p>e#m()s>EUKM2dBiw-9&L(gE-fTmE)8WW6~Lg8U9j)y z1{^xMh57YXFk%irld0w{@m*f!n_8$9MlCpjjVFPvXKAlaxuR(!)<0@8@0y=N z?qoj5*x<_ud%|4}`f#!>?8$b8%9WL0Cpo>ce7+XfqExHRd``IvT2-#kYs^@qCjC9( zuW_Ej8guo=T+7Fp*U)$QIIc=&&kq}WD^#pfir;IB_C1DR%-eX3dL4_J^ygZVH({?i z?W(X>=N-`OJ<#xVGWxzv#K-`^l52~!Lafa#Q3Z!+H?BU2#97w>Ty}`U4Yvffp0P#C zw)}k2=knb$uH+p5clZ-#gk5P16exfety`;FVqvXE@7`(beaoQEqU31mb6!kxJHqm} z{K>i*;ZO9Ts^7SG389hCkdh<``y^G9Dhx(2-xtTnD|XyQ&r$1PeSRPPf;sci%I7oc`6Z(v$80C+zxHIU1?N6Qnsd7e8Eowx zPu-Xd3*T^beB*~fYj#rql}7HmKjPx!Pq_Uc6qlTRaN9W$ZVzJc_{mF@OtmG3^GfAU z#aE0C*eW42IyZ9O|Ec~n^k1reRDDTbjj=Cng^E9P?*2^wb?n#?g$fyJ?NkiSYmuHo z;Z7KocH+44@N~O@_iyhj_Go|+!&J0LB!juYm{`>LaWr;%a1QwJfCIR$|aAKsZ?T0CKK(5vCPG?mQ4DjNRckg89l<`^NHAV zG71~d24LsaNaU^42U_k1sNQZSdQ9ApsVkf@cHwRG9&-^t_c;XNu11yWioNx#*BP7F zu2-6$H<0reR&2eQyNTd@TZGIF@^MJ!>_)H3RxNEca@_DKR=2KJsL12xacg;hD0WGs z&&=0Q_hhC{Hb(PG%uQy^k49~~qV*>~v=0hJ`4=Im{yqjjlDRgo$Q`96`>C0?fx)&P zlN5io9PUCJPTQM_@l(zpFk#anTzcZG>O*B-$&Taw%;gs-Rk{Ll(3e))yf> z@pJp`)Js`(Um$7ds1Z}zwfk{t)259#H*MC?vYvS@%ev+@Hh1mbY3`Ec^ZK0m{b+^w zFVV&hZZ^4c=E>r3=<}S+M>J6Eoj&~@mWP6|_f$02pZ3FVdtahd%TXAz_$0QUe}^3x zys`P14|bgk!oiF2O6u2P-h_IOwNXUPs1xTYUQv?toPoqJ%9aUNl0(cGc~cW?PeOEuUC4WeNJ+ zk5OoH^eXb?6+bK=a#U!E*_+SclG8g_xr^R=1K0bXarEd7T^K_`CI_l7<*=r8&e-|@oS{nvA(eN?X}C(JV#_RDrUVXg}z(ogL>b^7k}(!W-= zg}GEw{G~sh^xIjDO;9L*0oYt)t-iZ^s{J+a7oGTp)9+yJY&_QPV-3}lSoTc>E;s~| z#VE!ead3T+h)3*K|Byr_fow(mgW?e!#yWq-ExUG|;Q5*G-Y7DDrH|zL8S-*jGUcG% zFytw|9#`gAlkB;@au+Om#4NkyRiqy)VbOLw9JmmN-De`P^;oo$4Hx_%GGXIGEI;9o zt(TIq@%MNv+aHHDM^muj7_jy*P`73st!hR3d{wF&bKRO{Du1y$fBJWu<<5yL*$p`r zeV=Njt|PN?tk_(`vCA7U zOeQfuVNx7^qq(t!+*!0%qK}c zFXK7#vs3xYM*Wpu^3#@zjuZR)NBZwC_{-oA=-;Q?UTJ0hbnC{Aar5$J_vV*YZ6{)}>r5hc+z7|+ zTTxhW;5}BI4#I|WaaeaI25XOfF$me)c$~F&&tI9YShC!rNP)t;vKeP}Pveg^lzDNE z0nsf5c|652W6D~rSh@?pbQsLq{IcAimpM%KlR8G`dz6k$y>=z?p~b;%Xh!?mg7&rJ zC!h=CiAt2R+-@PhAEg@gUgh^uSkLtl#;pe+XQnc!P^>XlY`lQmE}!YkgyNc`FRUGX zl&o31k2QQ{QMXzHWH*)^xAcRsC){V?kMl2?DDs^Cyu9YCi2mzL8<}3((LZa{yg4|B zjy9}sY4zieD!%xc`+s7sOP6kpYtxA^C$TMaV1F2Y{9FEX|4+BYnR$L4TXal%_lAiA z@)t?n7Z#U<*>ksJ_1Y7#^6*za$yJwdTyYM=4d&`^-XVW4GvbcE785Av69VJTAujE)Yk@2Z)uJj2-#%J0XmU9yMcl>qh)e9oR6?6KA zoH8HFw#2sjqsI(@_yS6>K!B7ekRAa!1F=oiHwZ+D;=6!t=b$i~{e)N{FDYFaXB!h|Iu7tYw@ z%6Vtn{X4jI#RC`5+`_sQJD6V_3iDcA-)1biW6_1VQKV3L%-(r?)1`HU-`oBR+I(P2JdelBjx^^AFnm*Y-fYMvlNlUG?Y+IyCcRudM+SMLi z3~NJVeaL{`16eaMK7-V0F%;$N^gzB+ZIHDP@e&5!8Coo|065PD!4NuP?C0E@dq00^Gzh}?l@R4IaI81D=K|;clc&`lV zK;{5-dHdX-*G>Az40GpfN!oWf4kDY5$6@t>a4ezT64`VzRLRnX*HF9Ka2{(}zNZM< zw4Z?54f>&M-Nu-{Y%LC7x`C-&=hP`O~(cSC1*EUiBw(UyiYCMO1A$ z2?wwGV8_)k#o+#n9>`O!S%l1gN&So=vUT}-V~$yaotL_& z=CqfaH*dyozx{@~b?c&S+qM`qXb>h(o{R+!^w@Yi>Ei-?ohWr_<(y1 z5ZA8XhY_P@v2JuYS?j0R69#quI(F=ZU%3`Z*pq$2oGwx`-7}pCi+y|aQw)w6JQ5er zUQ+s7e2DaN+anma+=G}a3FNvcFRWj?3%`>8Vnr$TQ~{+bbb$K{Z&1a8AtadQ!JcF8=sk3WVsGIgZ&)6S z!}|T4*E$)9?fbkjdg^BM9ljJpmtVr7BM-3QVjx-dW2_RZWbugvh#Wi;fiu4+)Ao6x zZqwzc*Ln-K9ty|y)5(mb<8b(z2XdCFk6i4h5;-`2^X0+FTh^%Jn}n*Lla zT8;OMs6ze9wF-<)v`nQ@y=h-q9(Kod7y8{?<0!d*$-T;&TWhyaweJ$wL0-KRg==@C zmB{vGuFaMG7u~{GSA8FsFWa)_b2Ekxov2t7-gMn3%=KX2yicDIO8WL4iGKY?p@09; zO1chSh@K;sV!-${7&3VS#?9S{`D;(9F=OrinYJ-P$u+ldh)8U3Q}T4IvAs?me~afS zxnsNgQ8;P!8lz|JMFsP|9P3qip0q#oDe~kkgl1efSfoTboW5w2q-DvE*1z*05_j3ZarHG! zo3I-{^&E$R)N%8---PAwUW|Riv20HO&(oaAyB4U`eLa?+{D7s5pGAHnTOvnK#9-gS zP_Ao@$DX6XSht&b#p5y9bTk&L_l05A-V5xf0*bx5WvG+OmB9k9hl;<-pAx_0uO{ar z>ofm6*!nfUw;nR*sla^YZCrg8jBC$BaOq(PPP;SSAbWd`xT8_M#?%2~izIKx)}%OU zbIs0n!-sEp-{4Of#Pw+u93O?L zW4Y=I5@sgr(98;b)fL~^xhke zXX#&KOqg{$1Aj}8C1UZBcx*l$toW;2Z!p@nn+oe|_qcBTG1jvd&hx&HETvF$gG}ov z4#VhY+B?5Qxe^`GzRhGDx#WuTHeTpDZWD&iKZG6BX_jXa_?@(wN(!QB>%Q23))&XG zgmCVJxtz1fSjjk9Wc86`C6*@>nLh+}o{Yn0>cM8e%G{nQ=8d0=mbK}d6)B5lk6xoE zbDY0$U3ptxcage2w8xZ|KFkGv^hbX4wmj#2m^5xH9Bn@vun6+xGK} zGa53+keEV_(OBuf6`N1O;jRyyyuxAsAcXTDL3sTp3ZHzFIo?^{!8(TY62d$~dWoRl zEy7zdWRTDZ#+BSBCC3pFDXLDy&o7Gi$&`F8{U_FqgoiVC!G0By*NEt!BkvdS^^eIQ zZcpOi$T>sd>V_NV$L>a`ZQ)Mzo-V?th^{Afy?Ok17xWy+S|Fn$>;tjM;`8PtgE^2t zZz){9`Vh{pA92|&T8Y)2c!<1s6^j6l^Yr@fk64Tg<9%kxVA(qK`MGAM>96y#3 zI2wah`@^vGC~e>2L~J=1h;b{NP?u}9pMFe0EboUR`p!Tbx01P;UevQGcyQw`f}aK; z>Pry$49AO@Wc1`c80K%|HU1qc&u+NB%mZaAhOx6RVc(fgoIj3N41UYsR@&sOGQuV~TpLFYE9z9Nhxr|B$~-C(f}y^P+sao^Y`7<@#V= z6V9ti9Cw4czEShH!z_DwepWuNlgx!|T(dfA>~n9Ke_w6e8g zcSY~%I`Gf6z6AZygF*HPG`NK7ST(iA;=PsfvIk+|p*iSth3ioI)|ad^o! zmqF~8zvEBXiwa7_q=n;oP3XfHYtQ+-=de7T#JnD_@qsujJC=ftoX_Yq>KK0MI|ZNF z7YQuF5eEN?Ke@*vizL)qvqV-*M00i2d!l;A?LJ!dJ&UzuyhPs2YOYrFxiHbJ^Ey}_ z|A=+0Us${^9#c+yQL^AvEJQXk$Fub^V*{%|wClH#eUJ&g2aQnn|EA+(KIR%F`e*I- zoyVeQ{%FNM7pd+Wqh!_#GTXH$=LBd9 zn;=iA=G5H}@bFnEd^w&ahZ+*jb?KZ0O<^)Ng46Qh)KOtEhV4-LAL&Knz4Rg+iRk?3 zZFu+>vh^hcd!qM*MbUfmI>jTuFG9X6lKUw7XY2Y;@@wfua&D6Q2%{cWxeq$%e*TGg z`!1Y58t*g1Ic#B3=TP)wdb#S%=V(){odR&xmiLgEu^DD&71=jpgR{aoYiaC1WR0`- zVdO88IxrTOTw}PdKax3T!_vcOtQ){Gx$t@2H ztvb)do>LDnqEAPZDaiB4QV1ql3!^1-I6F@HV#VPUEIp9;hy1NO>yITnJbA1Vi)Mn` z{U-?GHI@3Z#VaMI;M|*WArzm!;yeE4z4^eJ`_jl+sRAzF^~6nk#u;`IioM^a?53}m zpEg(Wpjpt3_rt4Ea5A~``&Pwva`S9aUgK^rHzB9SI=9!?{$Irqz z*Jd~mojuj^K3M7b6Q?aM_zT`fKSI&kqFeLAV*F|>*%^SPy8;z^zmbEj%qfpsWsgo= z-{i~pBvYG-PWxN__zEy*uJb4CJr4{(exqY{yhjsZ#>7wqmD^+T6N&DGB4Gsx6%)BqK7!9!ooPW zw_z{u!6AAuGfay$Lf4^-7+c%pfWs3En@gQ(T$DNeAzZur3HR>3RQ~<;?T1-UEOy06 z-6v<~&ZB9*nCPAm>M;>rU;Wco=k3qhH)J`0dtJY3(<>y12iB8w*!F?!JR3^Lr9c zr5B0oCCDf)g;V_Xu7aGgPVD2wQ3YCNKYhk;{CsU{5l(oQz^T9Dz>V z7r}}4T|Y0mN2?N!SuwYS(jZBqBvYSDeT~%3eo0Kh!Mz7irbKDQ&#Eg?Sau-{Tdzl8 zHS;gic0R(6TQQ2i#pi+*d%~a8GIX1C4mrxSVt%Tq%xx*Y?CkEO`sazNu0lAJxy8G8 zU!hyK!75K;$RjYfU7|MM!yIs~a@f7$g5vM#(+{jAxWHNxnM=#WI-c?>{uJ9MzQ2Bb z|F`&i#`*XU{GJcgM=N%nL5ZrrB2S4{$Xom;{7|tgI`mzGZbP@AaD{frTChGQOyzwj zc#hEt35ek&zJ7dv*6PQLL?@E1rHK75MB6WZoK;!ko-BKyyJvA-%JpNT2T z6ez;`dKBP2tT<04HAtC^&|*k`=CdAWZ;iFI7JL9=SVIeq+c%g z=i%C&1zSR~p7nc^)`z0|6ff2od{F##>9q_a#_u9z2LJfq@F%ZLic7?Wv*+>KyoFqE zUj}EyE5C;G3y8hGs`gPaiUj4DOGEUxd{7W9UW#g2F||#i>b#^zinT5alpo;NU+PC3z8xnv89q9Du|!kb>#1O zT%zw12;a}IIgd^Miz0t=J%h=j*kNH&{B`~`5%1MN|Hg~C1rOFuv5xApR|sq$`!ZI0 z%lV2{{M;O@L(7L#r>-jIXx7s59{-fHKYN{i{nPVjc&%al{%-Gus?ED7_6k>Qi_z2e z;k>mEuHL3UZ$+QqJ`v|{M)Q7vA258>Cm7E6LNRsTUiMpIj2oU0ub(XATFH5M=R2GClitca!CXuoHXH@B zWJQr2rE%`iRXUvfpPwb{#q)E8JrQ9~7))S&eym>{n*GSUm{BH_t~Y~rJP`8^2IIG* z;aIf)5h}KwgHbcrg5k2_PwH^~Eq`K@)n95mX51cJy5fV4XMpXOpmg85lgT)6=pkBg z?NT1{uP zKjeuUb}_I;aU=II|5g4MPTQFC&*u@0U9>xhv1-O_0y zTC7apGkvTqF-kD`&_(hm{>&@+F^(WskaK~@=xgNX{1VUA!eVfm9!bxhp7;2tto_mJzU5E5BTs8gpo@|A9dDh+$% z^woE`<;ooKH~d|?p28TNtlCE7=$ZFiYuQ?f59?$jsH1f5^j7CiZ}sn&_V4%;7IpSQ zh=&p~Dfx4m&k~WCR(!-4TvNVa(GHYg4g4<+O5n-U3Cx2oSL_7@4W|KFhse+KvEFi~ ztP?`1LX`~+@;g%ad3*svLO!MOC+vyHns-xEsR_)D-!`7c?0t-bPeyaT-ygq>JD}|E zQ_f)tf1>{)V&WhSidJKoo5tNA@yC~B_)AGhW^Icpnl)*O<)?kH>fC3n<2-PWxmRG8 zjrXgf{J#5?$|1^6OosSI!1M>!h zkvne_*xvL)EYDx(PH**o&|CfcrTr)Tg|dIsOIqHB%*b{)pARD^ALd7;K| zi)KaO?s^38-YkW$&l)5p&O&t5WOzhO!{Sp_ke&ClDql4hyzac?nsRvT`+_raA zQy&7C&Yj-s`cJph3RJ!>A~+IjmakPcty0_CYUDv|^gc6 zH=IIn!#+yI8|nG8CWF^*8sw&3GVGjLyVH3#3KeOMZJST2>!ow2w>o!v`*-}M={a6| z+4lV$pF!SwhatU)-VNY-%D@2T#>w<2A7B0^!~p6F4K14&<^8qkn+9)1WW+?iW+WaZ z%u@^w9o-2gT*KC+-cN|8{uKKg6BDf1OV6MDCh5F+b5tEuR@SdC?#3B~3U z)Va$pqK5fU6fIg4B}>*rnF^Kg@X0g9pUlVU-0AJN{P}YoP`_S%!QXaO_rlG=IX}rE zI6H^zWLL(+15`W&mY=_P{KL=$zw%OXNsiev%YIZ zukx5A-z#%<3zlC%o+3Y@K&f6>y1^MYY^di%Y*Un2-~Iw6WbY>Rp6tI)op_V2$b`xJ zkSBjDjGeOq?|Ez)d^!4adVH(*i~P>4NFw9qSl;(5lE)BEUn|g`J{{{HUOZ001(4oBFW(a?mBUp*OLkvW0DK_@eu=iv=1x5N(!V@FmU-@uE{=#0_CeQNA8Ir#=RU4s`lzS z=hxl^N2u(yveDch!lZr>b5?#d(v_RGM4k%Nv1YkA*CqF8VT~E@2a+X|Ns?)fqRj7Q zLErwo?;z(b)A}=&Kg9s!+keVmDEs;KQ|cF&I9zv*MYmDY*#|k0hxsc0)}pdWI(wp< zbpI@^mFq>3ytc@H!e3A*eQchKoBIb8EBh;Q7HoxX11xdHma)b^;7=IT`TK3jdDgAA zM(6-PHglxj!=5{gn57xPRB5_c!1@s#3YrnbTY0P}s90 zcfy{?4M!d`#fpB6h_L7M&=1ZI?x;i-DizkSb!j&Qy%~jQ-^uiyCMgMxT#Wl(L(sBS zO|BQzFn-Yze2iuO{2Ttlcz>P{PT15k=Q=FTyO*uH2!~E_PGLW5mJi2cCFg8~!5Mpf z6oZ8;)kLpR6Y)XjRmhny>kHcV8H`!W*Fe%D!sI7TVhVfm_aZ0^Rn&cnIe%!J&AUO3 zs;AVt4KgUT_o}v<^<^hc*{J%7KjKgCTWR%iuD2Olr-N+Vn<3Er>SxCxB4%Pox)|Z z_{4gv&YdSCOO6((P;DUAZS%tU>oN4}QuSXdhrCra(#s9IM8+hEY5Z;5<&J!XenPWO zL*UJE@n`x^@3${}O#lo+q-FAXLc>u2;Ls`EDEg1l+Ra?}XT}E|4PIx9#GU z$hM7LtP(r6wj3{xHZi=vHES4B#ltBDF3eBfwPXFk;U}nCs0hmC&WqcpCL!SIUc?0a z#=Hx2=3&cuFZcDVc^Hi{b-CU*TYlaT&k+&42Yh;=ibfD8)OQ~^rudWfuT$3SL)pkR9(CKzg-MZL(4gf^Oqh8V^;^t9z8|`wP^q3ce*P1D>05~e z@^cwJV8iaCFv)9(JNbJ&M_xR*Yuv9}=eg9&srN3*%C)S;;lf&BAJ&TM{ixgD^z8i; z{(`ChTsUue>Goro6|AoGzuar>=piROei+5y1&PB$)`m(AjHhcT9^;N7L7n_&P?)@EqGS@10_u|Dz^jBJBGw$^L>G`v_ zkAt(FK^)nNINQd<_C_RL-D8fB`SOjt*Lq&+lJ@5Oy*8J12=bl*-@vzDm7UfYLpvUO4Z=HUSHN}24nrcB*mccx8qC- z7F)WZVaMN4wcc3dFWw#HYYfM#&8`?W{xHf`8;Zfql`h+U8cPXnw;@wtl#}b(OgRUp znfNSRxxT{CK0P&jNir^4u!#EIO7Z<_*K4JGKgmDoefceSX^+|8-|9Sh9Fi)p--7jN z{^-$fIKPu~Ls|G6H}xvR-HrR5=+o1tq!-=a(OYNMwJ9N=&Xi|wIWNF#>d$K{vG}+8 z?)%4lE$=DQs`+RX;CybQ=5w%q%UxLR`he|wd~o7?7_5Xp=2))T@b_ZKTm}){mrnm( zw)%`x<+!fALK8gVdggEAQ>iuJ?G064PvQxtOmX)i;J~_oTh6>^2=$)iLa(_6vEDC` zH3bHndXqL)$NP?m*rbXBV{{voB=yWbg_j~z2fi3c+Ww&-(u?P8JxG7iwV8zpgiyGvUtTh6*~xbQLHEO zXORb8IRsD#+PmG8d(CrBPeb8KZD@mUa7|GxwjA(b4RSKJ9ZA6EBas*~%@M12u_k59 z2W(^QvWUJ8xWmwG5HTq%{;AZJ$T}Pom{+N>Zt@8Jwp;funybyt($s(S`@}!ZjzL2w z;qlWzRX_IM^5=0U1V@g!s`~Eid8Kxq_Zgl%0*(%}MYJ>N*^_OZJH2&qj(`&CMhA-f zgK#|I{b4@P7XK6eL~rk8Vm*JJCMe2#C_M0rK zQP`+hh5JV>91tCos;=Wd;*a;c-1CYPM_$tBaiGjy1>a9X>wd#I%ikWox(}uHT@~^T z9C)rV_6inW8)r2>GvBLu_}s(~C=1Q!{4ecV7J|qjzWMMy?lY#7{LTEoH2X=Bhy5jIQ~EWAAxf-Rb-n zySDohZXNW(gEJv`Vsae`&dGRsIUVtV)KMc>Ehv;ekameVi&!|hYhqymZh8Wc1&zN%*t(p!&v&Iwn8r*U6%suEECE&Ed zbK;P5fyCc2gZrE{k)AK}+ zGX{4xiN=LpmteWY4xZa=;I;icuI)UF>$}ducZ1DeELhhbL0jz*xaA@OHrv8>>m^*; zdj+mWkqEMV@V~-eDEB9J97b*?cV&!Vu95xtICm!KD*TnaID^;phaa=HA}wwy%2%m{ zrmZ^QMcPYZtw3Qd4)Y& zhkUF&dKX(vlm5isF$d106N`eseHXH*&%xbyM*>(^me{Wr>XB#Hw0z}aD}CesHOp1X z(UhsebG{Z%8eU_+vj9=NcEab(=dSVRxf!M0UFMRKPbw>VgIt<6FF^sDFEm+oE;ahd zL~6eUW^f0~cjz~KA}%{$N0o9dFto=4MEPYP)H9Dc+k5&n#zY~+STJXB0tI7?k3KWs zFrHU5FZvy^D}3(5K1vX44`OML^HG?=K8MuSSmVeg*EXl{4dZ9l$+JG7(~y~{*?us3 z57~l~ruU(LCKYE+zfkx)V_5olN@c`oAk>b|}enwL8`tXD~+nsD=x;#MO9w&s7M-#Ti39cJ2!h5?5&hBu8 zo!(`fJ>diEQ+{wi9bRhsp>U@;o$`X+F?XChXve)SHgMf~L0Ocp10ptG#MM2QkYpK! zSl=Yv4@lwsW+uYJGFUrL<2mtynoRfAR;%2B*Y-Kz}G z4zc)f2rCyXR$LjAW46d+ekY%;`0#8N4aBr_O4>Z{r=M2y_f}*5xdZiM&U5}S6P8Y| zvD2g&OSeD8p0iLjYg4;?m|P@p!R`%q*}sLpU7@mGy{GfPZ%WM>)>{~(R@IctS99Y$ zm%9%b&R%C8!Xn?{R%{BMkq?nxm_wbfToqGGT=P%(u7TvH3tsu(Xz`bHKNByX zQ`7Df{un|-^m#~6fsLIns<$14ip~2#N7oxi3}XJopI}alKkZqZ3!auL|Q}{b*O?{OWQL0xbJrlv7nSFu6-sku` zc*K#fs~M^=r%^^Mmugh7*TUH{?`!0H6FU){LsNGBh3Yp-e5d&A^e^ILv^Z3;AJ5>o zpDdMgXz}D|gmAWN_}Jf3r%HF+;x##`@5R5<0-IOqQ?oe(PcJ1R$@MWZZ0;l3JQ@$^ z2V#x{;_i{)zgV34Z9HY{@X$LIi9W2?24^V?qt?z<)-OVDrz7?$YcqxKk@n^za_FD1>`F zXPvnS;fx>ruNA;Aoc9vB4le9<1p2>6P(U$)SnE^9`z(+?ARvUfN>C=X)EI+YO@sY~ zDDKkpf&DHgT-@P6pXvbrwKlkC6oKcq@dyk`MQB(i+@f<~MeGJL4{6>u;lP&DuyiYc?(t~ymkY?(`iT7(ilJv)f^D2*5<6^LYQ|2F(X3bMF4P5(8H4NhO2}RI!n1_Z z8ZN^<&KZ>`)6Uf9O1<*cd$q?c;TgFUtk2z~CdupxD|ogy$93D=xa*bxKi;=?ymtP& zI!S2Pd?>yfy&T!p9=*2rBEt4K zE58?NbsNdV+SMEHA-VE(_B6!80!uA~k7C-ovS$!`;$o~Vi+P8WA4DH0T*-sRA!yih zDr$A+Zk?~bL&ffs(7f*~tkS!PV8(wo7Gb!;YvHlp0anbT{mpN1XHSMQ@1PX8`8@u# zYmvJSnddgGUJv^={($=t=Q#(${#(jgBtCJ$xCu>AnH;T~w?Y;Eq<))C-zWB^|I@~S z+Q&Nq{%BmGJm(W8!P+$*%MVB%oD4(iU>&tjhJ|N3j9pV;YM%jf$E+e7x2LQf#-dWI zw#xU(%tg`zo)u^HxWA(g)ZW_`}yJ7LTYM_?q|T^R#zrcA{OIanw^8 ziXY~iK;MEjDCTR&j;H)b{0Zj71dHMa<+-ipb80~qbEi4aU}Dfa^d0wBXQF$@@mRWG z2g3L8yxnY$kX??*bxA;KSOIca>kEx|t77T@EB-Fg_X++sA7st49p|1pkwZ!=+hsh; zb)CT6b27#(+R2(=8h5AjnvoML7_`;%P#Cm68^JUEIl`jIH)lLJbJhff!JlU>M6(+8 z(TF(f!hPZ!Hdf&M*pAva{ZO5C=uVwmki(j&<_pFD{crL2>RA#_?buE%wZJbM%(2Nh z9(os3slA({W;PeBzld)pZZE3eZfSAN77L3y_xl;k*IT3enDNk5mzkdm*mu|z?qTG* zg>yETuiJ^f`$}XncXyZI_BG}%M+4zO{%an4Y1$d@oVUErwA{D6PnajM)P*}qoD08S z0-Zk$(4tH;oLqk#m-idPW!D*)?yufoXEq;W7M2GkyC1hPl+$q|Gw~jpJPz`pxK!O*qA-z%*RK#F((f9WW9}Myg(0L zThGIT7Ix?|UPNn+dOBKoxg?yP&GOzYo#*fSH8-mWsfXFstpYQfNa z8LR9%?7|({gHXTqFf?t=IVj#s4cm=J;|>$ivilDhKYJVF^ekom%vn4yKl{@U`sg|# z&hR??1CkNNI;qUwNX^&l;tgE&e1L_MmY{jH)~L(evo(A1wHSZ3s#yn)*1jlW{v`cM zmH)mXUxM~e{;%jg``Sqv?5TA@%daNkgjEo=F|K3vT3zM zcN)MtNPX^PZ^73^t+~pLc|D@x8d8Ew!NqW)Zol(w)jC5`1s33$$4lI_O2E}EE(m9h%ykcQ!{csv@t_Fb z4PAhyZ6;vMr1c7WQs*-oI~&3xh`rgJ;;QbGlrDTLJWc#&Pi2 zYz=SS3%GvF8@OA7kG%hK;>jPrQ%rsVb9DAAyx6b!-{Q}gF}FAK4c-0rsMcU0d-T0f zqsag?ZaG?6{WfD2{+e~2g54)Q8KaBVOa`K;^&%J)Kj^dN5(0O*;R??NtFz&pZ!h7! zEx1{SqnLTptCY9MNo23x*B$mIr{G~@%kQNsZvF?JyMN}t|4aO3<#MKu7@W3v zCwnCAu|($->b4z;GSoBbH)s^xY%k$SY$9_V{%&eez31O>cM3q&x}}<+JqFK0Fmp)H zNOFDRs0q#Z*N!oW%N^!=G1Tb~ErH)Xa=(s0hR*shSeV>KYBGNpW2`Lpz$Es|VxdsD zEJ=@x5gSv0@ne5Mt-39+WA{;Vb(nWjyJ7g870gB3V9(JA=76k?@EjA9`pV~~e1X;{ z{N<5(InSj4*8Y{zR|L?m2W8`|F|~*`+ar9ZD=x1#ht5~jqia4A2Xo3egynumkOQn2^LW1P06#*P)|3~48Ap5WB^r!YCqI?d6? z#Nu7Xg9(T+z6$@nm*{^jaAEUF=8w!7Sp<;%_74(SGZ4)}=KlV?f8@Prc~jaxV@MGH z{WA-?QUWm_)?v1>PE)UOZ)J5_bb?rej$Ki^O$Rh++n@Lw#2r4m@bG=X9Y&I4u{X{0 zUM!q*>K^V%jBzf*gY`x~=5_gnz)L$Wjd2vWB>B zV>tBocyph*=uwc{!94Rz{N0OvMIP=VrJpfl_D|GiO;;HFe%VIWw;N;m8Yk*VyvK#B z#jp?(@Wzb?4xvV}*sYx5I?>irspTuw(B7g+GY_jrB8m z59Pwd=p_#7J!TI$jj>)P^v{ytYo1ANQ40H*PjT4d0S=hngOxFJmcx&5_{2?OnOfAk z7vZ?p5-z(NNaXS*>Orcm{Vsz4o0I1{<~;fOfsc;Jm{<;yRnVxJ#J-lP~cZ{=!0X@!UQU zK0DkL{=&C&U$@s))GJ??J@9jQp8WE^j=z_AZ&cqdi*v=vFAJ#+A5VSOB<`9lRQeA2 z9C{GmtzeHdNTs2o%WEgRhM{vOk&oG%T6>QX63x6RhOyPvY`8E7u@1_G4eL-Iw?4p@ z&%HxFVEchE=&tdBvvn3S8D|wS-j|w8b{=aV?8QD!e2@6Jf=~NyDk=Ya!XXETsdu?v z&yL!FeK2Xh9{X0YF!s%WZ3KC5tV8&)4yH9f`nSIreV+I|?f1U)e?cLfhYddrvZTGP;?(HwMbzYNo7=rb3&kK^panVn6>G5vU)FigM+ zQ6RT9W6K9gUqe;7wIIMq}8k*z;@^|?&rw$@_FqHjqv8$}rhx1+*E~^vgS^PFT zAYiK#Y>t^zo3Rq~_ObTI8HX(97Gm0&8m<4+_Efcf%WQHh#Xg_UQ_tmlpPAJ8=^B+b z1;?S4&lD{luKQD*fY&9|EP0!cr_YPXtpRs`kcUvEHs&pIBp*9~{;~#EZO~&*9E8Kh z?)YVgEo^*~)fw!>K8Oq7uX|K!jX?SbdpC!`O8+sQJSxSU)TMLssq4@4SMYa_x@fX* zsX~>y3V+8%H;x!IbB;#aKC{qu;Ln&icOSl*ydA@TI)y{}fv|Rt{}YE=oc;O8YyF?_ z7b-PqTI{XYIDOOu-*=e-!?hNWKEB)Urs&yzIodXwizfAEpk?E^sNY~FI(JzCosCy` z_9nvQEV)FM&#+zZDu#^RhiZ){qH3cF)ZG|`DosYBa)SZ<-naO1#44QK?F`3NHaNfB z9KX!cW86NJ9Pw<0Khk>foV#A`C4RuIIP&FLdr|QMU;pR$)8bBW7)s3iyws5Ucn2%| z2?ndy?yRhG{nikx)wCxXwC;;PwiqMyW)^2cr7q1Fg!#)ApMt;PFFct2I9lvU>KCp( zk3;MBu)eFo`n5a$KjBY$UZ30N*puf4JY&;Y^q1MLtUS&^5Q{9^fGtqfuMq6z7Ud$3 zb+HXwIorWH(3rVD!iF<-#+HJ5@{NPnP0k`R54jjZZN3}i z$VFtr-tRH?t-TMu^$(Epn6WHtuYz;wi-mPgSilCvzB zdAMk0Ne-fnIgCHemw8?!^F06iuh3=G611gu#y8!k!gZwyq?do@4^y0HZRE)2vsf`} z2bwe=!5Q9fQLg?VEZb;K4#IWJnyZI~%$2Lu?1u&|2cbpBk@#i^_qfepgI&80Ku2dY z?AM=w$4*lmU1fxc-DaXwuO;O9PEu#@YtBq)>!HF=jK2Sdua`YY)>72HLk#-xxtR1e z!g){oT*<)VMJE)mNmG`&U^#MvE4D=WYOVO#6y<8Pq&{*xRIc8izTy!4f^*1!BnO(g zm@?+ofwXJHp;#cVk(lTQik900zcgGp=z${}_K}ZT6>C;+RX$J4%amG`*uV0B+V9D( zkUNjxA>v*>BIELKgO9i43vrJJHcIuJCu6`G^POK`WmpaEajj#*J+5 zH{p!th6L=@xraM9$dzC|B{-KeIE(q8;7`uqe!ZtDoDUo_mcH+`!r(!Jn^>^oEb4dT zyzTIrsMUME!e9sXqg!?V9xb|kr}Fm}thEjqzk+_z6PJ7+@$)OKPY4G875>6lhX@W! zhsos_)ctBAn$lmjr?z*=fgi@*3MOow>}OAp-_HCnv=H4jjX|dm}Cxo_ClN?x05BiYKtQ zxrfxGzvEAG;NHC^^Y>LjuRcR?(B?I^Q^#x7#vt|~9-vaY&Zyg?H=2+72@M9%r_RG9 zWzBkwL%oi}P_xwl)M@)IYPBASP6MW4-4;i7}P_QWXxYS~TS*sSi8k){Io&x&w zNVM-f5u?Wc%D&k{wc8?uTmU=P1N`=#NA6t~0QmoM&$?fUK&@_rQMr9z)Nl9=>elZ? zed&SNy4?_oDS6~wejxXVm||=p;zWTIgYw#IiLjIG55~Me-*NM)U*8W4f7|^h{oYsoLk_|NQP0QKyGG!p!N#JR;ej<<~yU^CNgaXR}}TxxFUtv>d|3 zrUHhmc(rK&X3Wp>wM++DwOR_ z{Ba&eW+6VuU-UL!wUs};&0LP*_!!NXJ`)Ywj5ll>skS)&vD-xKQ32KK=;TkIQo5n z$Gy9_92Y|#A7_{jq+s{jXE?Ye9SMxNpOLeZ@~#L^Ul-tNFfe_J5wC4|RBhf4dUlzZ zxyc8+^uwv!`vg7Ov949ID*c+F(yHvxe=geeo{4&$N1|@05vbL^A8NGigKBNMp>oU4 zYWK9JW;0Y^9w<8b+xJ-WzJG?u8=MEB4w>w^xOiRHcJwJ70sVcv(L?<$;t zfSc?~lNY7!(Mq3IYudV@J=I9Ir~8EpF8jha7Y8!+J01nMT3X%Z>$K0Y`-~?#4V;hqT{sg?`x1X;8n#3ECftirzl+k!*Hq|$ulvn{-U0SKxN||U z7t5OC^B3g4Fdmh<_)B6;i$5+ZMX;qk0=FGUy6qJd@?ObI`+y{3E0I4dy>{7+CYTfK zi3twn`ln|UBjSKPf_9M8;z&otm|Ocf{X6`X@cfZgy|3tp1%p>vKh@(s+^YQ?;($K= zbNp4{d0UlzfSQv3aUPbjcWTj={7oG_M>BuQQjNR2dvorTFOpfO+HI>~o><> z?6Ti^Z`a2Ow?a(OxlBEE?x_2OKkl+M#0OjwoBFBXh*YC{v{~%2i|Br0I^XUoC*%?qEFOIUqB_DV+5X&110) z-jAYTMv-E~gx!bl4l4xjyMV{hnJD4ED-D8F{yVYqj#w!nw%#*7eEpmnP5gc_W9z4k zyRU3AqyIaPC*e#i_&dMEAL+RelfIQ0C4trXE^&8K##`(;aEDy!MW|SLw6ZEyzea_M z-IZ>$rW|)Ga2~8&MZPcYGwDG6x05IQ5YD*+neV+C!rX_kotVsOiD|#qKKci+Zp)v) zahtfKR^U_aT}e*=h%|Df#AI)Y#Ns9<7u2k@7Ju4D?fcQz`!e|6lb_Px1ipZ?E9+vP z`MN9D*=zaiYkB^XI$&~7%b6mN&z}__F(DIrd-YM9IlOSC=lrxfpEa&x*?V-<=Do5V z&Y2LKIboq@?4Ze-`1tsm++mfXsm`;290`d@D%7Hmv*ad>i@Sfd3?{}g@C|vxUVIid zZi!HQ_5C}FuzuAGtXLg}7Bt@*n6eq;|*8h*rJGbmA=cPs|a!{$LH{$9m_WWHZWAtPm9;7?*p!QhLRAFz4%Ni=Es3w_>9 zYHN?;wH%Bu@mIc56W0D3k^eaWre;2@N2ei#IYfYO0q5QR2X9*Zg^{!IxkcV6B5&`b zihrLl$Im7XB*$X=LE&%MFvjAnZMf2hh^D6&ciP7<@ptP+F^mnaaX!w^uYacDsQEEiR(1W)<_bM&wGg$C{0%JU6qTdnlaqpHJ|H{`Pfx z5t2ABmmEt+!oU5N54UdwQqx0&-%s7e=oJp!B3hZIq%g< zX?rFmW&cb6C$(j^uhooxBOnWE6{IX4Iq1qPBk8YkcJ2ETJ!W zCui7;57g0!Q21N8Ck*3$cg7B*>sY+m4)rQm$G5%uP)nBD<8isG$m>qG& z1p{B09=-(M%~rU3(iGc=s#cPxPcV2jgqRD>#*K(P)|Xz9*Y=UxR*VTG=j44LwO5XXskt1f z{YcCzCh@=cIZB&SujIAzeH}Y~qp&ACbhLg?`zSMxg5gJr%qx#yR@n2t!a6DWpt4i$ zbL`1;tv?j}No<(*{I&9Zf<2}Hf5YDg`l9zdlY+ye(5+WL>V`K!@9{t7A2H{grbQlR z&X}c{GI^?I;*ha(Dw4Y}zqSobCO?B!PvW-w2b}8#TxBk3yy-bc9QeZH@(Fp z>JdMR|6~vOwb;{F)C}Ntk4s{|^DH`k%`>)5FO+BeSGmC_-zONX+LQdnuW5t1$7=-2 zR-1uxJm)G^)=uX%^Kx+RYbZ18`T_dwig0EG_X&?I!q{<(_@1~Yp$=>GW$KcbWmdWq zhP96uyg$=YSqtLde8~bvxvJGLaFG=zuk*n2otMyS!WQ~a?igFL3@>>9KIhL#45p0l zLv$5MJ3}TPdFvvZ*DF3bAG?m2qQ|#WnUfH6TK}i8_X&TD1GV_8QF9Y_C#^?~iU-iT zi7CqT*pFu8t>CctCY<))#)Y+paHAiL-hKp6j+!dGg;6(0@F%zv%-z{@1rH7f!+-U8 z<^K{*L-^u&mj4U?moGkrW^*bRBYPc3gBm9}bHzSq4f1_>FZ>h!g=OA%SjCWEBwhB9Cnv| zZu(4xJ^l`@=E#@$driNTPd_bn8sbdNYY!SQwx}$%)Aw&TFJ3rrzUE_bN%=~e>RvT? z51tuV0R2IQFd_cL48AQ?X40=1V!Deiu%14E?~53$-U@w3jZ)ffIczY$A_wPP)*BR) zXCXT!zMiWwbmvp5o?&?RLJ?BV7o7#snK_S%Ri{{+WdB&?( z_8B7OQi$qtrsPp$jGvfM#Valr^op6;0}FLG>z2QK|eQ z<`|RsS|>1u=*wCQ@0-fRT?N(v%hLC0txQAa!Gb^O#S??dYO21x^yZaGufBXORH@wp z^_q54^@*i_=rR_vhojGR@|YguA>;8EyoaaGU4^O@+9Dw?U!9GwhyksC$l-fnxsM!Y z*61G+Z!VUj*{FS3_TKOu}hOFKMl~|XrRFAqWG>MbT)^3Sr zUHW6-xS7;LnY=dS*M#5Ud;u*a zhP`Ue(FNWnCW$}UH7X|9OXN8q*h}V_kVca_*K^iNMYms@he=IUcBKh_UuvuplZy7c z!~ybuGIK=VUc6xT9F2vsN%OjzMmas3_QA;ksz91-e4WZE-kn( zaRztX-a)y#T`+vga`ksp`My5Tt;sC#tJDJ2uUHqgI!?xE`xs2$s*kz@r=v;t!Q@#p zAmD2faYErnXict3YVJoo&!M(F-;+G|y?O`pm+4U3s~7nuJ$c`M&FkKi*S}AxRr?lI zY7OHq^dC{X+A{8A*@oKXwxV8@ooH6)0H*wS1i`G~BZKjfTR6PPV+ucbk@K-1S^Hs4 zEuOJ9V|;!8XV`M?8uv1KbI)iLTy1aQC})dqZ$6KA?Bi$hy`}NGXQZ;XK}$(1wWmcd z@$79Lu5)f-`D$v6v|f)E4X8y{eia&5S&KTA7gOVNJ}Q@=gbHOQFcxIIT88IGnJ&Ek ztZUNuX|2qE@_%LN|7hiFHYPWQK9l~VOpPX}(x45hb7xPDW?hsu?c5JyBPagE-(MMP zbq?b_F_z66S#pw84)LTHWd5lu5FJJhjnj9iu|=JlNbWj&`~eAxCAk0O74AJ|Pk|!e z;vZuYa+OKUl)=wNFeli1%=(Lwl`Hliw;+ejK*g-*Y%h}&atHAb(^%IpDrP^fm^dsJ z9E!$hN=hy|b&~#LRs6j8M~&gB(_Pu4DeBvL5Ki>xoDMN))RX*}Ui5$8zEP&d--vc| zI8)M;JrX0>c|XMB)ly$?L~Zy$=77W$eIVC8p@?(JfS36^9DLA0??|f z7VMR)RqFqQrz&--I@IZt$L3uJpk>FQ{9Nk8)8iI3qDxdiLw3VFf0>G?d$(}W-3ezb zjj?jE1&-|w!rhx6kr2zv#B*Ejv*b_qg7;l2zo#{Yy-$8m@F(BjchrKufxOuU?NPH{ zYieqBCC{x3nzra#+~e!PMSc2@EE+s)V$sMkQ;Wa(W~kEL7X1X#;9l|bQq81sV+U7f z%saa0gumd=um|J60n`~B_#Wnc`Mk$Fbg0vX{p|YKO|1e4k2LCY#iM%bS>z24Vf>NA zxjo)%G&@IEzJJClRY!2{ll>9SoLB8V4}+HIf?*alW%xdcS2??qiARo4D!USyj0LOo z(1x$2Z0)Ypnd*e9P1>^-*s;`V&S8o)N-p8>P5hf)NMW3v zNsa&Ks;O3txGUG-z4JV7u+DOUS^>WwcEe;Ha~!?&5T?|IG>GDS9QkJR_FN<%A%U7O zfp8;_%Xj}}%+Pnj6RT_K4 zf<4Y@3;smYM6jp$sm(f}cJt1t-lPlrMV-iH`&RkC+D*GbEIs{0>AsWqg?wE(?tW=k0Fc$Vd{sF}+{ReEt!Pn5c%m~f88 z(lVtF4lG#mI~wt`s#LuZ`#t2%)^DXA>oshH`iV+}fQxk7)5X zQj0%1d$s=0vLE{x{oW`HTK1=oUaw-buHIhRfm2r*v!>!4dm+E*y7Ah4#rM*I`*UVv z+3&m1zLR`i28}u{bfI3n!YaQE%ljM7a<{tM}?}<-Qcb- zhqXp<*=U9(J8f}{HGX^6P@OnCX%_Pe7O_Q`zt;{Yyb}?@{pkkAL2%#hsPL!DUEe3~ zu#X<{5cAGZ10&)ENOS%h{$6ChAs0k+fhFc)O@M8?bo#~Hk%hQueG}U^SzzkKHE7*@ z2&z`>gz^>GgDXS4@$4;ELG%%8Dh!H7RJjTb`2A0Lhu}}nlh5!cai8*iO*^7yGvcj9 zcQo$UM`5s5ldc#$b%~nMk>2dPk8cop`#KubZ;3j!n__6U-!SGI9SrF417nr$sLa^E z0&`NG)jJjb-ZJhM48CV5FC0iYyQL?b^O7~s&+sSth2$9)*3M|rwmV;cHI%I+_?G-# z?j6bDKetM1?}g|W_3AbFZ}>aS+*eFuKl47o;a=pm5BLBp;_q08LNsBIyg#vZLjOK4 zI=#e^bL?3-y~U7e2l#iG*Hx7owftQT`|35Zepd)=F6H5X>kIT-tdB{$R=CVQku810 z{LT7Uy43{V|GXD1`_4d%KGRXP&2Us{Jxo1TYB_|x;7;oNtw#T+jCGf4V)T7tH7gHB z=eD!3X45{_v!5aTO*Z%QP#2QAT{#@~%i`I~6%b5Tfgt^0xNLPk1^(4fk|vWCTugqM*9dA`^ec;^e(s5 zh_@mEIzK1bx5hR;NF`C)_k}Dzq*D}3Olo$i87~nTLG0VPJxTCHOYyI zyVC2&`U0`2@K?T$`b^d5TBU(Hdn?fYiIuI@igi)WDp8M6W|Z20H4?qQorX#^+F|E@ zGp2dm-CoE(ZDBTN_>S{;cEaQVn{a5-TWlMagnBLX~jWxfr&(#1r^Y`&R zB`Yg=!#yJ@8Aymvzc^_h;)%%Vy*R$vO7&K%eb0cyp?fS4%y818Qr%PGm{d7I9?K6%J|lh0ik zrM0Id&u1`OV-7d+StB?b;r{3Y9AaPN0{5z&BY$S2TP%ibHpLX(vzSdE=;EKkJ@D-R zQpfl4t+TnH=MH#(=c%Ce9T_F0ZUizCT<(Cc72C=;Tyaz;jHC{ zB5c%M&)l&mrVQMSU88cbWiV&miN6CQscAJU9y-HAsLRJ*r=}q`>;BAh^$GLIkM!Gi zXx*|M9PM3-zQmts_KHc3<@Z0fp?RAg#FzLx`k=BBKM3AFH~J;!MWwrTD`DZ>nVNB< zhK;Gs9Ae6#S&a4Am+SulCcVhB>dc*_eX?+PU>(H5rEQ#;k;;u|0Cq$nR#o^?A`- zkg}$%nEf89J-(#xNnx5Tv8E;GNVcssz^PphFxp8SJ=aGFh@~z8XAGj5Xs|MeKok$ZTJxrWTh%1#HmgwpH(x&&`& z;Mps?Ox5y%mnGDE|Da4ecc$f;NY6-2eCFpSJH5oDmmrpPFBPeGp0Nk_l(XiEaPWzQ zE%}@)4w+%uej`lMGsM)xrugZ+JNAah;yT~|<+yCdB&}ey-+>o{@!wm{K=PV=KYA8p zzDZa$`V#igpNO3wQlL!Yd$BD8uA{!@C(dfsV}JRP%ANJkSTGi-&KR|7@kZUV?kTx%yv|}%W-MnM*aOuXc0jqBmB@1}L#}=KQmab+Ke73< zr)wsSA3a#`H?aM17}3{>8TVxUnHW4anCJ0`x3C0=3oB7X9ZhMcc8HIT!LRs`c!T%3Z%f zg-$)utS|Si4H%87v$jF+s5><~Uf|gy_V?KLrYxbtUfMg>9Erbotk)>~y(-0@#9!8t znV7cO1jn~uRQS{1b`ejmkrN-o{y8ydk?7eXJ>w3PQ{p#@6YL` z>=C6-7lluDk*_|-)DB|~7+}G61N_X{_>O z{#V2wv1dAnJpBQj9UAZ!XNJ%pjwr;sX17q6{j<5lzoT|AdseJnX#L(1k35L!d*mxK zbT5F|8Bg|8yb7V`^c+9x1!BdSAXr>4gyVzv*zRys<%*IwCa1kZ%>5LEUXSCREAqU8 zZgTHhB(>XL;zN1~66gya&@Yghf^FwQk(|vvRjg^Iz2@vK`!0e-u^it2Qg4l?Z}PjH zgK66>@CP~gqA%#S(h_IR_%lCwi}Q^4Z0;1`L{uh5Y&XZ^Q|`FPxgj_5&Bc7;IX^^; za(bZVF12~^dS~tyF!R7Mr$|up4^PPXeV9%CFV-wm`5L7UFFd&Sd@W+~-o&IfC6-#i zpQ9y{!zCshV=>`Oi@j&PR;+||+G4)H_c^5|K2i2tvs zeSqoETxF*wyugwsp2}~XV|^x*d7P<*ojNlYFI|V7yNvj}5sZv3vzEs*fcJ$m;!t`Y zGP|I&S!#0iI4^FCp;K6kZ9j$BV{XEDT~n?#|4s5NiEnFR$=oHHqx+7wuBfS--??5N zobFnRKZ_yn|1JI&jGBz2Tc}-pG5R0zC)g9yF^a~ZAC0lo{vO;DnSU_$T5T4fe7E>- z`HUFOemc+SLU@nMef947TOLTvm3h4b?-yekm$4spyQ_L~sy9OXrSd?~;xCgNh?l%B zu{j|&A_usZ(+il)zm)`zfd1|`pAhG z)a7fHHwyliwF-m&WZrkgpTRJmxnJP#@W>~aP{x8iLk+w=TvS|FioZ(eJ8%MSL?z?* zRh;Rp(;c3kF@M9KVDO@gAF<9R_NK=4kk?q*_zvS!4Q34(2CrR~ zxU%Og4Bc{Jr0=;+jkbhh z{BGh$4HD*w|G{2maW^-wx#R6u`0dzbOkloj6IY1m?^!#1&3lgcODFzBb6V^Le^&ZK zyImqNWt$Ov{gUCvTHckl7Ze7qeV@WVj@SO)E9?n!beui7%fv101zZ#8!^u@W=6-{kix+UhB^-D7_aD*dvXR02M{1m+ z`6H(7%RgmJGKT*UmGc@mUl!pZaZ9%=V|IAVd+;^Ceoie9ZA_TU?}_E`8i`5UGDm(l zf9GTJ+%|I$;HiMq%zG{SFg~W44lYopKj$vGX;z>{xw=TY|3byKC-hBGu6#3m(|aNw#<7mT*??Z{ ze?t4lv#4VdM9oK1Ggz!i=S`AE0dPleFXFE`4j**I4*LS^cjc_xH@>wiO*nqZIJ6WHD5tqm11AwFNvRh?6YD_+hKx@Hi3wZ zE2j2~oAQ14PlmzcCbe%l&uqfDZwme1a!Ya@+1ETzJla2e_c#3cbKYy~5jO-=qcwyY zt*30l6$b6thj)KOjjKn6u#HN=CPQcZe#Dv@I!SoSdqC=&JVyV9zbE`=z@0}}bJ`T! z827F>I7@waSGZk^MQr+O#a$*PgM7A+%BO#hKZQeLPwM+p`$=M~vF-@-an>a-B~w%9 zK_2YL;dWq;*7tTEXTIovh`&{n=g@D|!Vg_1{{?@h1K$4)fBVLVGDcj27WLX9{YegZ zdt=LveZ*5eOr7>C`LFrdw$TP(QP)$fYv-vjH4auhk&M@`6b_G>81v^_;jHB~g*`pD z_X>N0KLgg`jNH?pXG}ft^50{H_zRf# zU@pQl`P7IH*xNf7?U~C==+q5&c9|eyn^F#J~&m{Ygj{Q-Dhe8hPcZxu>^kZ$1jH9Tlzl3_yVNaK7i+XTNvzd!d16NaAKds_C^l$ zSWg*7|9vnZ5$5!L_T1&*nD9UN^XB~_cIv@fj5_sR&AV z1BWMXVaw0lDS`g<(hbbmcnTIl55W`^cj<>gPKcOxPs9V(ZXYtvI~#QuzZ+TdHPWv; zMqsAi1x($27JDs2;Kf?_L)LG^F#DFe;53U~^n3Ix7X98SGvVhfwr0E@WBkte zdCCsO8WY1s;>^ z*KHu{z#5*g5ItlG)0P`icMErQfnLpD2v*&!TnKyr`#__`0@{Q_L z-%v->QejZ1Ng#}dJ%<%vqd_P7zfp{LhZUnIbD3T>>mvEIBcgX2WA*j`?&3RwrF>l% zf%33>HP+l_AF%rqe=m7oD-04pTI|W={1Zl4Z|91D zr_4KeKdn3Dj7P-k{oL1h#(PurfYqEXKMTR%^`!UYS)ax#p1(#93gOFoUjX%^FX{*4 z8F^KX(FF>FIv#g1+w=;I$oV)+-zV5}BB@Gy{(g?XVS2W3KX?UJ8!o_|8ua0ZuE3Lg zB)@xwxWIGRo@bu(!`JX6pVq|hJ{HjT9y&+g%b4{`{G}3(iR@!VP%nM?DPwH(j)VTa z0$jNN8b|yeVbwuL%-dkhodH3J<+=7b{?fVMLR;_1=e3dfP_J8Qtl{g!BRUWMtYr%R zq8T$>W}V4unqc36@-?d0lwE|y<;zvT zH(&S0mI=ltIFr#OVHwZ-+#PH-{|Lh?=Sqon4NXwNtjf8KlzMvVOr-blufy%v~l;f?#OMRVOc z`w#44(O;v4|3kY=0-oRfrx?Nh^2w-oI31skbE$9ON}l79fEbKm&eKE30)`~$kdg?( zTp35ZmBzZvduOu0=)nAY(5~auyL^TO);k~aeV#w+3ja+Pl>W_k-Z!$C;Ao2zQ*-VqMa)Z)I(3jNWv_DBqB zd>V85x#E{0(O5Ym7JrOP#LxX>(6;Sd_H`$cZ_*HD+qcI-mm4tje}EOi+4W4 zlb@%U#D8ZHMHP@KB;Vx@{bhC^r+4H9f{-j25iShCGXU@UaH5yl0Z}PGX#aZ%p zMEgrLaJrXifzHfDjr%Z18T|2Y_&Yc76VGXMCRR5OW$&XFewnfk*}QMw6Zb#rOh?&z z8nmG%ShqIQOR+a5NMTR#_sgI-{Mh>zh7H@uzV37^-n|=U0Rha*6JU8Y>2LVc@ymi( z9r6&Q4=28F9I+=Dl;?xL9ij;h32RiYYNrVdwl18(iYie~k(D+Q*X5O}}!VnwHV5 zbFlugmh~4ims{LP%(H};k{Oc35^7Gfd9nD2E0&X|;`8JKt}x&5k7XX@_yFU#7{h@* ziz1$1V$xT5$R?cz6PgAT>qx~q1nzsc8 ztaFR44a&oh&QGw3IjPuoPu6>Bd;MQv<@sCehp-1+hI=8#O;orOKPWit+ozwVr1*n| z=kU`q?6s9IQyDd?*1^m*I;hmP9rqe^=S-FXUgw*0Zo?3-ZoA^H({VU%KaG$xcCa(C z=1!+&ix_DB)Jef|zB?+E<9jT-C+F5=2Y z2ZZh-PPnh;1%1iI>ly4Byo3c~HtUCPal|_wc5(j`_x(HkoxA%1V>X_{V(J^|F{iwG zBaiw;PjHL5u$TdRT!KNto?y_Hxu;;zD?w_BtVabU;k)gYaCM6))o0-Q6AYH*2yuk< zbjEnI*P7vgSByHpJ!7aHL=5^pVIPfkNGs}0ZS{_0ta2PPb{lan>je^dPOEto-nWIx zCGb2Sh~sPZVSAW47WL3?GS_l-iAKZvebI&-t5Y*=akN_|Oa{F9m-sUs0&E}e$+LG7 z+O=+lyd3IxJi3h`69$vRJq-1^vwPu4YHp6Q{|oj6i&F-lK-*eB;p_3gz&$b@=h^=d z{B61R2D%aN{>0x7uVRJ2L!nvtZXa`x@|D$DI&$Pljo9cBr8a!{aLxGf<27yDwslm! zJ8JW5xXW{$fv1YaI=7vH-rX0ow#0MysW16o28{bnsWr@9Q=Espt#=v^Pg~-+&U%dR z)&s4(&ce89+u1L;2anhixUhz7A5#QJYP(r-KftVQ&fpp|;+bblN)awT`+$M#PGFe{ zc{9YEE3tBk{^|<-kXIb=18eH*$v=@Z60 z5uD+E#Clac>sqA}m&#Z{ICD}@dC2#@+t?e^IFn+Lz_WpWPt1?LRxFJ8yh<$U1w6*M9hR6uPK(py zLTYO;+@ML^8*LSgsNIgr?;Sht#F}RUrcPg{&bMFdx!~NSG)O-|^|S^S!kDJtpY>YW zfnh1o?H!NF+>b0=z?NOQV(IEX6pytMwI{x+c@UF(IViukV#ICyI_er04)DWo)34yW zJ|^UQja3?s=7DhtOe%u)eR2R8dmQ2n)BX_d010@BL%vzq;+KaVtc6(lKEj}x8>#VC z78Prj$CvmUF=B*f+_-U?B}sc7H(p=SK z3h2MyFvi~La}WL29L3?lRM;h!;!p4)*b~!boHLEJ3gIvnGVXhnN$sKh_uLs0ukh!= zJtKkCDGd!sQ=Gdm@n;yD2P66=XVxoRnJ<2hJ$e2m{v3G^EI8uqS&)z-Y#}G zG7r}Ro+EO*6RtDIIj=`;)MFk9Vr|@)b3FE}4Z1KFzr?@e!F*=9#sA~(Ex@W;yRPA- zqKNG|b{)H0ECf{SR_yL>1rae2TTu}Uqy&|c?vPNhyRf^k3$Y#lmaZ7!e?y@0FYB>=`}WGdef#ut=g!T3VFh_|=H_g|UYz}NjxnxJu=0M3I=$yXDbohy zhYdz@Y!uR8a?g2d1MUaz!eRTBNS^7Bl%)x9UJ-)EWjkZ;`Vdt|)%P-UU)MfDa{G~| z(_lC=04t-Zctcwtnk+`^BGGD1ICk*fccPCNKn*ED82hgU&KF*x!y-qVIDJ*MAz*L9 z?ZAWF?>XYYA~)<<6O8i*o=_|CJx-82fw!1Dx%LJ2v~?TFn{C{CI5OVi&)7^L_X`@| zes$bUyE}GkGPmhU85N76%DB9l}9;a~PD0K?X zvBp8_9EgD9z<8Tj4CE}q2-?4UwAnWqhhb?QuH5gNz zz;P}M?YJLpq22C77&gluiLAwlUFDB>E6yLG|2S=h8=S*W!=IXY1?zAh$Qr74Ce*p5 zZ|ByGd)=0-wP=wJx0Y#eZ9yAo@JhwfewQ(%9_xqm6o5h7PMql(L9L`dD4{nEJ=^Za zSmToz-{!Q+y>W&|G1W8@BRevOQKvteHXeozfhQ0}UnAtyBWl{MMLqpqsM&zNSNB1% zFkcGmUDRFizl;UcY81HaPC$;lRZyTH`Nx`sth|%Q?b4&!3u;HOzv1 z#p}Y8KFo@cuUH?$cp!bCUS>Pcsxj-yI<}*RJM|68L8KhR9Y4;tofCxp(|oaNmJ2M$ zY{XKhM8?b*o6qFmfvZqCIDq;{lbv~Tzw$?!Kd)i#jxj;kvGqbaHZX^joqv07d_@2C zKGaAEq#olzM6Ps(4}GK18Fng;M%~7B+Nl)oISrrXbR4ZW0(0LF{| z3IBGH3)4AM%VfEq%0GAJi6wUKew_W9^d%qgJ`$~to36)^Xzfq!d0)t^F}w9a@bfu= z0LDXvf3aLsWhPatY1=W)fd&D*LA@95PRz4WADd8{tV>t#6aqqP!gQNzKwp33a9VTtY?5O*&qF$qwjSFAThn&=A z(dF8@aNzv<*CYZU9#SwH>#;qi`$g&mxt&moe2cob`n50fWHXs51T{>c9MPq;(9UEhsijO*{j z?qzP+YGsFIlNY0lac?xKX@L3s48g3AJHaIIQTE#(V=gX<`H3iM^aN0s(Ve-0Jr_U2 z>()yZzp{JU>p?faVO{hU7;q14e~dl&tk>Um>LYe@JruA64S|fkgtHjNH|{!|mixkG zCF38|;P>UeCDA4r*IZ8EX6!wr9(=Cqyv&+ukD$X3Rp0L+fBrG;;S!Go3|;KRx*X15 zx%dj#xmUkK`=4?q9S6AQzRcNUv9tCtm$Ms*?B$=!c{|grT;Y4+9PKduJ@)AtPq62; zrB0Zx307AA02jtqJh;bI(d-l4n||cuZ`j%33%nXqi?s>kKYb;pmWy#+1GMYNp1lGS z(bd=yBikIq_>N~V$|MqF+DBtt&%GGdaTlsq8-a4=2f)^OACkF$3%Z!f{P#|kQ%1w5 zpds%~K`y^|DB~?y56{{w#!zas?1Vj$2hpUtti458pUA#H_P1o^o;<<;`Jm-j$3!ekZY$aiZ~zA12;P$MRhLz zH|{cgy^q4hX&-j2_D9gx7`QFofo@p8ZPu`<_vXObZadWr9a+f>8ZeB-&fy-EP z$NDDjcUyN{ij8%ouIlIf+trx;VU4&B*LVWmqU?dEJ*wYm5Gq#bidGHQqF?7-7;SPw z<=}+QahTKZC`NYiM2(^|P`<)Itg}6cLn+h;BcgA<#@N+PJnuy@c9|UvZv)Ng?{wzA z%7nEew&D9wzGDxrwS}oO+LrGV+nB+>d`j^n+4;9ckGusadx;5XGwdaNi}b>3TJ<}895q< zAEqLb_9Xc9Yj~eyoS19oE^ZzLnw|UAQS!o`ywAHV^}{UAEfM=CcF>JI*&d7^25~;oB0iXJrsPEqB3(u{oI?9W=uU$CyhN$Xs)oEgeL^ zBY2(%eF+cv^1a8H>pMQr6FwHZ5xB?~UaLb8y@U5`*d-*hPs)Zl=EF>2HXv~4?+ zb@feIvto!5#Uf$X@iF#P<(f@+Hf6mB;X(T+*inzKw_r}yG#-XlWD5^6U zh7FcuNXugw)%py^G`)w>`mD7Y6pnGd{n4bZ1)ob9L!9Vt5u}8BT&xhT<$149UV;^IA=r=6zVa#XWOO=DG_C%uP zz{x0Hr#hN;>xfO8ccEL4;V?BGfMXG-a4+~0E_fWliB;i<<9Z%9cQ^NLGNay?@dK$d z@lrV%Gs_*ZvpuNK;z=zaYMpM1#0iIZ&eAXFw^PvBxqTIvhca-G#E|^xyJN;?u&v!JGDBBxBhc|6F+fcTyW5mi~{# z{o)DXpyY(38Ryy0eC`p(&0jL+{_yZ?T;6*T@tb0)WfZ7#@Hn-=&T$<*&HqjKCw^l% z=i3PP{5V(cB)@;k(g!DbzhCw|kK6mHb9VkK4ySM?DRbDHPCdr_@H1%DYBJ9Q^%2=m z*|zyC*qS`Rn$~v_K%V*3B-ivm|Al|vtr+t$_GMm+b)oF{EMKJ!4C>inaCdi1>`47u zVpNkmDkk*{M#FNJs9bgeEa$}G6!$B|cLV!1X5|K?F^LG~{u6Q-f%jc;hoyA}ZR?xD{>V$L^Q10QGWF=rzhG?u{jHn#u=DT< zD3z%JP#_0JrmV$^FG1M!B?NY#IZGIv9Az;MPAqpz}V@Cx_n)T|7qFDMY7 zFyI>ZIfT?E1oQg>zjH6t|9dm@8sk^)!Xnnc?uxkrU*;Ld%&}+vO9BEpKRN2~6GR_* z$~}L&3fZ>_{K)e_=2ik3=W$EE2csTb+jxB}8XkpxxBKwar_Cb*x%T=s`2wHDU+Cw3 z<(?x0%i3OnZP&Z3#TWgHlAKR%f)0k(7-STO@m-H$N{73cXmSgKTOGiZ?&mo}Xg2js zI%7QT@qu&9F)^kf2)OoDt>KKk_!!IFSudSCKU$BS3@JBj-$&JUY3981!<$NmfCNsQgdI{P#J1#q1Y z=FbTK+VmFv8(mDU9QygV(Z>%X`?Ncs=@#QKLF|6-)jBKBbQ5_C4 z-f|724XrAZyuD|UjPDd-oyEOg@ z2a8d&>5tFAXo?i+ZE~dknJ#mB3M`gdb{jEi)Zg-N^oWtlz<&L03KY!A8d%mr?mE^L~;4L+ZjNaDD3}>wDsJutf=Uw-CzsN5}>G8T9d^7_(dBPg|caH*(c0PCqUV z{;y^2jlj;daJan(svCT;J}?12NO?1{c~rI|C z^l$k$Z151}E&V8(q&R{pJtWX&n<-wF1};DoUw57Z#?6612@K~{1e|#_Qn5(f0C0^Z9n~wQ0|?BxPMt1 zdXo9!?P|@zwAG$i=6zUQTYY(-NPO{U{M*NSLU=6vTkmrQHQG(#b!CkywV1N28Tg z=GtSqx6jT$A6`GAN>@3S^cW=M`fjU^;$}U`VC#xeMn9n_Rr~}b=!8FC6x>-`hl_+7HE%` z?0<>H3G@w#CGqJhR&dtnD(3$jk1%d=fi(r>oj>noImiCAN3QJe*tGuvHWu&)*uX9K8C1R3tz1H<^%69p>TcUgPk8Z z@8)F;`VF7Pb5oG}@*aq}d|zGjg)_TpTXxaL_%jz6z#Nwbd9H$=O+H^D&$B>!>AYjF zqU)0F*vuX?561gYD$sM@YCV;n{4 z-&aMOWFJe(C@0r^H&1X7TY3{=e2mFk!V|dJEpeYj{zP1 zF|bnrTDD)snVXz%*V_Va%x4GlJ_)BS7vDAPIzJE3nIMq&q`>#sYqT6}%{W9s^tM}% z+O&1GX!DxV1~wxXJ2U>;@zzK5z4HOTyGQZ!Dd^hD*k+UcdS%Vp6+h*ka8moKSF5g4 zt6EJZz(24yYqBz9G2D-khfXeTD(5ub2?rM^zR18&_$M56IQ$B`=+9~V3!qOedDoqc zvu-ONn~$7jmxX`ALEmrT z@cJAA$JhHXA$|?_WyMu(CQs_P9ib*%)Xh%_SJz&~kjX(m=2j&hqal*#Oa*hakpw0B zL78-%;qf?YafWR4g2TxN{N5Kt@jSDHRYlUhZ#cmE=lz`Voya<>xLe#0-~7nhMb=J` zkKyE^KeY#pgh<3XT!a*bL?@n z9x($2ax(tReMM2FJ!z=v>Yf6I;b$ zY~L7kYVL~S701G`(^|M3VjUuTXM}r#5bmu6+4;AXbr_9JE$NTv!T3EMKjmL1=D{`o z72^Jb{9E`>`B$TQO{Hp;Y6^81ltxXOX4pV-nEXV&292@?VMM(oG~3k&XArH$T-5GFWhsm?ts|lB0bQ#q1UfEJf0_DL%Kig zJ_KUNhY%I^U;H4jkRrV`VcgtXbBU&$UrH^u3?L`Tp zKPVj3>JhX0r&;;8Iw6g93G{oy(^S~-{%tjQ6^d3M5A!qLmLng}8-4dbT42rwFSWKS zp7(p;`N!<@?|_zdm#T3WgVsx_S2YmzyUpYb@)roY^@{a~Tz4~hp8fC3XV}Yid9_Ot zDmP~ig>mdL`wpQ#YaHv*_meeP*5dZ}-lH9Bx`*@sDeuae0Q@YS4UH`~Y}}%(TxI*W z_OE7*T8hd+@-QeQtXgheJ=Ue>q>ci8DsphR+sPj|sPS(};@iKhzZv%LNB#--1YVcF zV8?ORVsHk7DQooyZ9R@v$*fUeYB=b^7nlwn$-k$Aae+(WlXeK3J_cbk?Ob;Lt$rVh zWp4s7z>4oLrf|;iP_Bc_!LrCTk%fjQ*eAwP4V5=6GX~Re8IZ@sci!=JdHjGmT2dFc z9?48X?uTwX$G8oyIL_GqkyQ2)@ih%BU54N?^SLQJ3U0p3sIj`p_noHJ%W?J^9AWK> z#>4O{T>rV(m0rAN^Vlm%pHh6z&|8eL{DOa3xv+*ljZO3mtcc*=FOvHmu0O%2zasf8 z>yP(_!rH}?@vJ}uvMwR^68!}Fzfxc5L%kW{U-i;6P^ZFN>dcs;ZO7HjN8LdpZG-SH zfId&wza@`_cN+J$&>vZ3AB9|{m^Wh&wf$u zpZHu_yv&=mZvxw-*RTk<4wEI}SiYZoKk8v@=S+@b#q*&|*&^7U9twx|Vc7O5@Tb`P zA|8vc_)zD!1PT@}2Is`n)cAOZlWf{PL(K$De?XhN@+9p8vf?rIoU%fjYb(03f{X9J zVu?HTQ&VoT$1M#vpQLf_Jb#8fyZ`DFwFx-4iv4Z(-~I?q@8CA;=5M|FOifJIaNU!b z<|o8324ER|8J(Du^J09!C*=eD=$DB9m;L%@f9`nVE7rxoq`&+U*6~lVo;7XTrQU{X zy_{QL`Yr-L`lAA=p$@yly+0j?THU6iBz?dlwFjZ|_%(3bNB#TjoHa;<-Dd2O$o}`D zZgEdTyXSiFJ|^*=@$o-E&FwN!jD}-8?VS;eDg=%0epAt!wX(+SEtpDQQP+an39NZ( zVbp5Smi3#J^($@vBlm=x+4*NSZ+-{y{nWh(?O^3`_UN%OPWAaDAENC=wEf58IK~JG z;hx4nC+4rj?-Kq6@tg=W{%t$?S><4tmHS{Gk_u-wBF%E%uYT_S1G@b!|F*nLK=D~^ z7?a3_X=ZbAgaxXnxDL{N2HofXG5;>4Gk1UVG`0mqu)poC%Dd~SZ}8$1*Ju8I;h@Sx z*4Ss|U{?OI&4ae}6E09NA%yu1v4QJPK0_C3vkL#DH|C%6&*>C>0@k{(U~RY!<5ruv zhB#fMe@O2AjDI1w$W1m7dL-Y+^o@a>Fa8o9^r3tXQEQy{|KIUX)??ZGEZ&oh`EK#p z;=o#F$wN(Ajp5v{W#^v}=YwecQ${SNK4m`6$n9vdb;B0LZq3HO=U=T_N-aXOf7Uk3 zM{8@Z@UYwnThy8Egpuy2S>yT`7SWGXSjMp4EA|Bz#&R9b%0J$t_B>A>>;uorKVSOc zf}PCmNQ_tfzp*=0;LJU-!AR4r{$jy1_N@ut zz2&?a;>xG@IL#vFa;YZrgH{nCLZ!A zxcBCrs);GOn73X~r+{mn@Xv{R+TkwyR9zb3pVWM3t%uEShqUJ$E5*10V}YC%=g9Ny z&Un4|MfMp{k8j64&gx)%P7ub}qacW}NWp3r^IGglVvLoshoetbJw)lrksOYS%=c^0 zzwbZDKe2uN7dol7apZu}+*jvE(LEQ?ma*C{)c+7Ppbsd}=WNlI58uL!zgsaFCO+Hi z#p~Bp+q80{vT^zPf6c!+X7kLm+CO9K0Mwu5it3ZLsdiBO!O5(noJ(z9!C%&2@^4-A zLpU9L@>Bk8Km7@gT%VW5r(wdbE7%%y8-?|UaAvTAkVAX_!oT%jnG*x|!I}IUG=Hd? zZ(Fc>8*V)Ph+Fi zd{p?S)n#iu`_<9o9OKN%oC%r47-a%$%NSc=o7OM+7jl!lCjYDhPGBnw2zK-Dxw9|E zoAKoU{#}i48eg@%j>bPf)=w_lorKZUaOV~oB?^{8`C@;dHRCy*`F~{RUt?YX;h#Bk z3`(VTj7Q|Wx5i;hA^TN!%9b_u>fS>37uiv@Mh!(^F=V7Ncfe3($mk);=t-lMIo5L* zN9!FpUyfW~`9~+2u z%=p@2FJDc@`Uk!!U9mI@6)%J&3J_g+O#MC10Ziq-;xTRDBkobNL-ca8LNqKz!|6L% z$MNDVwN}X8>pUJvIZM6kvYdg^290u#MY}v}P*0i5=e4MtTaNP{3R5p?Gkr?VkYKN+ z{9V!U7kwUOd~Z8jh_VqBpqg?R*ppL|wPoP81%-mn)bHi|lIvKLo^-+oBfSz@+G zF`GQ+FTB%usPWHy&~TN5gGLTgOb44PvzN@V;qO3x*01KHMx3gRowpP!u)eVU$mwW1 zYzoRWCC_zPA0Yb0#Y!`t?2K9aXb-vfkerEdP~)F)PdKRYFM#*1!2Qf;*t36Si+eC* zrkqdOqYUc_V*bKETQYAwyy5aL22+z(vyN8>U3z!HMQ&UruJTX$_vAJEbF)CxVBqOf zH81Y6Uj8)iV}qVuCDp1>n~EG&JefndhP!7HmBTea00UviCVV z|H{33htlj58?u9YkK8#?rDk`6qEdV#vsm%An!Hm60PxF<&xX znQpc~<)2&wB@cTzIXkbaLj;T;a;?Aq8Yb*RlVkg+6ev9<_fUeqTUqp2?8N-q4VWKz z3+WAAy+0LesX@P+y}*0f6YR&nOgV}V63+dg++X;&`P*LB z9?&Oz9t(q|tS93CXuH!z)zZJfx?-7QBmSGF)A5LP?}7)f$;~XdM;m#US^~Ev9wRY8 zt}TKyFF)WE`^1mkc!-%c>sY^01vLt`$JVCz*;DomTN_h5uQ}I1)*5fr|B5+H6Ht!& zQF-YT%^WjF)!^6Ge$nH@`V3{_ zta-|mxt4#+ztIyWD5e7ks@z+#d}T@2c_pdb6T7x>DMmAYHtqH|7(F2W*^kwM*Q`B# zu8E9YORxH5sdaO9x#;TJz}Fo=Ce5&WMuedgJVH>jVx z=Z=Z+PTT(%{@H(}CKdVTpLPI62hk^E9w_m`Sylh<9CJax;@^G7QUv#?@hG^>*t6gi zNhdhLptE5A?Z-H9=Lz;i?MKey6&OFQg}JnK?rmSG5dJx}V0@f4b;7~b-7aEQlQ{gI zrv%?yn6-LWf8e0TyX#NWap@uZ;>o`V=BI>%f`7|D;h3z!(n~MLpK&hhYr-YrpX7Mt zD16Jx!R-8#_X$ruZ@pYv!kmM$fqT;Yg^;s$J*)_Rf(Cc!RRsj$XMHM$iAM&8E{~IoxtAsBQ~~5$G`>)Xe(>M zbo_E8u~y|I&&65R1)t`*I>x%xL%i++S$hSV{qtl^r1zO;2$k9#$#-SiJ?)s?{%QR4 zA@>4k3pI#c{28=0S=M2J#=Y#k)8fqTj86)dhMY!wb9=;`yN=z=b0|90KrdBa zKlSLK2wJk&2MAQOaZJHDYf%2-Sl8*5@>l-#=Ncyan$i9X;PE0hgL}fkO#W?74}jCR2-f*| zKq;$m-dtD2-+6%J{7iy#49W=yFT51JmCrbl`VoijzEwFFca3w+{)T_ax6&|ktsC;> zF2UIu)v(UsA-1-s-e%KpaAbX5cK!+XHW<9afclo4!_*MH$J$Wu?*jRT!S^9)!<%N zpgq@))vDI-#{KMbMYkxbFW-O;{2L~;#fIE#8!>j(`7ZV0m`|!hC#Nn0Yyx>KuL&k! z`G!$v-f>1iHO}zO1M{8Nu{!i6Rvl!{k}>Kp^xV<3h+A|f>Qz!fB zzHZZ0|1Z1!+x9XL?w?{{vxl_?tVwM@djV^@*zdtOMAUie>@i*-npaM&U-2LZycuWn zVywfHwnyN@I#6HM&HoBfNyn+>UV+*arkLF{5RQh_SR+=ope8}{Z*XZs`=`&^y5{uh ziH(M7Sf&3A)j2Dyv`#5lQ-@9FB<*9}awz-WrS3rL55ie5D3F?3ffny@O1cH%pxC=0 z*63<>PdjF}f5Jb}5fOh*vwd>>neCI;{w@FZGCnlX{UAoo+`@UkAMrlzE$Y>-#d}>J z%UJ)@gnpqwIM;%8Ruc1T$Q-b&#eyw|S@&Foa|{X;ytmGN^Y1e(X4ChZqO4&UW#uf( z?|l0O|4g{%zShlC1p4;Qn4fZ6<)3iSn0tg)%*}S7edu`moyxoV6vC*-K(BC6<7IjF zoh!Aa&p_b}--cKo$axOooaxIrpe36ZHU4c#e2HyG-v7YA1Jt;8^V`RtWlX1g>7TE^ z@515uCIsVGbFag_=U6*O*1kW0KkK}Lj;EXG-ka)ZP+lUtpDhn%D+~u1riQs=U=NC>v;Z3pjg>*E1##mQ|4HiD^tyA182@x z)>~LB=A*}|m^gm4BIqz}xYBv%C}qNc?)n84<+(!L;D*~{)xCAyt8Y-9`wT%x)-jpT zKdH)iT`l^3^_f?1%vocD9z4R3N9;@T@kYVMjrg|<62C#IYKqAoXR!3ZC+0<%6A8b^ z*x(B+r#7@;C37VLH}=*_eT?r#?$L-tx1OVNE5b15N4GfJH1EW?MkjF%J{+Uc3qKsT=QL>Pt zoGe0{Re0VGOyvH$1sDDXH{SigKjB~o_kbF#D{9WYZ=ajDp)6fSF6eQ;ql20~Mq${} zEod@r2ih+1#<)EbDH|^QD<+Vv_V6)_O!;hX4G%- zgeyX^{i7ciz1xemh{9wm_Bt^xK6uSG1Ty9xc!vGH^e5#yIvu8tdi+gnjJ>A%NwQY` zihrTZ0czZ6H)S2|SSbu>Gfvg-5e{nn+s2rSz}1lb!F=7-C>8dNuA@?J1J>Tv#-gnu zNTzmGR_@7Tjel~K=NkWnd&0r-J5#WkIbGq2#=pRnNANoJg?az~KK}x)QXh=9wH_=E z8ox6PjVE|u+c7fl+Z*zajT~R7e|ho(N|mmHf}91buwT#QSQ^(spc~if=Cn~ASYOnS z1F)4f_SB%sgbg2tRMzn#&~o3isyk86L=cKzW7Vw`$cT@PRPJTvU#n(@y?Bk& zl`566^u#e3kjuis=Hyvp`js00YCis^@=t23yHJC!ZS+Yf)mrlNP#?NYU!1>m8}GAV2cnYLT)qgWA_`cwXGI`~qp$M;P6g8dSQd-Mr6IRoP;ugBmC zlW7MT_a@hrqIsd{Gq%{2u{OQDD8rg7r-Y02si-SS{>gO^I}*e=m)ODWN%yca=nUML zt5RdHjD>03lcVrYIN^6*@=_nw^;h~;OBSdAyE^P6qR*F|e=dA&L%mO!Vc^UhoFR%; zsEosBFRJ^W7}fz~<(@p&xThV3djZsG+sSp)mOk(j>U9eL60bdm>2xbptltE>#aZ9P z`Glovx4{@|NA{&A|5y39k#T>+CDcsbbx55P{(8T7J zGe8+VW}>ori(^S@F{CKD%Ar!kVf0{}qB`Rz0xiBRHc)IK-5s^BWaXn5=;jy7x%Uh|>VlCVLCs=ooS{GdRUAQ;&xb;b`o0r=8M8+jHQ(L<Ys)^z~0y5($i2D4GpPD z;t+yx@+?y98+|x|sfh zXFtJ$eb1qcRe2q|!JbF1Bg}7BF2jBueFgeWbFluAv#aQ1RII6sxmNRW@Z>SxCmA_2 z-$2^>VE#Q19$jhse7T1E@O}x&fESM{_#Q7}H=pm~aeF`p*g^@<7}h~}0sY4Sy-k@P z;d}m2XlE3z`&(JVM!(<^eM}X+UoH`+7~`659g9khYpI`CVi1aGMyma~)E~-~16*52 z3-{Vn*DpK&hEEuxOqe-VdHv?SQmS+XYL6F%N#AMc#D0qo+<$8P)ArLfSyV0@l)k18 zjDvUg4&UpL6a7ZxcMFufXrKd=K+of?&fMeQVZd$=+jr^lQ}?3jgX* z&%2xbA(eZJ8CVzotzy5q)KS`>phg&bd)!#dCfti)0bd+r5>f1-mFpCCiFIp<=MjA5 z8j{aYZ%FhW|Av3UsR7nIv5CH*@GL9$H2%qZG{l^`i^d&?aBZ)IaybkzLXR^~nxBJk z&zbA-rf$^O&BypU>kZp>DSi{JGbb9?8G6f5JcMb(6lV<)PORz##R8;AC}e zt5&Nl4#utK_1=NgXPglizZOHrw54_S?krhIN|3jKfDT-V?^Xju&HmzT9JpKBTYgGYC(vccG&5!kV^7*0c+<+CF(4ev9vA zp0umk7C3S}&dNXW_x2r&)NmBq)NRk+ zN9viBXaetea+*0@nE~T<=DiBpOaCXxXA=K(>;8vot#|lw@~5!@s+3aTylDn?dd(@8 zx`4A<%{YT=Chpz0<~*mKXj(4^3UZELy}^@t4j%FT`}d8)#Nc?p3V`w~3qpF1DFj%6W97-q4S=Z!NSU$aJ*c0|ebnClB?B~!PI zXl|G}mRE6}6YVFp5eL~yO=@oB$dQ+|7&)m`e@cjN(^Qtd((w~X8Ye@=Dz3XYJUKA zsV&xr8d1{&FEKv-SjE<(Z@K@XPV-r=2g1Rt)C9cr411W@77hyk_MH8u_S8xKPPq4< z_$R!QwRda6EzEX`{g3<;`zXERJFig#j_b73-s9-qb|i}Dsmk11G0thJ$YVZ~)%^p@ zcSKTeiMed*&x-D@&pG<{>Ym^~^H2CMbt@ZUZt;Klf{BY*Ys+ibt9>P0KFLM&wWZ2I z;ogV$b8-95A{9xo8_}X6>kp~-HPv zab~zB|2f@4rBR2m4kH$WbYj0@H`b4I;Qu42$KNl|e@vh7+5?zQFz4^4ZdWT~)!e;h zZSTh~oTEVQrSl`nts>@dnDbd`wy_r^o_vd9uUssTG3*bf7J^_&EbU?zm~J=#!})&b zu_g`+qn@$X`#tq8IJfB-V{G&{_OMS*pxHJ5i>&z|0ySj%eX{-uWPKC(WFmm|4Iww^ z6OpHm$q!*cjWenL^My(IT{M|=Y zopeubwEUUt@jlCZ1R>7_sx8f8Pi1eD6;3zl`^KFo_s)=mp`5LvpnOGL96H{Uvu5Vv z>D@IdkUED2+{7z0o?I)ud%O_Q{%vTVbWyi)Gx*0c2G{EdpTC4jXU0E90wpVdM5%(T z$*WC$`#oGAl0p4t=IZ;OLTPep>|kKz6mm@OBuX?1!1T4Cy%L+pJ>&W3;4*^}xj~)2 zcd%N|INxMIFaHx%t_lpE2_$i!l=k|AI(K$?5@#m`vK}Zo6^%#BJ(S@Ca{c`r6dGK+ zmuYW>`(UM8&+b29>sDLpowj72BL|urcR?fWFY2b8;{|v zEwc)}hi%8H-F}KQ&d5D4YR1S~DF|difr@{?KQH!v3AcQN59FF~a0SH`=D8=qgIcS8 zjL*vV9IMWb#jYE}RtI2C-SbjPLGdj&Ub@3bON0W~^sj&$Dt*9@{XkV#h*> z$a~jOrB`RQesI>@A)N1Frs6OBdp3*3tCR8Op(QRH8H$|Lb*NT{v97QT{^ib3&6P6b zknT;|#%m~)>$!^N{W;q>LFPR$Mm*>&N)&j5{@s91ra+#e7g4N{A6TCB7ygye1Afav z->bzVrEiHv!GQdcgKBDRF#+Jo0B{}%4ci1ArPan>|#pI~7Odqaq< zwoe`}=4;Cmm=oci*m~b9%=W*7u?&t)W~0OWpxdx#4fT%W0pkIhUUiJ19Kdl5hvJoZ?h_xQ82dA8MqFomBbA@BfT{T0X&tz3X0lzi?0>>#oW{ z+CI^G6aJ|nm(~4FHv8wpSxK?XfsCHc9&X*=(XNkztEnp(yPAe4FGh35`yv&1Oxs6% zOb`(VMnPLHAKZ;o?WV$37Q1+w?7T3%qE0Mef7c2q<4W6$Q(1?oksj44n4_Ids3UuJbeEscU*8jXdW5gVcbD zQ4j*@4n&f}Z8~q|>yMD9BV=L; zhjvQ$UOoQ8KMF5P53sjVxCpgcVwk^WyqPtZt*(Dlq4BT!(+vK#ar9(vqa^*lJpBE% zvAGMuWtSg(+2JFV2{*IxPdFtU6gwy!)AV#?4W7)4voh!+wsS8a>#@97vv;!giVmQ< z7W4NC|73>tF7j2dhchJv+P*`$_fPm2%0Yz-oI;VSa9QLlQ5k+Q13ABQFl)pP^-IuCpg->yD6bZd@~qB}1>! zS&ZZQX)kti>}eD(bQcX9a1S|zItb;jp>zvBH0^hU_Jn?YHvS0*H*cVQ;hsi%pxIVT zzwjM-z&TE=H;7KY##sxTFGCJGb6|v0gL`zTO(p*d6)k+LeWwm8?|x?g@aY3XHj0CA zX_|u*dR>2p?pGe8CHKB9xCXZ-#1@)7XKf98su~(}gNR@=Ca+`gn4reaZ?euM_BD=?ZiDg*(Yp;hFT~s@x<0WX=5n znY_!&zpVDphwoQGe^P3syg64)_QSIFN^Iujv7_qDmm-XtZCO7Bk6%vV%)e=P|8g>C z(@w$bmorp+ema{o&t~JV9wf5O2ZxF=l9!aaGM zm3#8|XZ#agt#yfyLwN+?y zw7smlpUC9hulOf>h=8li^&Wes+CMi2wWKFfwC+W3{C~F?`t@0(YlrdRMYym`#MC9muGlK{8rDwwjt+ECU!GIzB$j#?F7UbIib zy&U{3xj^38iT&%+rIXT~`;V-)Pabbuy;6mf?bhGfQyHw**H>x-Wm`CEu?|eQSMl0Q zjJ|LeO79VjwdQ$kYrU%W@5S^^$_$6RtgR}kjpI-6f9Kx@&WDiLOI8m4 zTl@ES{L^@t^(gjH7Jvshw@=vpS!aarY!w&Ux<65#4 z9^ReKnT}>$;}-pte;=OB!KW8ym@>I8Yq1q{8#je@L6;ayPgi@Xw?=31FSqi7`abm0 zPIK=={;gu(tYFzwC{XG$Mowj}ErEIbAxCKcUSQC0#>bdzD_s0JN;gUbrM?Dr7g^ry(?4{Cniua4HNm-zwN{6(v<$EbU;YE*e$0h1 z9=YS_N4WBJcbX`I^+cqnO+ngyd-ynZLJ_?js9)m`c*b#kJw$uMT=O>0 zV%kRj)$A6-da-o;rt=MEv}^XZ^qX3M8qaT$qntMuIdDx1BL8|^VeaH3IvN8*#!?5c zLMr~K&KNkknlC@&&AFbUIcJA9oxKM|3%rJI&PS+SmovrovOj`*;11+>@f=TS{~oCG zL1n(%+L%YMr5^5-d5jC^%1N%!KO+Ynwr=U=@9U$41qUhKdv+^9{(e8ev`2SkK%d@9 zuO6l<2W81OHa6~%D_1VsLawp2aguA4zD7kSM{1XzJ-fB$e6G1Orw*cW5ehOFsY^e& zRL>PE|D57(fJ+ycqQj!i9xDHYgFoe-aPohde+xp{*DiWcacL^dlF~4j{dNmj>nd1t z;4y3uKEw8-PgJ-t&$pBM(;n395oqy6i7^U1xW2fZpgu+>|Lpg_#^wWWu#Ic{cJgrt zdpevs_rsC({Vvqyaif-IR%GX&H&2dU9oC7kt{@=R{%`r0_GS{EJsE|w`)5+0kA2td zEe`VG%#Y*jkD`r}S+b&|>5|C&K<@>*M_5ga0Z@b6km&dKz%OenUf!373&b!vJXmh?Pacv%vTT%5C_)4iVnz5!j;dR zPkw*{-?J(C75!ss1#_0v2JV04`Q}4!)YrC9FMZpwk3V1w>$$h`=l7mseDEam*z5x+ zP{jllIUC~4(cSoTXLSZ1S>Y4$@yVP_nDJ3B?z+}(=f>T8d@}p zh5>))HkT+c^}${RuD@J&ZE643lYb2jTV>cQ9da;-^7-hYgE|qRAℜrxLK&_b>dD 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 literal 102454 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 X^XDOEuT^AZWLDtJYJIDO@b~`!1F?>E diff --git a/sysdata/programs/render_example/src/examples/assets/mini.qoi b/sysdata/programs/render_example/src/examples/assets/mini.qoi new file mode 100644 index 0000000000000000000000000000000000000000..3e42d02376480110ecb4a27b06ff55dfd98943a2 GIT binary patch literal 62864 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 Date: Wed, 6 Nov 2024 19:57:43 -0600 Subject: [PATCH 12/21] widgeting work --- sysdata/libraries/horizon_api/src/widgets.hb | 10 ++++++++-- sysdata/programs/horizon/src/main.hb | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/sysdata/libraries/horizon_api/src/widgets.hb b/sysdata/libraries/horizon_api/src/widgets.hb index 9bf56f7..1fa04b5 100644 --- a/sysdata/libraries/horizon_api/src/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets.hb @@ -40,12 +40,18 @@ render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: V render.put_text(label.surface, font, .(0, 0), render.white, label.text) } render.put_surface(surface, label.surface, pos, false) - render.sync(surface) } new_label := fn(text: ^u8): Label { - text_surface := render.new_surface(100, 20) + text_surface := render.new_surface(1000, 20) text_length := string.length(text) label := Label.(true, text_surface, text, text_length) return label +} + +VerticalLayout := 0 +HorizontalLayout := 1 + +Container := struct { + layout: uint, } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index c33882e..ed405bb 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -77,6 +77,15 @@ main := fn(): int { // TODO: Get windows out of a collection and iter through render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) + { + // Scroll bar :ThumbsUp: + render.put_rect(screen, .(100, 100), .(100, 10), render.white) + render.put_filled_rect(screen, .(110, 100), .(20, 10), render.white) + + render.put_rect(screen, .(90, 110), .(10, 100), render.white) + render.put_filled_rect(screen, .(90, 120), .(10, 20), render.white) + } + { pos := Vec2(uint).(1, screen.height - 21) render_label_to_surface(screen, text_label, font, pos) From f13c682171db2231bac645c612ab98dea4e8ef93 Mon Sep 17 00:00:00 2001 From: Able Date: Wed, 6 Nov 2024 20:11:36 -0600 Subject: [PATCH 13/21] refactor and widgeting magic --- sysdata/libraries/horizon_api/src/lib.hb | 2 +- .../src/{widgets.hb => widgets/label.hb} | 28 ++++--------------- .../horizon_api/src/widgets/widget_types.hb | 4 +++ .../horizon_api/src/widgets/widgets.hb | 28 +++++++++++++++++++ sysdata/programs/horizon/src/main.hb | 2 +- 5 files changed, 39 insertions(+), 25 deletions(-) rename sysdata/libraries/horizon_api/src/{widgets.hb => widgets/label.hb} (64%) create mode 100644 sysdata/libraries/horizon_api/src/widgets/widget_types.hb create mode 100644 sysdata/libraries/horizon_api/src/widgets/widgets.hb diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb index 72f58d1..112bbac 100644 --- a/sysdata/libraries/horizon_api/src/lib.hb +++ b/sysdata/libraries/horizon_api/src/lib.hb @@ -5,7 +5,7 @@ render := @use("../../../libraries/render/src/lib.hb") input := @use("../../intouch/src/lib.hb") -widgets := @use("widgets.hb") +widgets := @use("widgets/widgets.hb") ui := @use("ui.hb") WindowID := struct { diff --git a/sysdata/libraries/horizon_api/src/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/label.hb similarity index 64% rename from sysdata/libraries/horizon_api/src/widgets.hb rename to sysdata/libraries/horizon_api/src/widgets/label.hb index 1fa04b5..3a44b39 100644 --- a/sysdata/libraries/horizon_api/src/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets/label.hb @@ -1,25 +1,13 @@ -// Widget types - -// End types -stn := @use("../../../libraries/stn/src/lib.hb"); +stn := @use("../../../../libraries/stn/src/lib.hb"); .{string, log} := stn; .{Vec2} := stn.math -render := @use("../../../libraries/render/src/lib.hb"); +render := @use("../../../../libraries/render/src/lib.hb"); .{Surface} := render; .{Font} := render.text -LayoutChildHorizontalFirst := 0 -LayoutChildVerticalFirst := 1 - -Size := struct { - min_width: int, - max_width: int, - min_height: int, - max_height: int, -} - Label := struct { + magic: uint, is_dirty: bool, surface: Surface, text: ^u8, @@ -45,13 +33,7 @@ render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: V new_label := fn(text: ^u8): Label { text_surface := render.new_surface(1000, 20) text_length := string.length(text) - label := Label.(true, text_surface, text, text_length) + widget_type := 3 + label := Label.(widget_type, true, text_surface, text, text_length) return label -} - -VerticalLayout := 0 -HorizontalLayout := 1 - -Container := struct { - layout: uint, } \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widget_types.hb b/sysdata/libraries/horizon_api/src/widgets/widget_types.hb new file mode 100644 index 0000000..9755ec8 --- /dev/null +++ b/sysdata/libraries/horizon_api/src/widgets/widget_types.hb @@ -0,0 +1,4 @@ +VerticalWidgetType := 1 +HorizontalWidgetType := 2 + +LabelWidgetType := 3 \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/widgets.hb new file mode 100644 index 0000000..c9aca72 --- /dev/null +++ b/sysdata/libraries/horizon_api/src/widgets/widgets.hb @@ -0,0 +1,28 @@ +// Widget types + +// End types +stn := @use("../../../../libraries/stn/src/lib.hb"); +.{string, log} := stn; +.{Vec2} := stn.math + +render := @use("../../../../libraries/render/src/lib.hb"); +.{Surface} := render; +.{Font} := render.text + +widget_types := @use("widget_types.hb") +label := @use("label.hb") +// .{Label, render_label_to_surface, set_label_text} := label + +Size := struct { + min_width: int, + max_width: int, + min_height: int, + max_height: int, +} + +Vertical := packed struct { + magic: uint, + // array of children, idk + // use a vec or linked list or whatever + children: ^^u8, +} \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index ed405bb..cf9a000 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -3,7 +3,7 @@ stn := @use("../../../libraries/stn/src/lib.hb"); .{Vec2} := stn.math horizon_api := @use("../../../libraries/horizon_api/src/lib.hb"); -.{new_label, render_label_to_surface, set_label_text} := horizon_api.widgets; +.{new_label, render_label_to_surface, set_label_text} := horizon_api.widgets.label; .{sexpr_parser, render_ui} := horizon_api.ui render := @use("../../../libraries/render/src/lib.hb"); From ba59233ce729d9712c24bc3ff02e14ad64262cb7 Mon Sep 17 00:00:00 2001 From: koniifer Date: Thu, 7 Nov 2024 02:22:58 +0000 Subject: [PATCH 14/21] untested string.contains(haystack, needle) --- sysdata/libraries/stn/src/string.hb | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 41a5535..efea870 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -81,4 +81,41 @@ equals := fn(lhs: ^u8, rhs: ^u8): bool { } else { i += 1 } +} + +contains := fn(haystack: ^u8, needle: ^u8): bool { + haystack_len := @inline(length, haystack) + needle_len := @inline(length, needle) + + if needle_len == 0 { + return true + } + if haystack_len < needle_len { + return false + } + + max_start := haystack_len - needle_len + + pos := 0 + loop if pos > max_start break else { + is_match := true + offset := 0 + + loop if offset >= needle_len break else { + if *(haystack + pos + offset) != *(needle + offset) { + is_match = false + } + if is_match == false { + break + } + offset += 1 + } + + if is_match { + return true + } + pos += 1 + } + + return false } \ No newline at end of file From e8edee4ccc5d741ab7e34850265b5585ed72ce49 Mon Sep 17 00:00:00 2001 From: Able Date: Thu, 7 Nov 2024 09:57:39 -0600 Subject: [PATCH 15/21] broken :thumbsup: --- Cargo.lock | 6 +++--- .../libraries/horizon_api/src/widgets/label.hb | 3 +-- .../horizon_api/src/widgets/widgets.hb | 8 +++++++- sysdata/libraries/intouch/src/lib.hb | 5 +++-- sysdata/programs/horizon/src/main.hb | 18 +++++++++--------- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 680a1a5..fb7bb7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -264,12 +264,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -281,7 +281,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" dependencies = [ "hbbytecode", ] diff --git a/sysdata/libraries/horizon_api/src/widgets/label.hb b/sysdata/libraries/horizon_api/src/widgets/label.hb index 3a44b39..5a02f15 100644 --- a/sysdata/libraries/horizon_api/src/widgets/label.hb +++ b/sysdata/libraries/horizon_api/src/widgets/label.hb @@ -33,7 +33,6 @@ render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: V new_label := fn(text: ^u8): Label { text_surface := render.new_surface(1000, 20) text_length := string.length(text) - widget_type := 3 - label := Label.(widget_type, true, text_surface, text, text_length) + label := Label.(3, true, text_surface, text, text_length) return label } \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/widgets.hb index c9aca72..2cda29a 100644 --- a/sysdata/libraries/horizon_api/src/widgets/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets/widgets.hb @@ -11,7 +11,6 @@ render := @use("../../../../libraries/render/src/lib.hb"); widget_types := @use("widget_types.hb") label := @use("label.hb") -// .{Label, render_label_to_surface, set_label_text} := label Size := struct { min_width: int, @@ -25,4 +24,11 @@ Vertical := packed struct { // array of children, idk // use a vec or linked list or whatever children: ^^u8, +} + +Horizontal := packed struct { + magic: uint, + // array of children, idk + // use a vec or linked list or whatever + children: ^^u8, } \ No newline at end of file diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index d57177a..b6d751c 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -19,7 +19,7 @@ recieve_key_event := fn(): ?KeyEvent { return null } -recieve_mouse_event := fn(): ?^MouseEvent { +recieve_mouse_event := fn(): ?MouseEvent { mem_page := memory.request_page(1) buf_id := buffer.search("PS/2 Mouse\0") @@ -30,8 +30,9 @@ recieve_mouse_event := fn(): ?^MouseEvent { dx := *mem_page dy := *mem_page + 1 mevent := MouseEvent.(dx, dy, 0, 0, 0) - return &mevent + return mevent } + // log.warn("No mouse events\0") return null } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index cf9a000..0f59328 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -50,15 +50,15 @@ main := fn(): int { render.clear(screen, render.black) // TODO: Read the window buffer here - { - ret := buffer.recv([u8; 4096], win_buff, mem_buf) - // for some reason this null check causes the compiler to spin forever - // if ret == null { - // log.info("No messages\0") - // } else { - // log.info("Handle Messages\0") - // } - } + // { + // ret := buffer.recv([u8; 4096], win_buff, mem_buf) + // // for some reason this null check causes the compiler to spin forever + // // if ret == null { + // // log.info("No messages\0") + // // } else { + // // log.info("Handle Messages\0") + // // } + // } { // get input events from drivers via intouch From 7f01b0e0f8a7d3fba371ca7b12c99732475aab1c Mon Sep 17 00:00:00 2001 From: Able Date: Thu, 7 Nov 2024 09:57:50 -0600 Subject: [PATCH 16/21] :thumbsup: --- sysdata/programs/horizon/src/main.hb | 1 + sysdata/programs/test_abc/README.md | 1 + sysdata/programs/test_abc/meta.toml | 11 +++++++++++ sysdata/programs/test_abc/src/main.hb | 19 +++++++++++++++++++ sysdata/system_config.toml | 11 +++++++---- 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 sysdata/programs/test_abc/README.md create mode 100644 sysdata/programs/test_abc/meta.toml create mode 100644 sysdata/programs/test_abc/src/main.hb diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 0f59328..8afd4f6 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -65,6 +65,7 @@ main := fn(): int { // key_event := intouch.recieve_key_event() mouse_event := intouch.recieve_mouse_event() if mouse_event != null { + log.info("Mouse event") mouse_x += mouse_event.x_change mouse_y += mouse_event.y_change set_label_text(text_label, "Mouse Moved\0") diff --git a/sysdata/programs/test_abc/README.md b/sysdata/programs/test_abc/README.md new file mode 100644 index 0000000..f569e63 --- /dev/null +++ b/sysdata/programs/test_abc/README.md @@ -0,0 +1 @@ +# test_abc \ No newline at end of file diff --git a/sysdata/programs/test_abc/meta.toml b/sysdata/programs/test_abc/meta.toml new file mode 100644 index 0000000..45bb122 --- /dev/null +++ b/sysdata/programs/test_abc/meta.toml @@ -0,0 +1,11 @@ +[package] +name = "test_abc" +authors = [""] + +[dependants.libraries] + +[dependants.binaries] +hblang.version = "1.0.0" + +[build] +command = "hblang src/main.hb" diff --git a/sysdata/programs/test_abc/src/main.hb b/sysdata/programs/test_abc/src/main.hb new file mode 100644 index 0000000..f00414c --- /dev/null +++ b/sysdata/programs/test_abc/src/main.hb @@ -0,0 +1,19 @@ +stn := @use("../../../libraries/stn/src/lib.hb"); +.{log} := stn + +Structure := struct {} + +returner_fn := fn(): ?Structure { + structure := Structure.() + return structure +} + +main := fn(): int { + ret := returner_fn() + if ret != null { + log.info("not null\0") + return 1 + } + + return 0 +} \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index b1d8a65..082f361 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -37,8 +37,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.serial_driver_test] # path = "boot:///serial_driver_test.hbf" -[boot.limine.ableos.modules.horizon] -path = "boot:///horizon.hbf" +# [boot.limine.ableos.modules.horizon] +# path = "boot:///horizon.hbf" # [boot.limine.ableos.modules.horizon_testing_program] # path = "boot:///horizon_testing_program.hbf" @@ -58,8 +58,11 @@ path = "boot:///horizon.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -[boot.limine.ableos.modules.ps2_mouse_driver] -path = "boot:///ps2_mouse_driver.hbf" +# [boot.limine.ableos.modules.ps2_mouse_driver] +# path = "boot:///ps2_mouse_driver.hbf" # [boot.limine.ableos.modules.app_bar] # path = "boot:///app_bar.hbf" + +# [boot.limine.ableos.modules.test_abc] +# path = "boot:///test_abc.hbf" From adbf32d970c27adddd0c2c2005bd5939ae1accc0 Mon Sep 17 00:00:00 2001 From: koniifer Date: Fri, 8 Nov 2024 01:48:17 +0000 Subject: [PATCH 17/21] text editor work and misc changes --- Cargo.lock | 147 +---------- kernel/Cargo.toml | 4 - repbuild/Cargo.toml | 24 +- sysdata/libraries/stn/src/assets/sin_table | Bin 728 -> 0 bytes .../stn/src/assets/this-is-temporary | 0 sysdata/libraries/stn/src/math.hb | 2 - .../render_example/src/examples/text.hb | 232 ++++++++---------- sysdata/programs/render_example/src/main.hb | 2 +- sysdata/system_config.toml | 4 +- 9 files changed, 108 insertions(+), 307 deletions(-) delete mode 100644 sysdata/libraries/stn/src/assets/sin_table delete mode 100644 sysdata/libraries/stn/src/assets/this-is-temporary diff --git a/Cargo.lock b/Cargo.lock index fb7bb7e..7172fee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,21 +17,6 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anyhow" version = "1.0.92" @@ -110,26 +95,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -208,7 +173,6 @@ checksum = "05669f8e7e2d7badc545c513710f0eba09c2fbef683eb859fd79c46c355048e0" dependencies = [ "bitflags 1.3.2", "byteorder", - "chrono", "log", ] @@ -264,12 +228,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -281,34 +245,11 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" dependencies = [ "hbbytecode", ] -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "icu_collections" version = "1.5.0" @@ -458,15 +399,6 @@ dependencies = [ "hashbrown 0.15.1", ] -[[package]] -name = "js-sys" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "kernel" version = "0.2.0" @@ -567,15 +499,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "once_cell" version = "1.20.2" @@ -986,61 +909,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" - [[package]] name = "webpki-roots" version = "0.26.6" @@ -1050,15 +918,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.52.0" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 3f7e4bc..9cbe895 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -24,10 +24,6 @@ version = "0.3" default-features = false features = ["alloc", "nightly"] -# [dependencies.clparse] -# git = "https://git.ablecorp.us/ableos/ableos_userland" -# default-features = false - [dependencies.derive_more] version = "1" default-features = false diff --git a/repbuild/Cargo.toml b/repbuild/Cargo.toml index a681d45..2b7fc84 100644 --- a/repbuild/Cargo.toml +++ b/repbuild/Cargo.toml @@ -6,29 +6,15 @@ edition = "2021" [dependencies] str-reader = "0.1" derive_more = { version = "1", default-features = false, features = [ - "add", - "add_assign", - "constructor", "display", - "from", - "into", - "mul", - "mul_assign", - "not", - "sum", ] } error-stack = "0.5" -fatfs = "0.3" -toml = "0.8" -hblang = { git = "https://git.ablecorp.us/AbleOS/holey-bytes.git", features = [ +fatfs = { version = "0.3", default-features = false, features = [ "std", - # "opts", -], default-features = false } + "alloc", +] } +toml = "0.8" +hblang.git = "https://git.ablecorp.us/AbleOS/holey-bytes.git" log = "0.4" raw-cpuid = "11" ureq = { version = "2", default-features = false, features = ["tls"] } - -# [dependencies.reqwest] -# version = "0.12" -# default-features = false -# features = ["rustls-tls", "blocking"] diff --git a/sysdata/libraries/stn/src/assets/sin_table b/sysdata/libraries/stn/src/assets/sin_table deleted file mode 100644 index 0d1c9d8b1737b6f0e24ea094333419297756bd95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 728 zcmXZUJxc;m7(ijO4>L{8vNBCg%b+ocen8M)DA1|Jp`oFmKw6v{S{hthirQ@`h^R>l zY-tLjIS7KF7HPEMoVz^tyzk`(0-Tms`?y4cc&D{~IyHc8-5I2_L)g*15S<^!XWb3^ zegq$NJ3=R;c&pV>x~CU0dM%D0+DOoaF?`UCB>k&L<8*EUuXQ~|ziDlf?rLL-j!t7! zGimx=KS6wPHKhzh^l$h7_ vO1ru`&wgKT^he7J>??Y#_xh>vGVfM&QxCMMEq&5={n9@TF7jVkW17%^E6g+* diff --git a/sysdata/libraries/stn/src/assets/this-is-temporary b/sysdata/libraries/stn/src/assets/this-is-temporary deleted file mode 100644 index e69de29..0000000 diff --git a/sysdata/libraries/stn/src/math.hb b/sysdata/libraries/stn/src/math.hb index 9d0910d..64ba6b8 100644 --- a/sysdata/libraries/stn/src/math.hb +++ b/sysdata/libraries/stn/src/math.hb @@ -33,8 +33,6 @@ Vec2 := fn($Expr: type): type { /* source: https://github.com/baker-Xie/FastMath/blob/master/include/fast_math.h */ -// ! heavily broken, possibly due to compiler bug... or skill issues - PI := 3.14159265358979323846 SIN_TABLE := [f32].(0.0, 0.02454122852291229, 0.04906767432741801, 0.07356456359966743, 0.0980171403295606, 0.1224106751992162, 0.1467304744553617, 0.1709618887603012, 0.1950903220161282, 0.2191012401568698, 0.2429801799032639, 0.2667127574748984, 0.2902846772544623, 0.3136817403988915, 0.3368898533922201, 0.3598950365349881, 0.3826834323650898, 0.4052413140049899, 0.4275550934302821, 0.4496113296546065, 0.4713967368259976, 0.492898192229784, 0.5141027441932217, 0.5349976198870972, 0.5555702330196022, 0.5758081914178453, 0.5956993044924334, 0.6152315905806268, 0.6343932841636455, 0.6531728429537768, 0.6715589548470183, 0.6895405447370668, 0.7071067811865475, 0.7242470829514669, 0.7409511253549591, 0.7572088465064845, 0.773010453362737, 0.7883464276266062, 0.8032075314806448, 0.8175848131515837, 0.8314696123025452, 0.844853565249707, 0.8577286100002721, 0.8700869911087113, 0.8819212643483549, 0.8932243011955153, 0.9039892931234433, 0.9142097557035307, 0.9238795325112867, 0.9329927988347388, 0.9415440651830208, 0.9495281805930367, 0.9569403357322089, 0.9637760657954398, 0.970031253194544, 0.9757021300385286, 0.9807852804032304, 0.9852776423889412, 0.989176509964781, 0.99247953459871, 0.9951847266721968, 0.9972904566786902, 0.9987954562051724, 0.9996988186962042, 1.0, 0.9996988186962042, 0.9987954562051724, 0.9972904566786902, 0.9951847266721969, 0.99247953459871, 0.989176509964781, 0.9852776423889412, 0.9807852804032304, 0.9757021300385286, 0.970031253194544, 0.9637760657954398, 0.9569403357322089, 0.9495281805930367, 0.9415440651830208, 0.9329927988347388, 0.9238795325112867, 0.9142097557035307, 0.9039892931234434, 0.8932243011955152, 0.881921264348355, 0.8700869911087115, 0.8577286100002721, 0.8448535652497072, 0.8314696123025455, 0.8175848131515837, 0.8032075314806449, 0.7883464276266063, 0.7730104533627371, 0.7572088465064847, 0.740951125354959, 0.7242470829514669, 0.7071067811865476, 0.6895405447370671, 0.6715589548470186, 0.6531728429537766, 0.6343932841636455, 0.6152315905806269, 0.5956993044924335, 0.5758081914178454, 0.5555702330196022, 0.5349976198870972, 0.5141027441932218, 0.4928981922297841, 0.4713967368259979, 0.4496113296546069, 0.427555093430282, 0.4052413140049899, 0.3826834323650899, 0.3598950365349883, 0.3368898533922203, 0.3136817403988914, 0.2902846772544624, 0.2667127574748985, 0.2429801799032641, 0.21910124015687, 0.1950903220161286, 0.1709618887603012, 0.1467304744553618, 0.1224106751992163, 0.09801714032956083, 0.07356456359966773, 0.04906767432741797, 0.02454122852291233, 0.0, -0.02454122852291208, -0.04906767432741772, -0.0735645635996675, -0.09801714032956059, -0.1224106751992161, -0.1467304744553616, -0.170961888760301, -0.1950903220161284, -0.2191012401568698, -0.2429801799032638, -0.2667127574748983, -0.2902846772544621, -0.3136817403988912, -0.3368898533922201, -0.3598950365349881, -0.3826834323650897, -0.4052413140049897, -0.4275550934302818, -0.4496113296546067, -0.4713967368259976, -0.4928981922297839, -0.5141027441932216, -0.5349976198870969, -0.555570233019602, -0.5758081914178453, -0.5956993044924332, -0.6152315905806267, -0.6343932841636453, -0.6531728429537765, -0.6715589548470184, -0.6895405447370668, -0.7071067811865475, -0.7242470829514668, -0.7409511253549589, -0.7572088465064842, -0.7730104533627367, -0.7883464276266059, -0.8032075314806451, -0.8175848131515838, -0.8314696123025452, -0.844853565249707, -0.857728610000272, -0.8700869911087113, -0.8819212643483549, -0.8932243011955152, -0.9039892931234431, -0.9142097557035305, -0.9238795325112865, -0.932992798834739, -0.9415440651830208, -0.9495281805930367, -0.9569403357322088, -0.9637760657954398, -0.970031253194544, -0.9757021300385285, -0.9807852804032303, -0.9852776423889411, -0.9891765099647809, -0.9924795345987101, -0.9951847266721969, -0.9972904566786902, -0.9987954562051724, -0.9996988186962042, -1.0, -0.9996988186962042, -0.9987954562051724, -0.9972904566786902, -0.9951847266721969, -0.9924795345987101, -0.9891765099647809, -0.9852776423889412, -0.9807852804032304, -0.9757021300385286, -0.970031253194544, -0.96377606579544, -0.9569403357322089, -0.9495281805930368, -0.9415440651830209, -0.9329927988347391, -0.9238795325112866, -0.9142097557035306, -0.9039892931234433, -0.8932243011955153, -0.881921264348355, -0.8700869911087115, -0.8577286100002722, -0.8448535652497072, -0.8314696123025455, -0.817584813151584, -0.8032075314806453, -0.7883464276266061, -0.7730104533627369, -0.7572088465064846, -0.7409511253549591, -0.724247082951467, -0.7071067811865477, -0.6895405447370672, -0.6715589548470187, -0.6531728429537771, -0.6343932841636459, -0.6152315905806274, -0.5956993044924332, -0.5758081914178452, -0.5555702330196022, -0.5349976198870973, -0.5141027441932219, -0.4928981922297843, -0.4713967368259979, -0.449611329654607, -0.4275550934302825, -0.4052413140049904, -0.3826834323650904, -0.359895036534988, -0.33688985339222, -0.3136817403988915, -0.2902846772544625, -0.2667127574748986, -0.2429801799032642, -0.2191012401568702, -0.1950903220161287, -0.1709618887603018, -0.1467304744553624, -0.122410675199216, -0.09801714032956051, -0.07356456359966741, -0.04906767432741809, -0.02454122852291245) diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index d049034..67b9d9d 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -1,19 +1,42 @@ .{memory, log, string} := @use("../../../../libraries/stn/src/lib.hb") render := @use("../../../../libraries/render/src/lib.hb") -/* expected result: - trash notepad app, colours should change with r,g,b,w keys */ +/* expected result: almost-not-trash notepad app + very jank + ----------------- + features: + - basic keys + - holding support with DAS + - visible cursor + - shift key support +*/ psf := @embed("../../../../consolefonts/tamsyn/10x20r.psf") -send_byte := fn(byte: u8): u8 { - memory.outb(96, byte) - return memory.inb(96) +handle_char := fn(char: u8, cursor: ^u8, buf: ^u8): ^u8 { + if char == 0 { + return cursor + } + if char != 0x8 { + *cursor = char + return cursor + 1 + } else if char == 0xA { + *cursor = 32 + cursor += 1; + *cursor = 92 + return cursor + 1 + } else if cursor > buf { + cursor -= 1; + *cursor = 32 + return cursor + } + return cursor } example := fn(): void { screen := render.init(true) - font := render.text.font_from_psf2(@bitcast(&psf), false) + window := render.new_surface(480, 340) + font := render.text.font_from_psf2(@bitcast(&psf), true) msg := "ableboard (tm) (patent pending):\n\0" msg_len := string.length(msg) @@ -25,147 +48,86 @@ example := fn(): void { if font == null { return - } + }; - _ = send_byte(238) - log.info("PS/2 Driver Loaded\0") - if send_byte(238) == 238 { - log.info("PS/2 Keyboard Echoed\0") - } - if send_byte(244) == 250 { - log.info("Enabled scanning\0") - } + *cursor = 95 + draw(screen, window, font, buf); + *cursor = 0 - color := render.white + memory.outb(96, 238) + memory.outb(96, 238) + memory.outb(96, 244) + + prev_input := @as(u8, 0xFF) + current_key := @as(u8, 0) + holding_timer := @as(u32, 0) + is_shift_pressed := @as(bool, false) + + initial_delay := @as(u32, 50) + repeat_delay := @as(u32, 7) - prev_input := 250 loop { input := memory.inb(96) - if input != prev_input { - prev_input = input - char := map_keys(input) - if char != 0 { - if char == 0xA { - *cursor = 32 - cursor += 1; - *cursor = 92 - cursor += 1 - } else if char == 0x72 { - color = render.red - } else if char == 0x67 { - color = render.green - } else if char == 0x62 { - color = render.blue - } else if char == 0x77 { - color = render.white - } else if char == 0xE { - cursor -= 1; - *cursor = 32 + is_release := (input & 0x80) != 0 + key_code := input & 0x7F - continue - }; - *cursor = char - cursor += 1 + if input != prev_input { + if is_release { + if key_code == current_key { + current_key = 0 + holding_timer = 0 + } else if input == 0xAA | input == 0xB6 { + is_shift_pressed = false + } + } else { + if input == 0x2A | input == 0x36 { + is_shift_pressed = true + } else { + current_key = input + holding_timer = 1 + cursor = handle_char(map_keys(current_key, is_shift_pressed), cursor, buf) + } + } + prev_input = input + } + + if current_key != 0 & holding_timer > 0 { + holding_timer += 1 + + if holding_timer >= initial_delay { + cursor = handle_char(map_keys(current_key, is_shift_pressed), cursor, buf) + holding_timer = initial_delay - repeat_delay + } + }; + *cursor = 95 + draw(screen, window, font, buf); + *cursor = 0 + + if holding_timer > 0 & current_key != 0 { + if (memory.inb(96) & 0x80) != 0 { + current_key = 0 + holding_timer = 0 } } - render.clear(screen, render.black) - render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) - render.put_text(screen, font, .(0, 0), color, buf) - - render.sync(screen) } return } -map_keys := fn(inp: u8): u8 { - if inp == 0x29 { - return 0x7E - } else if inp == 0x2 { - return 0x31 - } else if inp == 0x3 { - return 0x32 - } else if inp == 0x4 { - return 0x33 - } else if inp == 0x5 { - return 0x34 - } else if inp == 0x6 { - return 0x35 - } else if inp == 0x7 { - return 0x36 - } else if inp == 0x8 { - return 0x37 - } else if inp == 0x9 { - return 0x38 - } else if inp == 0xA { - return 0x39 - } else if inp == 0xB { - return 0x30 - } else if inp == 0xC { - return 0x2D - } else if inp == 0xD { - return 0x3D - } else if inp == 0xE { - return 0xE - } else if inp == 0x1C { - return 0xA - } else if inp == 0x10 { - return 0x71 - } else if inp == 0x11 { - return 0x77 - } else if inp == 0x12 { - return 0x65 - } else if inp == 0x13 { - return 0x72 - } else if inp == 0x14 { - return 0x74 - } else if inp == 0x15 { - return 0x79 - } else if inp == 0x16 { - return 0x75 - } else if inp == 0x17 { - return 0x69 - } else if inp == 0x18 { - return 0x6F - } else if inp == 0x19 { - return 0x70 - } else if inp == 0x1E { - return 0x61 - } else if inp == 0x1F { - return 0x73 - } else if inp == 0x20 { - return 0x64 - } else if inp == 0x21 { - return 0x66 - } else if inp == 0x22 { - return 0x67 - } else if inp == 0x23 { - return 0x68 - } else if inp == 0x24 { - return 0x6A - } else if inp == 0x25 { - return 0x6B - } else if inp == 0x26 { - return 0x6C - } else if inp == 0x2C { - return 0x7A - } else if inp == 0x2D { - return 0x78 - } else if inp == 0x2E { - return 0x63 - } else if inp == 0x2F { - return 0x76 - } else if inp == 0x30 { - return 0x62 - } else if inp == 0x31 { - return 0x6E - } else if inp == 0x32 { - return 0x6D - } else if inp == 0x2B { - return 0x5C - } else if inp == 0x39 { - return 32 - } else { - return 0 +draw := fn(screen: render.Surface, window: render.Surface, font: render.text.Font, buf: ^u8): void { + render.clear(screen, render.black) + render.clear(window, render.black) + render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) + render.put_text(window, font, .(7, 7), render.gray, buf) + render.put_surface(screen, window, .(100, 100), false) + render.sync(screen) +} + +ps2_table := [u8].(0x0, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x8, 0x9, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0xA, 0x0, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x0, 0x5C, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x0, 0x2A, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x8, 0x9, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0xA, 0x0, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x0, 0x7C, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x0, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +map_keys := fn(scancode: u8, shift: bool): u8 { + if shift { + return ps2_table[scancode + 0x40] } + return ps2_table[scancode] } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/main.hb b/sysdata/programs/render_example/src/main.hb index 06644f7..77cb04c 100644 --- a/sysdata/programs/render_example/src/main.hb +++ b/sysdata/programs/render_example/src/main.hb @@ -1 +1 @@ -.{example: main} := @use("./examples/image.hb") \ No newline at end of file +.{example: main} := @use("./examples/text.hb") \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 082f361..34b3a7b 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -# [boot.limine.ableos.modules.render_example] -# path = "boot:///render_example.hbf" +[boot.limine.ableos.modules.render_example] +path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" From 444fcdb0c4d5468f1d65536daa0b478147760cc1 Mon Sep 17 00:00:00 2001 From: koniifer Date: Fri, 8 Nov 2024 13:47:24 +0000 Subject: [PATCH 18/21] more text editor work --- Cargo.lock | 18 +-- sysdata/libraries/render/src/software.hb | 4 +- sysdata/libraries/stn/src/memory.hb | 4 +- .../render_example/src/examples/text.hb | 124 ++++++++++-------- 4 files changed, 84 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7172fee..bdd8f05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anyhow" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "autocfg" @@ -82,9 +82,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.36" +version = "1.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" +checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" dependencies = [ "shlex", ] @@ -228,12 +228,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#a299bad75b068f565e6e10b6c3501a9422e283c4" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#a299bad75b068f565e6e10b6c3501a9422e283c4" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -245,7 +245,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#a299bad75b068f565e6e10b6c3501a9422e283c4" dependencies = [ "hbbytecode", ] @@ -428,9 +428,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "limine" diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 8f15843..7b9c6c1 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -4,7 +4,7 @@ .{Vec2} := math // safety: don't use before init() or you will get a memory access violation -framebuffer := memory.dangling(^Color) +framebuffer := memory.dangling(Color) Surface := struct { buf: ^Color, @@ -266,7 +266,7 @@ put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: current_char := str loop if *current_char == 0 break else { - glyph_data := memory.dangling(^u8) + glyph_data := memory.dangling(u8) if font.unicode != null { code_point := @as(uint, 0) diff --git a/sysdata/libraries/stn/src/memory.hb b/sysdata/libraries/stn/src/memory.hb index f133e63..f0b951a 100644 --- a/sysdata/libraries/stn/src/memory.hb +++ b/sysdata/libraries/stn/src/memory.hb @@ -2,8 +2,8 @@ PAGE_SIZE := 4096 MAX_ALLOC := 0xFF MAX_FREE := 0xFF -dangling := fn($Expr: type): Expr { - return @bitcast(@sizeof(Expr)) +dangling := fn($Expr: type): ^Expr { + return @bitcast(@alignof(Expr)) } calc_pages := fn($Expr: type, num: uint): uint { diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index 67b9d9d..71b4d50 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -13,46 +13,34 @@ render := @use("../../../../libraries/render/src/lib.hb") psf := @embed("../../../../consolefonts/tamsyn/10x20r.psf") -handle_char := fn(char: u8, cursor: ^u8, buf: ^u8): ^u8 { - if char == 0 { - return cursor - } - if char != 0x8 { - *cursor = char - return cursor + 1 - } else if char == 0xA { - *cursor = 32 - cursor += 1; - *cursor = 92 - return cursor + 1 - } else if cursor > buf { - cursor -= 1; - *cursor = 32 - return cursor - } - return cursor -} +is_shift_pressed := false +is_ctrl_pressed := false +$initial_delay := 50 +$repeat_delay := 7 example := fn(): void { screen := render.init(true) window := render.new_surface(480, 340) - font := render.text.font_from_psf2(@bitcast(&psf), true) + font := render.text.font_from_psf2(@bitcast(&psf), false) - msg := "ableboard (tm) (patent pending):\n\0" + if font == null { + return + } + + msg := "sticky note:\n\0" msg_len := string.length(msg) buf := memory.alloc(u8, 4096) - @inline(memory.copy, u8, msg, buf, msg_len) - cursor := buf + msg_len + bottom := buf + msg_len - if font == null { - return - }; + @inline(memory.copy, u8, msg, buf, msg_len) + cursor := buf + msg_len; *cursor = 95 - draw(screen, window, font, buf); - *cursor = 0 + draw_window(window, font, buf) + draw_screen(screen, window); + *cursor = 32 memory.outb(96, 238) memory.outb(96, 238) @@ -60,32 +48,29 @@ example := fn(): void { prev_input := @as(u8, 0xFF) current_key := @as(u8, 0) - holding_timer := @as(u32, 0) - is_shift_pressed := @as(bool, false) - - initial_delay := @as(u32, 50) - repeat_delay := @as(u32, 7) + holding_timer := 0 loop { input := memory.inb(96) - is_release := (input & 0x80) != 0 - key_code := input & 0x7F - if input != prev_input { - if is_release { - if key_code == current_key { + if (input & 0x80) != 0 { + if (input & 0x7F) == current_key { current_key = 0 holding_timer = 0 } else if input == 0xAA | input == 0xB6 { is_shift_pressed = false + } else if input == 0x9D { + is_ctrl_pressed = false } } else { if input == 0x2A | input == 0x36 { is_shift_pressed = true + } else if input == 0x1D { + is_ctrl_pressed = true } else { current_key = input holding_timer = 1 - cursor = handle_char(map_keys(current_key, is_shift_pressed), cursor, buf) + cursor = handle_char(map_keys(current_key), cursor, bottom) } } prev_input = input @@ -95,13 +80,14 @@ example := fn(): void { holding_timer += 1 if holding_timer >= initial_delay { - cursor = handle_char(map_keys(current_key, is_shift_pressed), cursor, buf) + cursor = handle_char(map_keys(current_key), cursor, bottom) holding_timer = initial_delay - repeat_delay } }; *cursor = 95 - draw(screen, window, font, buf); - *cursor = 0 + draw_window(window, font, buf) + draw_screen(screen, window); + *cursor = 32 if holding_timer > 0 & current_key != 0 { if (memory.inb(96) & 0x80) != 0 { @@ -114,20 +100,52 @@ example := fn(): void { return } -draw := fn(screen: render.Surface, window: render.Surface, font: render.text.Font, buf: ^u8): void { - render.clear(screen, render.black) - render.clear(window, render.black) - render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) - render.put_text(window, font, .(7, 7), render.gray, buf) - render.put_surface(screen, window, .(100, 100), false) - render.sync(screen) +handle_char := fn(char: u8, cursor: ^u8, bottom: ^u8): ^u8 { + if char == 0 { + return cursor + } + if is_ctrl_pressed & char == 48 { + cursor = bottom + } else if char != 0x8 { + *cursor = char + return cursor + 1 + } else if char == 0xA { + *cursor = 32 + cursor += 1; + *cursor = 92 + return cursor + 1 + } else if cursor > bottom { + cursor -= 1; + *cursor = 32 + return cursor + } + return cursor } -ps2_table := [u8].(0x0, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x8, 0x9, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0xA, 0x0, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x0, 0x5C, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x0, 0x2A, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x8, 0x9, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0xA, 0x0, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x0, 0x7C, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x0, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - -map_keys := fn(scancode: u8, shift: bool): u8 { - if shift { +map_keys := fn(scancode: u8): u8 { + if is_shift_pressed { return ps2_table[scancode + 0x40] } return ps2_table[scancode] +} + +ps2_table := [u8].(0x0, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x8, 0x9, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0xA, 0x0, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x0, 0x5C, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x0, 0x2A, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x8, 0x9, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0xA, 0x0, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x0, 0x7C, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x0, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) +$padding := 7 + +draw_window := fn(window: render.Surface, font: render.text.Font, buf: ^u8): void { + render.clear(window, render.light_yellow) + line := font.height + font.line_gap + padding - 1 + render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.black) + loop if line >= window.height break else { + render.put_hline(window, line, padding, window.width - padding, render.yellow) + line += font.height + font.line_gap + } + + render.put_text(window, font, .(padding, padding), render.black, buf) +} + +draw_screen := fn(screen: render.Surface, window: render.Surface): void { + render.clear(screen, render.light_blue) + render.put_surface(screen, window, .(100, 100), false) + render.sync(screen) } \ No newline at end of file From 2676bd7b62f1f43c67aeb450769ff1d9e2e5aaff Mon Sep 17 00:00:00 2001 From: Jakub Doka Date: Fri, 8 Nov 2024 15:04:10 +0100 Subject: [PATCH 19/21] fixing error messages --- repbuild/src/dev.rs | 31 ++++++++++--------- repbuild/src/main.rs | 21 ++++++++++++- .../render_example/src/examples/text.hb | 2 +- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/repbuild/src/dev.rs b/repbuild/src/dev.rs index f93edd1..91c4644 100644 --- a/repbuild/src/dev.rs +++ b/repbuild/src/dev.rs @@ -62,12 +62,11 @@ impl Package { build_cmd, } } - pub fn build(&self) -> std::io::Result<()> { + pub fn build(&self, out: &mut Vec) -> std::io::Result<()> { if self.binaries.contains(&"hblang".to_string()) { let file = self.build_cmd.split_ascii_whitespace().last().unwrap(); let path = format!("sysdata/programs/{}/{}", self.name, file); - let mut bytes = Vec::new(); // compile here hblang::run_compiler( @@ -76,15 +75,7 @@ impl Package { fmt: true, ..Default::default() }, - &mut bytes, - )?; - - hblang::run_compiler( - &path, - Options { - ..Default::default() - }, - &mut bytes, + out, )?; match std::fs::create_dir("target/programs") { @@ -92,17 +83,27 @@ impl Package { Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => (), Err(e) => panic!("{}", e), } - std::fs::write(format!("target/programs/{}.hbf", self.name), &bytes).unwrap(); - bytes.clear(); + + hblang::run_compiler( + &path, + Options { + ..Default::default() + }, + out, + )?; + std::fs::write(format!("target/programs/{}.hbf", self.name), &out)?; + out.clear(); + hblang::run_compiler( &path, Options { dump_asm: true, ..Default::default() }, - &mut bytes, + out, )?; - std::fs::write(format!("target/programs/{}.hba", self.name), &bytes).unwrap(); + std::fs::write(format!("target/programs/{}.hba", self.name), &out)?; + out.clear(); } Ok(()) } diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 19f2d38..898d1f7 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -1,6 +1,7 @@ mod dev; use { + core::fmt::Write as _, derive_more::Display, dev::Package, error_stack::{bail, report, Context, Report, Result, ResultExt}, @@ -204,6 +205,9 @@ TERM_BACKDROP={} let modules = value.get_mut("modules").unwrap().as_table_mut().unwrap(); // let mut real_modules = modules.clone(); + let mut errors = String::new(); + let mut out = Vec::new(); + modules .into_iter() .map(|(_, value)| -> Result<(), io::Error> { @@ -218,11 +222,26 @@ TERM_BACKDROP={} let p = Package::load_from_file( format!("sysdata/programs/{}/meta.toml", path).to_owned(), ); - p.build()?; + match p.build(&mut out) { + Ok(()) => {} + Err(_) => { + writeln!(errors, "========= while compiling {} =========", path) + .unwrap(); + errors.push_str(core::str::from_utf8(&out).expect("no")); + out.clear(); + } + } } Ok(()) }) .for_each(drop); + + if !errors.is_empty() { + writeln!(errors, "!!! STOPPING DUE TO PREVIOUS ERRORS !!!"); + std::eprint!("{errors}"); + continue; + } + modules.into_iter().for_each(|(_key, value)| { if value.is_table() { let path = value.get("path").expect("You must have `path` as a value"); diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index 71b4d50..2f2c299 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -148,4 +148,4 @@ draw_screen := fn(screen: render.Surface, window: render.Surface): void { render.clear(screen, render.light_blue) render.put_surface(screen, window, .(100, 100), false) render.sync(screen) -} \ No newline at end of file +} From d2b5f0951125aee1ff063eb07cad6781023c3bb9 Mon Sep 17 00:00:00 2001 From: Able Date: Fri, 8 Nov 2024 10:16:24 -0600 Subject: [PATCH 20/21] changes + lui examples --- repbuild/src/main.rs | 2 +- .../libraries/horizon_api/examples/label.lui | 1 + .../horizon_api/examples/vertical.lui | 3 ++ sysdata/libraries/horizon_api/src/ui.hb | 7 +-- .../horizon_api/src/widgets/widgets.hb | 1 + sysdata/libraries/intouch/src/lib.hb | 5 +- sysdata/programs/horizon/src/main.hb | 46 +++++++++++++------ sysdata/programs/ps2_mouse_driver/src/main.hb | 12 +++-- sysdata/system_config.toml | 12 ++--- 9 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 sysdata/libraries/horizon_api/examples/label.lui create mode 100644 sysdata/libraries/horizon_api/examples/vertical.lui diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 898d1f7..05522e2 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -237,7 +237,7 @@ TERM_BACKDROP={} .for_each(drop); if !errors.is_empty() { - writeln!(errors, "!!! STOPPING DUE TO PREVIOUS ERRORS !!!"); + let _ = writeln!(errors, "!!! STOPPING DUE TO PREVIOUS ERRORS !!!"); std::eprint!("{errors}"); continue; } diff --git a/sysdata/libraries/horizon_api/examples/label.lui b/sysdata/libraries/horizon_api/examples/label.lui new file mode 100644 index 0000000..6db625c --- /dev/null +++ b/sysdata/libraries/horizon_api/examples/label.lui @@ -0,0 +1 @@ +(label "hello") diff --git a/sysdata/libraries/horizon_api/examples/vertical.lui b/sysdata/libraries/horizon_api/examples/vertical.lui new file mode 100644 index 0000000..ee6a401 --- /dev/null +++ b/sysdata/libraries/horizon_api/examples/vertical.lui @@ -0,0 +1,3 @@ +(vertical + (label "hello") + (label "hello" color:red)) \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/ui.hb b/sysdata/libraries/horizon_api/src/ui.hb index 93fae2d..e6512a1 100644 --- a/sysdata/libraries/horizon_api/src/ui.hb +++ b/sysdata/libraries/horizon_api/src/ui.hb @@ -6,11 +6,8 @@ render := @use("../../../libraries/render/src/lib.hb"); .{Surface} := render; .{Font} := render.text -UI := struct { - raw: ^u8, - raw_length: uint, - is_dirty: bool, - surface: Surface, +UI := struct {raw: ^u8, raw_length: uint, is_dirty: bool, surface: Surface, // Each child has their WidgetType as their first byte +// children: ^^u8, } render_ui := fn(surface: Surface, ui: UI): void { diff --git a/sysdata/libraries/horizon_api/src/widgets/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/widgets.hb index 2cda29a..360b994 100644 --- a/sysdata/libraries/horizon_api/src/widgets/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets/widgets.hb @@ -23,6 +23,7 @@ Vertical := packed struct { magic: uint, // array of children, idk // use a vec or linked list or whatever + children: ^^u8, } diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index b6d751c..ebfde1b 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -27,12 +27,13 @@ recieve_mouse_event := fn(): ?MouseEvent { // Read out of the Mouse buffer here buffer.recv(MouseEvent, buf_id, mem_page) if *mem_page != 0 { + log.info("Mouse events\0") dx := *mem_page dy := *mem_page + 1 mevent := MouseEvent.(dx, dy, 0, 0, 0) return mevent } - // log.warn("No mouse events\0") - return null + // log.error("No mouse events\0") + return MouseEvent.(0, 0, 0, 0, 0) } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 8afd4f6..33d3185 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -9,6 +9,7 @@ horizon_api := @use("../../../libraries/horizon_api/src/lib.hb"); render := @use("../../../libraries/render/src/lib.hb"); .{Surface} := render; .{Font} := render.text + intouch := @use("../../../libraries/intouch/src/lib.hb") Window := struct { @@ -42,30 +43,45 @@ main := fn(): int { mouse_x := 0 mouse_y := 0 text_label := new_label("Hi\0") - widgets := "()\0" - ui := sexpr_parser(widgets) + // widgets := "()\0" + // ui := sexpr_parser(widgets) + + mouse_event := intouch.recieve_mouse_event() + + if mouse_event == null { + log.warn("null\0") + } else { + log.warn("not null\0") + } loop { // Clear the screen render.clear(screen, render.black) // TODO: Read the window buffer here - // { - // ret := buffer.recv([u8; 4096], win_buff, mem_buf) - // // for some reason this null check causes the compiler to spin forever - // // if ret == null { - // // log.info("No messages\0") - // // } else { - // // log.info("Handle Messages\0") - // // } - // } - { - // get input events from drivers via intouch - // key_event := intouch.recieve_key_event() + ret := buffer.recv([u8; 4096], win_buff, mem_buf) + // for some reason this null check causes the compiler to spin forever + // if ret == null { + // log.info("No messages\0") + // } else { + // log.info("Handle Messages\0") + // } + } + + // get input events from drivers via intouch + // key_event := intouch.recieve_key_event(); + // log.info("before mouse event check\0"); + { + // Note: MLokis, this inline halts the compiler forever + // mouse_event := @inline(intouch.recieve_mouse_event) + // Note: MLokis, this function returns null unless the mouse is moving mouse_event := intouch.recieve_mouse_event() + // + if mouse_event != null { - log.info("Mouse event") + log.warn("Mouse event recieved\0") + mouse_x += mouse_event.x_change mouse_y += mouse_event.y_change set_label_text(text_label, "Mouse Moved\0") diff --git a/sysdata/programs/ps2_mouse_driver/src/main.hb b/sysdata/programs/ps2_mouse_driver/src/main.hb index 82c849e..1f66ada 100644 --- a/sysdata/programs/ps2_mouse_driver/src/main.hb +++ b/sysdata/programs/ps2_mouse_driver/src/main.hb @@ -13,13 +13,13 @@ Button4 := Button.(8) Button5 := Button.(16) mouse_moved := fn(delta: Vec2(i9)): void { - log.info("Mouse movement.\0") + // log.info("Mouse movement.\0") } button_event := fn(button: Button, pressed: bool): void { if pressed { - log.info("Mouse-button pressed.\0") + // log.info("Mouse-button pressed.\0") } else { - log.info("Mouse-button released.\0") + // log.info("Mouse-button released.\0") } } @@ -132,7 +132,7 @@ main := fn(): int { button_states ^= changes - log.info(string.display_int(status, format_page, 10)) + // log.info(string.display_int(status, format_page, 10)) dx := i9.(false, 0) dy := i9.(false, 0) @@ -144,7 +144,9 @@ main := fn(): int { dy.sign = (status & 0x20) == 0 if dy.value != 0 & dx.value != 0 { - event := MouseEvent.(dx.value, dy.value, 0, 0, 0) + y_change := dy.value + x_change := dx.value + event := MouseEvent.(x_change, y_change, 0, 0, 0) buffer.write(MouseEvent, &event, mouse_buffer) // mouse_moved(.(dx, dy)) diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 34b3a7b..dfe4619 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -[boot.limine.ableos.modules.render_example] -path = "boot:///render_example.hbf" +# [boot.limine.ableos.modules.render_example] +# path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -37,8 +37,8 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver_test] # path = "boot:///serial_driver_test.hbf" -# [boot.limine.ableos.modules.horizon] -# path = "boot:///horizon.hbf" +[boot.limine.ableos.modules.horizon] +path = "boot:///horizon.hbf" # [boot.limine.ableos.modules.horizon_testing_program] # path = "boot:///horizon_testing_program.hbf" @@ -58,8 +58,8 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -# [boot.limine.ableos.modules.ps2_mouse_driver] -# path = "boot:///ps2_mouse_driver.hbf" +[boot.limine.ableos.modules.ps2_mouse_driver] +path = "boot:///ps2_mouse_driver.hbf" # [boot.limine.ableos.modules.app_bar] # path = "boot:///app_bar.hbf" From 818bcb458f2d4073d6b5aca1a59fc8394c62ebb3 Mon Sep 17 00:00:00 2001 From: Able Date: Fri, 8 Nov 2024 10:29:06 -0600 Subject: [PATCH 21/21] widgets --- sysdata/libraries/horizon_api/examples/horizontal.lui | 4 ++++ sysdata/libraries/horizon_api/src/widgets/image.hb | 5 +++++ sysdata/libraries/horizon_api/src/widgets/widget_types.hb | 5 ++++- sysdata/libraries/horizon_api/src/widgets/widgets.hb | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 sysdata/libraries/horizon_api/examples/horizontal.lui create mode 100644 sysdata/libraries/horizon_api/src/widgets/image.hb diff --git a/sysdata/libraries/horizon_api/examples/horizontal.lui b/sysdata/libraries/horizon_api/examples/horizontal.lui new file mode 100644 index 0000000..8954780 --- /dev/null +++ b/sysdata/libraries/horizon_api/examples/horizontal.lui @@ -0,0 +1,4 @@ +(horizontal + spacing : 10 + (label "hi") + (label "goodbye")) \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/image.hb b/sysdata/libraries/horizon_api/src/widgets/image.hb new file mode 100644 index 0000000..faa0d99 --- /dev/null +++ b/sysdata/libraries/horizon_api/src/widgets/image.hb @@ -0,0 +1,5 @@ +Image := struct { + magic: uint, + is_dirty: bool, + surface: Surface, +} \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widget_types.hb b/sysdata/libraries/horizon_api/src/widgets/widget_types.hb index 9755ec8..97ddcb8 100644 --- a/sysdata/libraries/horizon_api/src/widgets/widget_types.hb +++ b/sysdata/libraries/horizon_api/src/widgets/widget_types.hb @@ -1,4 +1,7 @@ +NoWidget := 0 + VerticalWidgetType := 1 HorizontalWidgetType := 2 -LabelWidgetType := 3 \ No newline at end of file +LabelWidgetType := 3 +ImageWidgetType := 4 \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/widgets.hb index 360b994..cb15cbb 100644 --- a/sysdata/libraries/horizon_api/src/widgets/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets/widgets.hb @@ -11,6 +11,7 @@ render := @use("../../../../libraries/render/src/lib.hb"); widget_types := @use("widget_types.hb") label := @use("label.hb") +image := @use("image.hb") Size := struct { min_width: int,