odin-libui

Odin bindings to libui
Log | Files | Refs | Submodules | README | LICENSE

commit 4f90f930522d920c913a079b016db8617e44d61e
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Mon, 28 Oct 2024 19:00:42 +0800

initial: odin bindings for libui

Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>

Diffstat:
A.gitmodules | 3+++
ALICENSE | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 16++++++++++++++++
Aexamples/drawtext/main.odin | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agenerator/gen.odin | 292+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agenerator/main.odin | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agenerator/parse.odin | 414+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibui | 1+
Alibui.odin | 645+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aodinfmt.json | 6++++++
Aols.json | 11+++++++++++
11 files changed, 1614 insertions(+), 0 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libui"] + path = libui + url = https://github.com/andlabs/libui.git diff --git a/LICENSE b/LICENSE @@ -0,0 +1,61 @@ +This project is dual-licensed under the UNLICENSE or +the MIT license with the SPDX identifier: + +SPDX-License-Identifier: Unlicense OR MIT + +You may use the project under the terms of either license. + +Both licenses are reproduced below. + +---- +The MIT License (MIT) + +Copyright (c) 2023 Jack Mordaunt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--- + +--- +The UNLICENSE + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <https://unlicense.org/> +--- diff --git a/README.md b/README.md @@ -0,0 +1,16 @@ +# odin-libui + +This package contains bindings generated for [libui](https://github.com/andlabs/libui). + +## Build + +libui must be available to the Odin compiler. You need to build and install `libui.a`. + +## Generator + +Regenerate bindings with `odin run ./generator`. + +## Notes + +- widgets structures are opaque since their implementation is contained in platform specific code +- code generator uses ad-hoc C parsing logic that is in no way appropriate for general use as it is tuned to the content of ui.h diff --git a/examples/drawtext/main.odin b/examples/drawtext/main.odin @@ -0,0 +1,102 @@ +package main + +import "core:c" +import "core:log" + +import libui "../../" + +main :: proc() { + options: libui.InitOptions + + if err := libui.Init(&options); err != "" { + defer libui.FreeInitError(err) + log.errorf("init error: %v", err) + return + } + + window := libui.NewWindow("drawtext", 640, 480, 1) + libui.WindowSetMargined(window, 1) + + libui.WindowOnClosing(window, on_closing, nil) + libui.OnShouldQuit(should_quit, window) + + box := libui.NewHorizontalBox() + libui.BoxSetPadded(box, 1) + libui.WindowSetChild(window, auto_cast box) + + control_panel := libui.NewVerticalBox() + libui.BoxSetPadded(control_panel, 1) + libui.BoxAppend(box, auto_cast control_panel, 1) + + handler := libui.AreaHandler { + Draw = handler_draw, + MouseEvent = handler_mouse_event, + MouseCrossed = handler_mouse_crossed, + DragBroken = handler_drag_broken, + KeyEvent = handler_key_event, + } + + area := libui.NewArea(&handler) + libui.BoxAppend(box, auto_cast area, 1) + + libui.BoxAppend(control_panel, auto_cast libui.NewButton("one"), 0) + libui.BoxAppend(control_panel, auto_cast libui.NewButton("two"), 0) + libui.BoxAppend(control_panel, auto_cast libui.NewButton("three"), 0) + libui.BoxAppend(control_panel, auto_cast libui.NewButton("four"), 0) + libui.BoxAppend(control_panel, auto_cast libui.NewButton("five"), 0) + + libui.ControlShow(auto_cast window) + libui.Main() + libui.Uninit() +} + +on_closing :: proc "c" (w: libui.Window, data: rawptr) -> c.int { + libui.ControlDestroy(auto_cast w) + libui.Quit() + return 0 +} + +should_quit :: proc "c" (data: rawptr) -> c.int { + libui.ControlDestroy(auto_cast data) + return 1 +} + +handler_draw :: proc(handler: ^libui.AreaHandler, area: libui.Area, p: ^libui.AreaDrawParams) { + params: libui.DrawTextLayoutParams + default_font: libui.FontDescriptor + + text := libui.NewAttributedString("The quick brown fox jumps over the lazy dog.") + defer libui.FreeAttributedString(text) + + params.String = text + params.Width = p.AreaWidth + params.Align = .DrawTextAlignCenter + + params.DefaultFont = &default_font + params.DefaultFont.Family = "Courier New" + params.DefaultFont.Size = 18.0 + params.DefaultFont.Weight = .TextWeightNormal + params.DefaultFont.Stretch = .TextStretchNormal + params.DefaultFont.Italic = .TextItalicNormal + + text_layout := libui.DrawNewTextLayout(&params) + defer libui.DrawFreeTextLayout(text_layout) + + libui.DrawText(p.Context, text_layout, 0, 0) +} + +handler_mouse_event :: proc(_: ^libui.AreaHandler, _: libui.Area, _: ^libui.AreaMouseEvent) { + +} + +handler_mouse_crossed :: proc(_: ^libui.AreaHandler, _: libui.Area, left: c.int) { + +} +handler_drag_broken :: proc(_: ^libui.AreaHandler, _: libui.Area) { + +} + +handler_key_event :: proc(_: ^libui.AreaHandler, _: libui.Area, _: ^libui.AreaKeyEvent) -> c.int { + return 0 +} + diff --git a/generator/gen.odin b/generator/gen.odin @@ -0,0 +1,292 @@ +package main + +import "core:bytes" +import "core:fmt" +import "core:io" +import "core:strings" + + +// generate the odin bindings. +generate :: proc(wr: io.Writer, p: Parser) { + fmt.wprintf(wr, "// GENERATED CODE\n") + fmt.wprintf(wr, "package libui\n\n") + + fmt.wprintln(wr, `import libc "core:c"`) + fmt.wprintln(wr, import_preamble) + + // To handle the opaque (rawptr) types properly we want to avoid declaring + // them with a ^, since that would make them a pointer to a rawptr. + // However, we can only know if a type should be opaque if we haven't + // encountered any definition for it (struct, enum, function). Thus as + // quick and dirty hack, this logic will perform a dry run in order to + // resolve opaque types. The second and true run then can omit the + // superfluous caret appropriately. + + dry_run: bytes.Buffer + write_everything(bytes.buffer_to_stream(&dry_run), p) + + fmt.wprintln(wr, "// Unresolved symbols; typically opaque widget pointers") + + outer: for type_name in idents { + for s in p.struct_defs { + if s.name == type_name { + continue outer + } + } + for e in p.enum_defs { + if e.name == type_name { + continue outer + } + } + for f in p.function_defs { + if f.name == type_name { + continue outer + } + } + opaque_types[type_name] = {} + fmt.wprintf(wr, "%s :: rawptr\n", ident(type_name)) + } + + fmt.wprintln(wr) + + write_everything(wr, p) +} + +write_everything :: proc(wr: io.Writer, p: Parser) { + fmt.println("writing edge-cases") + + fmt.wprintln(wr, "// Hardcoded #defines ") + fmt.wprintf(wr, "DrawDefaultMiterLimit :: 10.0\n") + fmt.wprintln(wr) + + fmt.wprintln(wr, "// Enum definitions") + + /* + Enum definitions. + */ + + for enum_def in p.enum_defs { + fmt.println("writing", enum_def.name) + fmt.wprintf(wr, "%s :: enum {{\n", ident(enum_def.name)) + for variant in enum_def.variants { + fmt.wprintf(wr, "\t%s", ident(variant.name)) + if n, ok := variant.value.?; ok { + fmt.wprintf(wr, " = %d", n) + } + fmt.wprintf(wr, ",\n") + } + fmt.wprintf(wr, "}\n\n") + } + + /* + Struct definitions. + */ + + fmt.wprintln(wr, "// Struct definitions") + + // Explit case for tm (libc time struct) + fmt.wprintln(wr, broken_down_time_def) + + for struct_def in p.struct_defs { + fmt.println("writing", struct_def.name) + fmt.wprintf(wr, "%s :: struct {{\n", ident(struct_def.name)) + for field in struct_def.fields { + fn, fn_ok := field.function.? + if fn_ok { + fmt.wprintf(wr, "\t%s: %sproc", ident(fn.name), field.is_pointer ? "^" : "") + write_procedure_args(wr, fn.args[:]) + if fn.return_type != "" && !(fn.return_type == "void" && !fn.return_is_pointer) { + fmt.wprintf(wr, " -> %s", ident(fn.return_type, fn.return_is_pointer, true)) + } + } else { + fmt.wprintf(wr, "\t%s: %s", ident(field.name), ident(field.type, field.is_pointer)) + } + fmt.wprintf(wr, ",\n") + } + fmt.wprintf(wr, "}\n\n") + } + + /* + Procedure types are named proc types that are not part of the binding. + */ + + fmt.wprintln(wr, "// Procedure types") + + for function_def in p.function_defs { + if !function_def.is_type do continue + fmt.println("writing", function_def.name) + fmt.wprintf( + wr, + "%s :: %s proc", + ident(function_def.name), + function_def.is_type ? "#type" : "", + ) + write_procedure_args(wr, function_def.args[:]) + if function_def.return_type != "" && + !(function_def.return_type == "void" && !function_def.return_is_pointer) { + fmt.wprintf( + wr, + " -> %s", + ident(function_def.return_type, function_def.return_is_pointer, true), + ) + } + if !function_def.is_type { + fmt.wprintf(wr, " ---") + } + fmt.wprintf(wr, "\n") + } + + fmt.wprintf(wr, "\n") + + /* + Procedure definitions. These procs are implemented by the library. + */ + + fmt.wprintln(wr, "// Procedure definitions") + + // TODO: maybe check for system installed version of libui.a? + // TODO: change to .lib for Windows with "where" clause + fmt.wprintln(wr, `@(default_calling_convention = "stdcall", link_prefix = "ui")`) + fmt.wprintf(wr, "foreign lib {{\n") + + for function_def in p.function_defs { + if function_def.is_type do continue + fmt.println("writing", function_def.name) + fmt.wprintf( + wr, + "\t%s :: %s proc", + ident(function_def.name), + function_def.is_type ? "#type" : "", + ) + write_procedure_args(wr, function_def.args[:]) + if function_def.return_type != "" && + !(function_def.return_type == "void" && !function_def.return_is_pointer) { + fmt.wprintf( + wr, + " -> %s", + ident(function_def.return_type, function_def.return_is_pointer, true), + ) + } + if !function_def.is_type { + fmt.wprintf(wr, " ---") + } + fmt.wprintf(wr, "\n") + } + + fmt.wprintf(wr, "}\n") +} + +write_procedure_args :: proc(wr: io.Writer, args: []Function_Argument) { + fmt.wprintf(wr, "(") + for arg, ii in args { + fn, fn_ok := arg.function.? + if fn_ok { + fmt.wprintf(wr, "%s: %sproc", ident(fn.name), arg.is_pointer ? "^" : "") + write_procedure_args(wr, fn.args[:]) + if fn.return_type != "" && !(fn.return_type == "void" && !fn.return_is_pointer) { + fmt.wprintf(wr, " -> %s", ident(fn.return_type, fn.return_is_pointer)) + } + } else { + if !(arg.type == "void" && !arg.is_pointer) { + fmt.wprintf( + wr, + "%s: %s", + arg.name == "" ? "_" : ident(arg.name), + ident(arg.type, arg.is_pointer, true), + ) + } + } + if ii < len(args) - 1 { + fmt.wprintf(wr, ", ") + } + } + fmt.wprintf(wr, ")") +} + +// opaque_types is a map of types that have no known definition. +// Such types will be defined as rawptr. +opaque_types := map[string]struct {}{} + +// idents is a map of encoutered identifiers. Compared against +// known defintions to figure out which types are opaque. +idents := map[string]struct {}{} + +// ident strips off the "ui" prefix and does name replacements. +// +// opaque types (void pointers) must not be prefixed with ^ otherwise +// they become pointer-to-rawptr which is not correct. +ident :: proc(text: string, is_pointer := false, is_type := false) -> string { + text := text + + b := strings.Builder{} + strings.builder_init_none(&b, context.temp_allocator) + + if is_pointer { + if _, ok := opaque_types[text]; !ok { + strings.write_string(&b, "^") + } + } + + switch text { + case "int": + strings.write_string(&b, "libc.int") + case "uint32_t": + strings.write_string(&b, "libc.uint32_t") + case "uint64_t": + strings.write_string(&b, "libc.uint64_t") + case "uintptr_t": + strings.write_string(&b, "libc.uintptr_t") + case "size_t": + strings.write_string(&b, "libc.size_t") + case "void": + if is_pointer { + strings.builder_reset(&b) + strings.write_string(&b, "rawptr") + } else { + return "" + } + case "double": + strings.write_string(&b, "libc.double") + case "char": + if is_pointer { + strings.builder_reset(&b) + strings.write_string(&b, "cstring") + } else { + strings.write_string(&b, "byte") + } + case: + if is_type && text != "tm" && text != "int" { + idents[text] = {} + } + if strings.has_prefix(text, "ui") { + text = text[2:] + } + strings.write_string(&b, text) + } + + return strings.to_string(b) +} + +broken_down_time_def :: ` +// ISO C broken-down time structure. +tm :: struct { + tm_sec: int, + tm_min: int, + tm_hour: int, + tm_mday: int, + tm_mon: int, + tm_year: int, + tm_wday: int, + tm_yday: int, + tm_isdst: int, +} +` + +import_preamble :: ` +when ODIN_OS == .Linux { + @(extra_linker_flags="-lgtk-3 -lgdk-3 -lz -lharfbuzz -lpangocairo-1.0 -lpango-1.0 -latk-1.0 -lcairo -lcairo-gobject -lgdk_pixbuf-2.0 -lgio-2.0 -lglib-2.0 -lgobject-2.0") + foreign import lib "system:ui" +} else { + foreign import lib "system:ui" +} +` diff --git a/generator/main.odin b/generator/main.odin @@ -0,0 +1,63 @@ +// Package generator implements logic for generating bindings for libui. +// +// This package has rough parsing logic that is tuned for the C syntax +// found in ui.h, and does not come even close to being a generally-useful +// C parser. +// +// Specfically we do no type checking and we minimal expression parsing. +package main + +import "core:bufio" +import "core:fmt" +import "core:log" +import "core:mem/virtual" +import "core:os" + +main :: proc() { + context.logger = log.create_console_logger() + + dir := os.get_current_directory() + + f, f_err := os.open("libui/ui.h") + if f_err != os.ERROR_NONE { + log.errorf("failed opening header file: %w", f_err) + return + } + + defer os.close(f) + + by, by_err := os.read_entire_file_or_err(f) + if by_err != os.ERROR_NONE { + log.errorf("failed reading header file: %w", by_err) + return + } + + defer delete(by) + + arena: virtual.Arena + if err := virtual.arena_init_growing(&arena); err != nil { + panic(fmt.tprintf("creating growing virtual memory arena: %w", err)) + } + + context.allocator = virtual.arena_allocator(&arena) + defer free_all() + + p: Parser + parser_parse(&p, by) + + os.remove("libui.odin") + + out_f, out_f_err := os.open("libui.odin", os.O_CREATE | os.O_RDWR, 0o644) + if out_f_err != os.ERROR_NONE { + log.errorf("failed opening output file: %w", out_f_err) + return + } + + defer os.close(out_f) + + buf_writer: bufio.Writer + bufio.writer_init(&buf_writer, os.stream_from_handle(out_f)) + + generate(bufio.writer_to_writer(&buf_writer), p) +} + diff --git a/generator/parse.odin b/generator/parse.odin @@ -0,0 +1,414 @@ +package main + +import "core:bytes" +import "core:fmt" +import "core:strconv" +import "core:strings" +import "core:unicode" + +Struct :: struct { + name: string, + fields: [dynamic]Struct_Field, +} + +Struct_Field :: struct { + type: string, + name: string, + is_pointer: bool, + // Because we don't do generic expression parsing we must explicitly + // allow for function pointers. + function: Maybe(Function), +} + +Function :: struct { + name: string, + return_type: string, + return_is_pointer: bool, + is_type: bool, + args: [dynamic]Function_Argument, +} + +Function_Argument :: struct { + type: string, + name: string, + is_pointer: bool, + function: Maybe(Function), +} + +Enum :: struct { + name: string, + type: string, + variants: [dynamic]Enum_Variant, +} + +Enum_Variant :: struct { + name: string, + value: Maybe(uint), +} + +Parser :: struct { + data: []byte, + offset: int, + line: int, + column: int, + enum_defs: [dynamic]Enum, + struct_defs: [dynamic]Struct, + function_defs: [dynamic]Function, +} + +Parser_Proc :: #type proc(_: ^Parser) + +parser_parse_struct :: proc(p: ^Parser) { + struct_def: Struct + parser_consume(p, "struct") + parser_skip_space(p) + struct_def.name = parser_consume_identifier(p) + parser_skip_space(p) + if !parser_check(p, "{") do return // not all structs have bodies + parser_consume(p, "{") + for { + parser_skip_space(p) + if parser_check(p, "};") { + break + } + append(&struct_def.fields, parser_parse_struct_field(p)) + } + parser_consume(p, "};") + append(&p.struct_defs, struct_def) +} + +parser_check :: proc(p: ^Parser, text: string) -> bool { + return parser_peek(p, len(text)) == text +} + +parser_consume :: proc(p: ^Parser, text: string) { + got := p.data[p.offset:p.offset + len(text)] + if !bytes.equal(got, transmute([]u8)text) { + panic( + fmt.tprintf( + "expected %q, got %q (offset=%d) (line=%d, col=%d) (context=%q)", + text, + got, + p.offset, + p.line + 1, + p.column, + p.data[p.offset - 10:p.offset + 10], + ), + ) + } + parser_skip_n(p, len(text)) +} + +parser_consume_identifier :: proc(p: ^Parser) -> string { + parser_skip_text(p, "const ") + parser_skip_text(p, "struct ") + start := p.offset + for { + if !is_ident(p.data[p.offset]) { + return cast(string)p.data[start:p.offset] + } + if !parser_advance(p) { + break + } + } + panic("EOF before identifier") +} + +is_ident :: proc(b: byte) -> bool { + r := rune(b) + return unicode.is_letter(r) || unicode.is_digit(r) || b == '_' +} + +parser_skip_space :: proc(p: ^Parser) -> bool { + start := p.offset + for { + if p.data[p.offset] == '/' { + parser_skip_comment(p) + } + if !unicode.is_space(rune(p.data[p.offset])) { + return p.offset != start + } + if !parser_advance(p) { + break + } + } + return false +} + +parser_parse_struct_field :: proc(p: ^Parser) -> (field: Struct_Field) { + ident := parser_consume_identifier(p) + parser_skip_space(p) + if parser_check(p, "*") { + parser_consume(p, "*") + field.is_pointer = true + } + parser_skip_space(p) + if !parser_check(p, "(") { + field.name = parser_consume_identifier(p) + } else { + field.function = parser_parse_function_pointer(p) + } + parser_consume(p, ";") + // Special case to ensure that the return type is appropriately labeled. + if field.function == nil { + field.type = ident + } else { + if f, ok := &field.function.?; ok { + f.return_type = ident + } + } + return +} + +parser_parse_function_pointer :: proc(p: ^Parser) -> (f: Function) { + f.return_type = parser_consume_identifier(p) + parser_skip_space(p) + parser_consume(p, "(") + parser_skip_space(p) + parser_consume(p, "*") + parser_skip_space(p) + f.name = parser_consume_identifier(p) + parser_consume(p, ")") + parser_skip_space(p) + f.args = parser_parse_function_arguments(p) + return +} + +parser_parse_function :: proc(p: ^Parser) { + f: Function + parser_consume(p, "_UI_EXTERN") + parser_skip_space(p) + f.return_type = parser_consume_identifier(p) + parser_skip_space(p) + if parser_check(p, "*") { + f.return_is_pointer = true + parser_consume(p, "*") + } + parser_skip_space(p) + f.name = parser_consume_identifier(p) + f.args = parser_parse_function_arguments(p) + parser_skip_space(p) + parser_consume(p, ";") + append(&p.function_defs, f) + return +} + +parser_parse_function_arguments :: proc(p: ^Parser) -> (args: [dynamic]Function_Argument) { + parser_consume(p, "(") + for { + arg: Function_Argument + if parser_check(p, ")") { + break + } + parser_skip_space(p) + arg.type = parser_consume_identifier(p) + parser_skip_space(p) + if parser_check(p, "*") { + arg.is_pointer = true + parser_consume(p, "*") + } + parser_skip_space(p) + if parser_check(p, "(") { + fn := parser_parse_function_pointer(p) + fn.return_type = arg.type + arg.type = "" + arg.function = fn + } else { + arg.name = parser_consume_identifier(p) + } + + if parser_check(p, ",") { + parser_consume(p, ",") + } + parser_skip_space(p) + append(&args, arg) + } + parser_consume(p, ")") + return +} + +parser_skip_n :: proc(p: ^Parser, n: int = 1) { + for ii in 0 ..< n { + if !parser_advance(p) do return + } +} + +parser_skip_text :: proc(p: ^Parser, text: string) { + if parser_check(p, text) { + parser_consume(p, text) + } +} + +// TODO: might need to handle multiline preprocessor things +parser_skip_preproccessor_stuff :: proc(p: ^Parser) -> bool { + if !parser_check(p, "#") { + return false + } + parser_skip_until(p, "\n") + return true +} + +parser_parse_enum :: proc(p: ^Parser) { + enum_def: Enum + parser_consume(p, "_UI_ENUM") + parser_consume(p, "(") + enum_def.name = parser_consume_identifier(p) + parser_consume(p, ")") + parser_skip_space(p) + parser_consume(p, "{") + for { + variant: Enum_Variant + parser_skip_space(p) + if parser_check(p, "};") { + break + } + append(&enum_def.variants, parser_parse_enum_variant(p)) + } + parser_consume(p, "};") + append(&p.enum_defs, enum_def) +} + +parser_skip_comment :: proc(p: ^Parser) -> bool { + line_comment := parser_check(p, "//") + inline_comment := parser_check(p, "/*") + + if line_comment { + parser_skip_until(p, "\n") + return true + } + + if inline_comment { + parser_skip_until(p, "*/") + return true + } + + return false +} + +parser_skip_until :: proc(p: ^Parser, text: string) { + for { + if parser_check(p, text) { + defer parser_consume(p, text) + return + } + if !parser_advance(p) do return + } +} + +parser_advance :: proc(p: ^Parser) -> bool { + if p.offset > len(p.data) - 1 { + return false + } + if p.data[p.offset] == '\n' { + p.line += 1 + p.column = 0 + } + p.column += 1 + p.offset += 1 + return p.offset < len(p.data) +} + +parser_peek :: proc(p: ^Parser, n: int) -> string { + end := p.offset + n + if end > len(p.data) - 1 do return "" + return cast(string)p.data[p.offset:end] +} + +bitwise_operators :: [?]string{"<<", ">>"} + +parser_parse_enum_variant :: proc(p: ^Parser) -> (variant: Enum_Variant) { + variant.name = parser_consume_identifier(p) + parser_skip_space(p) + if parser_check(p, "=") { + parser_consume(p, "=") + parser_skip_space(p) + n_1 := parser_parse_int(p) + parser_skip_space(p) + // Because some enums use bitshifting we have to account for that, + // but because we aren't doing proper expresion parsing it's total + // jank. + for op in bitwise_operators { + if !parser_check(p, op) do continue + parser_consume(p, op) + parser_skip_space(p) + n_2 := parser_parse_int(p) + switch op { + case "<<": + n_1 = n_1 << n_2 + case ">>": + n_1 = n_1 << n_2 + } + } + variant.value = n_1 + } + parser_consume(p, ",") + return +} + +parser_parse_int :: proc(p: ^Parser) -> uint { + start := p.offset + index: int + for unicode.is_digit(rune(p.data[p.offset + index])) { + index += 1 + } + n, n_ok := strconv.parse_uint(cast(string)p.data[p.offset:p.offset + index]) + if !n_ok { + panic("not a number") + } + parser_skip_n(p, index) + return n +} + +// We are only handling typedef'd function pointers. +parser_parse_typedef :: proc(p: ^Parser) { + parser_consume(p, "typedef") + parser_skip_space(p) + if parser_check(p, "struct") { + parser_skip_until(p, ";") + return + } + function := parser_parse_function_pointer(p) + function.is_type = true + parser_skip_until(p, ";") + append(&p.function_defs, function) +} + +keywords :: [?]string{"_UI_ENUM", "_UI_EXTERN", "typedef", "struct"} + +parser_parse :: proc(p: ^Parser, by: []byte) { + p.data = by + + for p.offset < len(p.data) - 1 { + skipped_space := parser_skip_space(p) + skipped_comment := parser_skip_comment(p) + skipped_preprocessor := parser_skip_preproccessor_stuff(p) + + start := p.offset + + + defer if !(skipped_space || skipped_comment || skipped_preprocessor) { + end := p.offset + + if start == end { + parser_skip_n(p, 1) + } + } + + for keyword in keywords { + if !parser_check(p, keyword) { + continue + } + switch keyword { + case "struct": + parser_parse_struct(p) + case "_UI_ENUM": + parser_parse_enum(p) + case "_UI_EXTERN": + parser_parse_function(p) + case "typedef": + parser_parse_typedef(p) + } + } + } +} + diff --git a/libui b/libui @@ -0,0 +1 @@ +Subproject commit fea45b2d5b75839be0af9acc842a147c5cba9295 diff --git a/libui.odin b/libui.odin @@ -0,0 +1,645 @@ +// GENERATED CODE +package libui + +import libc "core:c" + +when ODIN_OS == .Linux { + @(extra_linker_flags="-lgtk-3 -lgdk-3 -lz -lharfbuzz -lpangocairo-1.0 -lpango-1.0 -latk-1.0 -lcairo -lcairo-gobject -lgdk_pixbuf-2.0 -lgio-2.0 -lglib-2.0 -lgobject-2.0") + foreign import lib "system:ui" +} else { + foreign import lib "system:ui" +} + +// Unresolved symbols; typically opaque widget pointers +OpenTypeFeatures :: rawptr +Menu :: rawptr +Separator :: rawptr +RadioButtons :: rawptr +AttributedString :: rawptr +DrawContext :: rawptr +Grid :: rawptr +Button :: rawptr +Window :: rawptr +TableValue :: rawptr +ColorButton :: rawptr +Combobox :: rawptr +DrawTextLayout :: rawptr +Area :: rawptr +FontButton :: rawptr +DateTimePicker :: rawptr +Attribute :: rawptr +DrawPath :: rawptr +Table :: rawptr +Slider :: rawptr +Box :: rawptr +ProgressBar :: rawptr +EditableCombobox :: rawptr +Checkbox :: rawptr +Entry :: rawptr +MenuItem :: rawptr +Spinbox :: rawptr +Group :: rawptr +Form :: rawptr +MultilineEntry :: rawptr +TableModel :: rawptr +Image :: rawptr +Label :: rawptr +Tab :: rawptr + +// Hardcoded #defines +DrawDefaultMiterLimit :: 10.0 + +// Enum definitions +ForEach :: enum { + ForEachContinue, + ForEachStop, +} + +WindowResizeEdge :: enum { + WindowResizeEdgeLeft, + WindowResizeEdgeTop, + WindowResizeEdgeRight, + WindowResizeEdgeBottom, + WindowResizeEdgeTopLeft, + WindowResizeEdgeTopRight, + WindowResizeEdgeBottomLeft, + WindowResizeEdgeBottomRight, +} + +DrawBrushType :: enum { + DrawBrushTypeSolid, + DrawBrushTypeLinearGradient, + DrawBrushTypeRadialGradient, + DrawBrushTypeImage, +} + +DrawLineCap :: enum { + DrawLineCapFlat, + DrawLineCapRound, + DrawLineCapSquare, +} + +DrawLineJoin :: enum { + DrawLineJoinMiter, + DrawLineJoinRound, + DrawLineJoinBevel, +} + +DrawFillMode :: enum { + DrawFillModeWinding, + DrawFillModeAlternate, +} + +AttributeType :: enum { + AttributeTypeFamily, + AttributeTypeSize, + AttributeTypeWeight, + AttributeTypeItalic, + AttributeTypeStretch, + AttributeTypeColor, + AttributeTypeBackground, + AttributeTypeUnderline, + AttributeTypeUnderlineColor, + AttributeTypeFeatures, +} + +TextWeight :: enum { + TextWeightMinimum = 0, + TextWeightThin = 100, + TextWeightUltraLight = 200, + TextWeightLight = 300, + TextWeightBook = 350, + TextWeightNormal = 400, + TextWeightMedium = 500, + TextWeightSemiBold = 600, + TextWeightBold = 700, + TextWeightUltraBold = 800, + TextWeightHeavy = 900, + TextWeightUltraHeavy = 950, + TextWeightMaximum = 1000, +} + +TextItalic :: enum { + TextItalicNormal, + TextItalicOblique, + TextItalicItalic, +} + +TextStretch :: enum { + TextStretchUltraCondensed, + TextStretchExtraCondensed, + TextStretchCondensed, + TextStretchSemiCondensed, + TextStretchNormal, + TextStretchSemiExpanded, + TextStretchExpanded, + TextStretchExtraExpanded, + TextStretchUltraExpanded, +} + +Underline :: enum { + UnderlineNone, + UnderlineSingle, + UnderlineDouble, + UnderlineSuggestion, +} + +UnderlineColor :: enum { + UnderlineColorCustom, + UnderlineColorSpelling, + UnderlineColorGrammar, + UnderlineColorAuxiliary, +} + +DrawTextAlign :: enum { + DrawTextAlignLeft, + DrawTextAlignCenter, + DrawTextAlignRight, +} + +Modifiers :: enum { + ModifierCtrl = 1, + ModifierAlt = 2, + ModifierShift = 4, + ModifierSuper = 8, +} + +ExtKey :: enum { + ExtKeyEscape = 1, + ExtKeyInsert, + ExtKeyDelete, + ExtKeyHome, + ExtKeyEnd, + ExtKeyPageUp, + ExtKeyPageDown, + ExtKeyUp, + ExtKeyDown, + ExtKeyLeft, + ExtKeyRight, + ExtKeyF1, + ExtKeyF2, + ExtKeyF3, + ExtKeyF4, + ExtKeyF5, + ExtKeyF6, + ExtKeyF7, + ExtKeyF8, + ExtKeyF9, + ExtKeyF10, + ExtKeyF11, + ExtKeyF12, + ExtKeyN0, + ExtKeyN1, + ExtKeyN2, + ExtKeyN3, + ExtKeyN4, + ExtKeyN5, + ExtKeyN6, + ExtKeyN7, + ExtKeyN8, + ExtKeyN9, + ExtKeyNDot, + ExtKeyNEnter, + ExtKeyNAdd, + ExtKeyNSubtract, + ExtKeyNMultiply, + ExtKeyNDivide, +} + +Align :: enum { + AlignFill, + AlignStart, + AlignCenter, + AlignEnd, +} + +At :: enum { + AtLeading, + AtTop, + AtTrailing, + AtBottom, +} + +TableValueType :: enum { + TableValueTypeString, + TableValueTypeImage, + TableValueTypeInt, + TableValueTypeColor, +} + +// Struct definitions + +// ISO C broken-down time structure. +tm :: struct { + tm_sec: int, + tm_min: int, + tm_hour: int, + tm_mday: int, + tm_mon: int, + tm_year: int, + tm_wday: int, + tm_yday: int, + tm_isdst: int, +} + +InitOptions :: struct { + Size: libc.size_t, +} + +Control :: struct { + Signature: libc.uint32_t, + OSSignature: libc.uint32_t, + TypeSignature: libc.uint32_t, + Destroy: proc(_: ^Control), + Handle: proc(_: ^Control) -> libc.uintptr_t, + Parent: ^proc(_: ^Control) -> Control, + SetParent: proc(_: ^Control, _: ^Control), + Toplevel: proc(_: ^Control) -> libc.int, + Visible: proc(_: ^Control) -> libc.int, + Show: proc(_: ^Control), + Hide: proc(_: ^Control), + Enabled: proc(_: ^Control) -> libc.int, + Enable: proc(_: ^Control), + Disable: proc(_: ^Control), +} + +AreaHandler :: struct { + Draw: proc(_: ^AreaHandler, _: Area, _: ^AreaDrawParams), + MouseEvent: proc(_: ^AreaHandler, _: Area, _: ^AreaMouseEvent), + MouseCrossed: proc(_: ^AreaHandler, _: Area, left: libc.int), + DragBroken: proc(_: ^AreaHandler, _: Area), + KeyEvent: proc(_: ^AreaHandler, _: Area, _: ^AreaKeyEvent) -> libc.int, +} + +AreaDrawParams :: struct { + Context: DrawContext, + AreaWidth: libc.double, + AreaHeight: libc.double, + ClipX: libc.double, + ClipY: libc.double, + ClipWidth: libc.double, + ClipHeight: libc.double, +} + +DrawMatrix :: struct { + M11: libc.double, + M12: libc.double, + M21: libc.double, + M22: libc.double, + M31: libc.double, + M32: libc.double, +} + +DrawBrush :: struct { + Type: DrawBrushType, + R: libc.double, + G: libc.double, + B: libc.double, + A: libc.double, + X0: libc.double, + Y0: libc.double, + X1: libc.double, + Y1: libc.double, + OuterRadius: libc.double, + Stops: ^DrawBrushGradientStop, + NumStops: libc.size_t, +} + +DrawBrushGradientStop :: struct { + Pos: libc.double, + R: libc.double, + G: libc.double, + B: libc.double, + A: libc.double, +} + +DrawStrokeParams :: struct { + Cap: DrawLineCap, + Join: DrawLineJoin, + Thickness: libc.double, + MiterLimit: libc.double, + Dashes: ^libc.double, + NumDashes: libc.size_t, + DashPhase: libc.double, +} + +FontDescriptor :: struct { + Family: cstring, + Size: libc.double, + Weight: TextWeight, + Italic: TextItalic, + Stretch: TextStretch, +} + +DrawTextLayoutParams :: struct { + String: AttributedString, + DefaultFont: ^FontDescriptor, + Width: libc.double, + Align: DrawTextAlign, +} + +AreaMouseEvent :: struct { + X: libc.double, + Y: libc.double, + AreaWidth: libc.double, + AreaHeight: libc.double, + Down: libc.int, + Up: libc.int, + Count: libc.int, + Modifiers: Modifiers, + Held1To64: libc.uint64_t, +} + +AreaKeyEvent :: struct { + Key: byte, + ExtKey: ExtKey, + Modifier: Modifiers, + Modifiers: Modifiers, + Up: libc.int, +} + +TableModelHandler :: struct { + NumColumns: proc(_: ^TableModelHandler, _: TableModel) -> libc.int, + ColumnType: proc(_: ^TableModelHandler, _: TableModel, _: libc.int) -> TableValueType, + NumRows: proc(_: ^TableModelHandler, _: TableModel) -> libc.int, + CellValue: ^proc(mh: ^TableModelHandler, m: TableModel, row: libc.int, column: libc.int) -> TableValue, + SetCellValue: proc(_: ^TableModelHandler, _: TableModel, _: libc.int, _: libc.int, _: TableValue), +} + +TableTextColumnOptionalParams :: struct { + ColorModelColumn: libc.int, +} + +TableParams :: struct { + Model: TableModel, + RowBackgroundColorModelColumn: libc.int, +} + +// Procedure types +OpenTypeFeaturesForEachFunc :: #type proc(otf: OpenTypeFeatures, a: byte, b: byte, c: byte, d: byte, value: libc.uint32_t, data: rawptr) -> ForEach +AttributedStringForEachAttributeFunc :: #type proc(s: AttributedString, a: Attribute, start: libc.size_t, end: libc.size_t, data: rawptr) -> ForEach + +// Procedure definitions +@(default_calling_convention = "stdcall", link_prefix = "ui") +foreign lib { + Init :: proc(options: ^InitOptions) -> cstring --- + Uninit :: proc() --- + FreeInitError :: proc(err: cstring) --- + Main :: proc() --- + MainSteps :: proc() --- + MainStep :: proc(wait: libc.int) -> libc.int --- + Quit :: proc() --- + QueueMain :: proc(f: proc(data: rawptr), data: rawptr) --- + Timer :: proc(milliseconds: libc.int, f: proc(data: rawptr) -> libc.int, data: rawptr) --- + OnShouldQuit :: proc(f: proc(data: rawptr) -> libc.int, data: rawptr) --- + FreeText :: proc(text: cstring) --- + ControlDestroy :: proc(_: ^Control) --- + ControlHandle :: proc(_: ^Control) -> libc.uintptr_t --- + ControlParent :: proc(_: ^Control) -> ^Control --- + ControlSetParent :: proc(_: ^Control, _: ^Control) --- + ControlToplevel :: proc(_: ^Control) -> libc.int --- + ControlVisible :: proc(_: ^Control) -> libc.int --- + ControlShow :: proc(_: ^Control) --- + ControlHide :: proc(_: ^Control) --- + ControlEnabled :: proc(_: ^Control) -> libc.int --- + ControlEnable :: proc(_: ^Control) --- + ControlDisable :: proc(_: ^Control) --- + AllocControl :: proc(n: libc.size_t, OSsig: libc.uint32_t, typesig: libc.uint32_t, typenamestr: cstring) -> ^Control --- + FreeControl :: proc(_: ^Control) --- + ControlVerifySetParent :: proc(_: ^Control, _: ^Control) --- + ControlEnabledToUser :: proc(_: ^Control) -> libc.int --- + UserBugCannotSetParentOnToplevel :: proc(type: cstring) --- + WindowTitle :: proc(w: Window) -> cstring --- + WindowSetTitle :: proc(w: Window, title: cstring) --- + WindowContentSize :: proc(w: Window, width: ^libc.int, height: ^libc.int) --- + WindowSetContentSize :: proc(w: Window, width: libc.int, height: libc.int) --- + WindowFullscreen :: proc(w: Window) -> libc.int --- + WindowSetFullscreen :: proc(w: Window, fullscreen: libc.int) --- + WindowOnContentSizeChanged :: proc(w: Window, f: proc(_: Window, _: rawptr), data: rawptr) --- + WindowOnClosing :: proc(w: Window, f: proc(w: Window, data: rawptr) -> libc.int, data: rawptr) --- + WindowBorderless :: proc(w: Window) -> libc.int --- + WindowSetBorderless :: proc(w: Window, borderless: libc.int) --- + WindowSetChild :: proc(w: Window, child: ^Control) --- + WindowMargined :: proc(w: Window) -> libc.int --- + WindowSetMargined :: proc(w: Window, margined: libc.int) --- + NewWindow :: proc(title: cstring, width: libc.int, height: libc.int, hasMenubar: libc.int) -> Window --- + ButtonText :: proc(b: Button) -> cstring --- + ButtonSetText :: proc(b: Button, text: cstring) --- + ButtonOnClicked :: proc(b: Button, f: proc(b: Button, data: rawptr), data: rawptr) --- + NewButton :: proc(text: cstring) -> Button --- + BoxAppend :: proc(b: Box, child: ^Control, stretchy: libc.int) --- + BoxDelete :: proc(b: Box, index: libc.int) --- + BoxPadded :: proc(b: Box) -> libc.int --- + BoxSetPadded :: proc(b: Box, padded: libc.int) --- + NewHorizontalBox :: proc() -> Box --- + NewVerticalBox :: proc() -> Box --- + CheckboxText :: proc(c: Checkbox) -> cstring --- + CheckboxSetText :: proc(c: Checkbox, text: cstring) --- + CheckboxOnToggled :: proc(c: Checkbox, f: proc(c: Checkbox, data: rawptr), data: rawptr) --- + CheckboxChecked :: proc(c: Checkbox) -> libc.int --- + CheckboxSetChecked :: proc(c: Checkbox, checked: libc.int) --- + NewCheckbox :: proc(text: cstring) -> Checkbox --- + EntryText :: proc(e: Entry) -> cstring --- + EntrySetText :: proc(e: Entry, text: cstring) --- + EntryOnChanged :: proc(e: Entry, f: proc(e: Entry, data: rawptr), data: rawptr) --- + EntryReadOnly :: proc(e: Entry) -> libc.int --- + EntrySetReadOnly :: proc(e: Entry, readonly: libc.int) --- + NewEntry :: proc() -> Entry --- + NewPasswordEntry :: proc() -> Entry --- + NewSearchEntry :: proc() -> Entry --- + LabelText :: proc(l: Label) -> cstring --- + LabelSetText :: proc(l: Label, text: cstring) --- + NewLabel :: proc(text: cstring) -> Label --- + TabAppend :: proc(t: Tab, name: cstring, c: ^Control) --- + TabInsertAt :: proc(t: Tab, name: cstring, before: libc.int, c: ^Control) --- + TabDelete :: proc(t: Tab, index: libc.int) --- + TabNumPages :: proc(t: Tab) -> libc.int --- + TabMargined :: proc(t: Tab, page: libc.int) -> libc.int --- + TabSetMargined :: proc(t: Tab, page: libc.int, margined: libc.int) --- + NewTab :: proc() -> Tab --- + GroupTitle :: proc(g: Group) -> cstring --- + GroupSetTitle :: proc(g: Group, title: cstring) --- + GroupSetChild :: proc(g: Group, c: ^Control) --- + GroupMargined :: proc(g: Group) -> libc.int --- + GroupSetMargined :: proc(g: Group, margined: libc.int) --- + NewGroup :: proc(title: cstring) -> Group --- + SpinboxValue :: proc(s: Spinbox) -> libc.int --- + SpinboxSetValue :: proc(s: Spinbox, value: libc.int) --- + SpinboxOnChanged :: proc(s: Spinbox, f: proc(s: Spinbox, data: rawptr), data: rawptr) --- + NewSpinbox :: proc(min: libc.int, max: libc.int) -> Spinbox --- + SliderValue :: proc(s: Slider) -> libc.int --- + SliderSetValue :: proc(s: Slider, value: libc.int) --- + SliderOnChanged :: proc(s: Slider, f: proc(s: Slider, data: rawptr), data: rawptr) --- + NewSlider :: proc(min: libc.int, max: libc.int) -> Slider --- + ProgressBarValue :: proc(p: ProgressBar) -> libc.int --- + ProgressBarSetValue :: proc(p: ProgressBar, n: libc.int) --- + NewProgressBar :: proc() -> ProgressBar --- + NewHorizontalSeparator :: proc() -> Separator --- + NewVerticalSeparator :: proc() -> Separator --- + ComboboxAppend :: proc(c: Combobox, text: cstring) --- + ComboboxSelected :: proc(c: Combobox) -> libc.int --- + ComboboxSetSelected :: proc(c: Combobox, n: libc.int) --- + ComboboxOnSelected :: proc(c: Combobox, f: proc(c: Combobox, data: rawptr), data: rawptr) --- + NewCombobox :: proc() -> Combobox --- + EditableComboboxAppend :: proc(c: EditableCombobox, text: cstring) --- + EditableComboboxText :: proc(c: EditableCombobox) -> cstring --- + EditableComboboxSetText :: proc(c: EditableCombobox, text: cstring) --- + EditableComboboxOnChanged :: proc(c: EditableCombobox, f: proc(c: EditableCombobox, data: rawptr), data: rawptr) --- + NewEditableCombobox :: proc() -> EditableCombobox --- + RadioButtonsAppend :: proc(r: RadioButtons, text: cstring) --- + RadioButtonsSelected :: proc(r: RadioButtons) -> libc.int --- + RadioButtonsSetSelected :: proc(r: RadioButtons, n: libc.int) --- + RadioButtonsOnSelected :: proc(r: RadioButtons, f: proc(_: RadioButtons, _: rawptr), data: rawptr) --- + NewRadioButtons :: proc() -> RadioButtons --- + DateTimePickerTime :: proc(d: DateTimePicker, time: ^tm) --- + DateTimePickerSetTime :: proc(d: DateTimePicker, time: ^tm) --- + DateTimePickerOnChanged :: proc(d: DateTimePicker, f: proc(_: DateTimePicker, _: rawptr), data: rawptr) --- + NewDateTimePicker :: proc() -> DateTimePicker --- + NewDatePicker :: proc() -> DateTimePicker --- + NewTimePicker :: proc() -> DateTimePicker --- + MultilineEntryText :: proc(e: MultilineEntry) -> cstring --- + MultilineEntrySetText :: proc(e: MultilineEntry, text: cstring) --- + MultilineEntryAppend :: proc(e: MultilineEntry, text: cstring) --- + MultilineEntryOnChanged :: proc(e: MultilineEntry, f: proc(e: MultilineEntry, data: rawptr), data: rawptr) --- + MultilineEntryReadOnly :: proc(e: MultilineEntry) -> libc.int --- + MultilineEntrySetReadOnly :: proc(e: MultilineEntry, readonly: libc.int) --- + NewMultilineEntry :: proc() -> MultilineEntry --- + NewNonWrappingMultilineEntry :: proc() -> MultilineEntry --- + MenuItemEnable :: proc(m: MenuItem) --- + MenuItemDisable :: proc(m: MenuItem) --- + MenuItemOnClicked :: proc(m: MenuItem, f: proc(sender: MenuItem, window: Window, data: rawptr), data: rawptr) --- + MenuItemChecked :: proc(m: MenuItem) -> libc.int --- + MenuItemSetChecked :: proc(m: MenuItem, checked: libc.int) --- + MenuAppendItem :: proc(m: Menu, name: cstring) -> MenuItem --- + MenuAppendCheckItem :: proc(m: Menu, name: cstring) -> MenuItem --- + MenuAppendQuitItem :: proc(m: Menu) -> MenuItem --- + MenuAppendPreferencesItem :: proc(m: Menu) -> MenuItem --- + MenuAppendAboutItem :: proc(m: Menu) -> MenuItem --- + MenuAppendSeparator :: proc(m: Menu) --- + NewMenu :: proc(name: cstring) -> Menu --- + OpenFile :: proc(parent: Window) -> cstring --- + SaveFile :: proc(parent: Window) -> cstring --- + MsgBox :: proc(parent: Window, title: cstring, description: cstring) --- + MsgBoxError :: proc(parent: Window, title: cstring, description: cstring) --- + AreaSetSize :: proc(a: Area, width: libc.int, height: libc.int) --- + AreaQueueRedrawAll :: proc(a: Area) --- + AreaScrollTo :: proc(a: Area, x: libc.double, y: libc.double, width: libc.double, height: libc.double) --- + AreaBeginUserWindowMove :: proc(a: Area) --- + AreaBeginUserWindowResize :: proc(a: Area, edge: WindowResizeEdge) --- + NewArea :: proc(ah: ^AreaHandler) -> Area --- + NewScrollingArea :: proc(ah: ^AreaHandler, width: libc.int, height: libc.int) -> Area --- + DrawNewPath :: proc(fillMode: DrawFillMode) -> DrawPath --- + DrawFreePath :: proc(p: DrawPath) --- + DrawPathNewFigure :: proc(p: DrawPath, x: libc.double, y: libc.double) --- + DrawPathNewFigureWithArc :: proc(p: DrawPath, xCenter: libc.double, yCenter: libc.double, radius: libc.double, startAngle: libc.double, sweep: libc.double, negative: libc.int) --- + DrawPathLineTo :: proc(p: DrawPath, x: libc.double, y: libc.double) --- + DrawPathArcTo :: proc(p: DrawPath, xCenter: libc.double, yCenter: libc.double, radius: libc.double, startAngle: libc.double, sweep: libc.double, negative: libc.int) --- + DrawPathBezierTo :: proc(p: DrawPath, c1x: libc.double, c1y: libc.double, c2x: libc.double, c2y: libc.double, endX: libc.double, endY: libc.double) --- + DrawPathCloseFigure :: proc(p: DrawPath) --- + DrawPathAddRectangle :: proc(p: DrawPath, x: libc.double, y: libc.double, width: libc.double, height: libc.double) --- + DrawPathEnd :: proc(p: DrawPath) --- + DrawStroke :: proc(c: DrawContext, path: DrawPath, b: ^DrawBrush, p: ^DrawStrokeParams) --- + DrawFill :: proc(c: DrawContext, path: DrawPath, b: ^DrawBrush) --- + DrawMatrixSetIdentity :: proc(m: ^DrawMatrix) --- + DrawMatrixTranslate :: proc(m: ^DrawMatrix, x: libc.double, y: libc.double) --- + DrawMatrixScale :: proc(m: ^DrawMatrix, xCenter: libc.double, yCenter: libc.double, x: libc.double, y: libc.double) --- + DrawMatrixRotate :: proc(m: ^DrawMatrix, x: libc.double, y: libc.double, amount: libc.double) --- + DrawMatrixSkew :: proc(m: ^DrawMatrix, x: libc.double, y: libc.double, xamount: libc.double, yamount: libc.double) --- + DrawMatrixMultiply :: proc(dest: ^DrawMatrix, src: ^DrawMatrix) --- + DrawMatrixInvertible :: proc(m: ^DrawMatrix) -> libc.int --- + DrawMatrixInvert :: proc(m: ^DrawMatrix) -> libc.int --- + DrawMatrixTransformPoint :: proc(m: ^DrawMatrix, x: ^libc.double, y: ^libc.double) --- + DrawMatrixTransformSize :: proc(m: ^DrawMatrix, x: ^libc.double, y: ^libc.double) --- + DrawTransform :: proc(c: DrawContext, m: ^DrawMatrix) --- + DrawClip :: proc(c: DrawContext, path: DrawPath) --- + DrawSave :: proc(c: DrawContext) --- + DrawRestore :: proc(c: DrawContext) --- + FreeAttribute :: proc(a: Attribute) --- + AttributeGetType :: proc(a: Attribute) -> AttributeType --- + NewFamilyAttribute :: proc(family: cstring) -> Attribute --- + AttributeFamily :: proc(a: Attribute) -> cstring --- + NewSizeAttribute :: proc(size: libc.double) -> Attribute --- + AttributeSize :: proc(a: Attribute) -> libc.double --- + NewWeightAttribute :: proc(weight: TextWeight) -> Attribute --- + AttributeWeight :: proc(a: Attribute) -> TextWeight --- + NewItalicAttribute :: proc(italic: TextItalic) -> Attribute --- + AttributeItalic :: proc(a: Attribute) -> TextItalic --- + NewStretchAttribute :: proc(stretch: TextStretch) -> Attribute --- + AttributeStretch :: proc(a: Attribute) -> TextStretch --- + NewColorAttribute :: proc(r: libc.double, g: libc.double, b: libc.double, a: libc.double) -> Attribute --- + AttributeColor :: proc(a: Attribute, r: ^libc.double, g: ^libc.double, b: ^libc.double, alpha: ^libc.double) --- + NewBackgroundAttribute :: proc(r: libc.double, g: libc.double, b: libc.double, a: libc.double) -> Attribute --- + NewUnderlineAttribute :: proc(u: Underline) -> Attribute --- + AttributeUnderline :: proc(a: Attribute) -> Underline --- + NewUnderlineColorAttribute :: proc(u: UnderlineColor, r: libc.double, g: libc.double, b: libc.double, a: libc.double) -> Attribute --- + AttributeUnderlineColor :: proc(a: Attribute, u: ^UnderlineColor, r: ^libc.double, g: ^libc.double, b: ^libc.double, alpha: ^libc.double) --- + NewOpenTypeFeatures :: proc() -> OpenTypeFeatures --- + FreeOpenTypeFeatures :: proc(otf: OpenTypeFeatures) --- + OpenTypeFeaturesClone :: proc(otf: OpenTypeFeatures) -> OpenTypeFeatures --- + OpenTypeFeaturesAdd :: proc(otf: OpenTypeFeatures, a: byte, b: byte, c: byte, d: byte, value: libc.uint32_t) --- + OpenTypeFeaturesRemove :: proc(otf: OpenTypeFeatures, a: byte, b: byte, c: byte, d: byte) --- + OpenTypeFeaturesGet :: proc(otf: OpenTypeFeatures, a: byte, b: byte, c: byte, d: byte, value: ^libc.uint32_t) -> libc.int --- + OpenTypeFeaturesForEach :: proc(otf: OpenTypeFeatures, f: OpenTypeFeaturesForEachFunc, data: rawptr) --- + NewFeaturesAttribute :: proc(otf: OpenTypeFeatures) -> Attribute --- + AttributeFeatures :: proc(a: Attribute) -> OpenTypeFeatures --- + NewAttributedString :: proc(initialString: cstring) -> AttributedString --- + FreeAttributedString :: proc(s: AttributedString) --- + AttributedStringString :: proc(s: AttributedString) -> cstring --- + AttributedStringLen :: proc(s: AttributedString) -> libc.size_t --- + AttributedStringAppendUnattributed :: proc(s: AttributedString, str: cstring) --- + AttributedStringInsertAtUnattributed :: proc(s: AttributedString, str: cstring, at: libc.size_t) --- + AttributedStringDelete :: proc(s: AttributedString, start: libc.size_t, end: libc.size_t) --- + AttributedStringSetAttribute :: proc(s: AttributedString, a: Attribute, start: libc.size_t, end: libc.size_t) --- + AttributedStringForEachAttribute :: proc(s: AttributedString, f: AttributedStringForEachAttributeFunc, data: rawptr) --- + AttributedStringNumGraphemes :: proc(s: AttributedString) -> libc.size_t --- + AttributedStringByteIndexToGrapheme :: proc(s: AttributedString, pos: libc.size_t) -> libc.size_t --- + AttributedStringGraphemeToByteIndex :: proc(s: AttributedString, pos: libc.size_t) -> libc.size_t --- + DrawNewTextLayout :: proc(params: ^DrawTextLayoutParams) -> DrawTextLayout --- + DrawFreeTextLayout :: proc(tl: DrawTextLayout) --- + DrawText :: proc(c: DrawContext, tl: DrawTextLayout, x: libc.double, y: libc.double) --- + DrawTextLayoutExtents :: proc(tl: DrawTextLayout, width: ^libc.double, height: ^libc.double) --- + FontButtonFont :: proc(b: FontButton, desc: ^FontDescriptor) --- + FontButtonOnChanged :: proc(b: FontButton, f: proc(_: FontButton, _: rawptr), data: rawptr) --- + NewFontButton :: proc() -> FontButton --- + FreeFontButtonFont :: proc(desc: ^FontDescriptor) --- + ColorButtonColor :: proc(b: ColorButton, r: ^libc.double, g: ^libc.double, bl: ^libc.double, a: ^libc.double) --- + ColorButtonSetColor :: proc(b: ColorButton, r: libc.double, g: libc.double, bl: libc.double, a: libc.double) --- + ColorButtonOnChanged :: proc(b: ColorButton, f: proc(_: ColorButton, _: rawptr), data: rawptr) --- + NewColorButton :: proc() -> ColorButton --- + FormAppend :: proc(f: Form, label: cstring, c: ^Control, stretchy: libc.int) --- + FormDelete :: proc(f: Form, index: libc.int) --- + FormPadded :: proc(f: Form) -> libc.int --- + FormSetPadded :: proc(f: Form, padded: libc.int) --- + NewForm :: proc() -> Form --- + GridAppend :: proc(g: Grid, c: ^Control, left: libc.int, top: libc.int, xspan: libc.int, yspan: libc.int, hexpand: libc.int, halign: Align, vexpand: libc.int, valign: Align) --- + GridInsertAt :: proc(g: Grid, c: ^Control, existing: ^Control, at: At, xspan: libc.int, yspan: libc.int, hexpand: libc.int, halign: Align, vexpand: libc.int, valign: Align) --- + GridPadded :: proc(g: Grid) -> libc.int --- + GridSetPadded :: proc(g: Grid, padded: libc.int) --- + NewGrid :: proc() -> Grid --- + NewImage :: proc(width: libc.double, height: libc.double) -> Image --- + FreeImage :: proc(i: Image) --- + ImageAppend :: proc(i: Image, pixels: rawptr, pixelWidth: libc.int, pixelHeight: libc.int, byteStride: libc.int) --- + FreeTableValue :: proc(v: TableValue) --- + TableValueGetType :: proc(v: TableValue) -> TableValueType --- + NewTableValueString :: proc(str: cstring) -> TableValue --- + TableValueString :: proc(v: TableValue) -> cstring --- + NewTableValueImage :: proc(img: Image) -> TableValue --- + TableValueImage :: proc(v: TableValue) -> Image --- + NewTableValueInt :: proc(i: libc.int) -> TableValue --- + TableValueInt :: proc(v: TableValue) -> libc.int --- + NewTableValueColor :: proc(r: libc.double, g: libc.double, b: libc.double, a: libc.double) -> TableValue --- + TableValueColor :: proc(v: TableValue, r: ^libc.double, g: ^libc.double, b: ^libc.double, a: ^libc.double) --- + NewTableModel :: proc(mh: ^TableModelHandler) -> TableModel --- + FreeTableModel :: proc(m: TableModel) --- + TableModelRowInserted :: proc(m: TableModel, newIndex: libc.int) --- + TableModelRowChanged :: proc(m: TableModel, index: libc.int) --- + TableModelRowDeleted :: proc(m: TableModel, oldIndex: libc.int) --- + TableAppendTextColumn :: proc(t: Table, name: cstring, textModelColumn: libc.int, textEditableModelColumn: libc.int, textParams: ^TableTextColumnOptionalParams) --- + TableAppendImageColumn :: proc(t: Table, name: cstring, imageModelColumn: libc.int) --- + TableAppendImageTextColumn :: proc(t: Table, name: cstring, imageModelColumn: libc.int, textModelColumn: libc.int, textEditableModelColumn: libc.int, textParams: ^TableTextColumnOptionalParams) --- + TableAppendCheckboxColumn :: proc(t: Table, name: cstring, checkboxModelColumn: libc.int, checkboxEditableModelColumn: libc.int) --- + TableAppendCheckboxTextColumn :: proc(t: Table, name: cstring, checkboxModelColumn: libc.int, checkboxEditableModelColumn: libc.int, textModelColumn: libc.int, textEditableModelColumn: libc.int, textParams: ^TableTextColumnOptionalParams) --- + TableAppendProgressBarColumn :: proc(t: Table, name: cstring, progressModelColumn: libc.int) --- + TableAppendButtonColumn :: proc(t: Table, name: cstring, buttonModelColumn: libc.int, buttonClickableModelColumn: libc.int) --- + NewTable :: proc(params: ^TableParams) -> Table --- +} diff --git a/odinfmt.json b/odinfmt.json @@ -0,0 +1,6 @@ +{ + "character_width": 80, + "tabs": true, + "tabs_width": 4, + "sort_imports": true +} diff --git a/ols.json b/ols.json @@ -0,0 +1,11 @@ +{ + "enable_hover": true, + "enable_format": true, + "enable_rename": true, + "enable_snippets": true, + "enable_references": true, + "enable_fake_methods": true, + "enable_semantic_tokens": true, + "enable_document_symbols": true, + "enable_procedure_snippet": true, +}