diff options
Diffstat (limited to 'code')
| -rw-r--r-- | code/demo.odin | 3 | ||||
| -rw-r--r-- | code/fmt.odin | 583 | ||||
| -rw-r--r-- | code/os.odin | 107 | ||||
| -rw-r--r-- | code/punity.odin | 484 |
4 files changed, 1175 insertions, 2 deletions
diff --git a/code/demo.odin b/code/demo.odin index f1667bd66..9fea042ff 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -2,7 +2,6 @@ main :: proc() { init :: proc(c: ^pn.Core) { - } step :: proc(c: ^pn.Core) { @@ -11,5 +10,5 @@ main :: proc() { } } - // pn.run(init, step) + pn.run(init, step) } diff --git a/code/fmt.odin b/code/fmt.odin new file mode 100644 index 000000000..0c7e3a696 --- /dev/null +++ b/code/fmt.odin @@ -0,0 +1,583 @@ +#import "os.odin" as os + +print_byte_buffer :: proc(buf: ^[]byte, b: []byte) { + if buf.count < buf.capacity { + n := min(buf.capacity-buf.count, b.count) + if n > 0 { + offset := ptr_offset(buf.data, buf.count) + memory_copy(offset, ^b[0], n) + buf.count += n + } + } +} + +print_string_to_buffer :: proc(buf: ^[]byte, s: string) { + print_byte_buffer(buf, s as []byte) +} + + +byte_reverse :: proc(b: []byte) { + n := b.count + for i := 0; i < n/2; i++ { + b[i], b[n-1-i] = b[n-1-i], b[i] + } +} + +encode_rune :: proc(r: rune) -> ([4]byte, int) { + buf: [4]byte + i := r as u32 + mask: byte : 0x3f + if i <= 1<<7-1 { + buf[0] = r as byte + return buf, 1 + } + if i <= 1<<11-1 { + buf[0] = 0xc0 | (r>>6) as byte + buf[1] = 0x80 | (r) as byte & mask + return buf, 2 + } + + // Invalid or Surrogate range + if i > 0x0010ffff || + (i >= 0xd800 && i <= 0xdfff) { + r = 0xfffd + } + + if i <= 1<<16-1 { + buf[0] = 0xe0 | (r>>12) as byte + buf[1] = 0x80 | (r>>6) as byte & mask + buf[2] = 0x80 | (r) as byte & mask + return buf, 3 + } + + buf[0] = 0xf0 | (r>>18) as byte + buf[1] = 0x80 | (r>>12) as byte & mask + buf[2] = 0x80 | (r>>6) as byte & mask + buf[3] = 0x80 | (r) as byte & mask + return buf, 4 +} + +print_rune_to_buffer :: proc(buf: ^[]byte, r: rune) { + b, n := encode_rune(r) + print_string_to_buffer(buf, b[:n] as string) +} + +print_space_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune " ") } +print_nl_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune "\n") } + +print_int_to_buffer :: proc(buf: ^[]byte, i: int) { + print_int_base_to_buffer(buf, i, 10); +} +PRINT__NUM_TO_CHAR_TABLE :: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$" +print_int_base_to_buffer :: proc(buffer: ^[]byte, i, base: int) { + + buf: [65]byte + len := 0 + negative := false + if i < 0 { + negative = true + i = -i + } + if i == 0 { + buf[len] = #rune "0" + len++ + } + for i > 0 { + buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % base] + len++ + i /= base + } + + if negative { + buf[len] = #rune "-" + len++ + } + + byte_reverse(buf[:len]) + print_string_to_buffer(buffer, buf[:len] as string) +} + +print_uint_to_buffer :: proc(buffer: ^[]byte, i: uint) { + print_uint_base_to_buffer(buffer, i, 10, 0, #rune " ") +} +print_uint_base_to_buffer :: proc(buffer: ^[]byte, i, base: uint, min_width: int, pad_char: byte) { + buf: [65]byte + len := 0 + if i == 0 { + buf[len] = #rune "0" + len++ + } + for i > 0 { + buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % base] + len++ + i /= base + } + for len < min_width { + buf[len] = pad_char + len++ + } + + byte_reverse(buf[:len]) + print_string_to_buffer(buffer, buf[:len] as string) +} + +print_bool_to_buffer :: proc(buffer: ^[]byte, b : bool) { + if b { print_string_to_buffer(buffer, "true") } + else { print_string_to_buffer(buffer, "false") } +} + +print_pointer_to_buffer :: proc(buffer: ^[]byte, p: rawptr) #inline { print_uint_base_to_buffer(buffer, p as uint, 16, 0, #rune " ") } + +print_f32_to_buffer :: proc(buffer: ^[]byte, f: f32) #inline { print__f64(buffer, f as f64, 7) } +print_f64_to_buffer :: proc(buffer: ^[]byte, f: f64) #inline { print__f64(buffer, f, 10) } + +print__f64 :: proc(buffer: ^[]byte, f: f64, decimal_places: int) { + if f == 0 { + print_rune_to_buffer(buffer, #rune "0") + return + } + if f < 0 { + print_rune_to_buffer(buffer, #rune "-") + f = -f + } + + print_u64_to_buffer :: proc(buffer: ^[]byte, i: u64) { + buf: [22]byte + len := 0 + if i == 0 { + buf[len] = #rune "0" + len++ + } + for i > 0 { + buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % 10] + len++ + i /= 10 + } + byte_reverse(buf[:len]) + print_string_to_buffer(buffer, buf[:len] as string) + } + + i := f as u64 + print_u64_to_buffer(buffer, i) + f -= i as f64 + + print_rune_to_buffer(buffer, #rune ".") + + mult := 10.0 + for decimal_places := 6; decimal_places >= 0; decimal_places-- { + i = (f * mult) as u64 + print_u64_to_buffer(buffer, i as u64) + f -= i as f64 / mult + mult *= 10 + } +} + +print_type_to_buffer :: proc(buf: ^[]byte, ti: ^Type_Info) { + if ti == null { return } + + using Type_Info + match type info : ti { + case Named: + print_string_to_buffer(buf, info.name) + case Integer: + match { + case ti == type_info(int): + print_string_to_buffer(buf, "int") + case ti == type_info(uint): + print_string_to_buffer(buf, "uint") + default: + if info.signed { + print_string_to_buffer(buf, "i") + } else { + print_string_to_buffer(buf, "u") + } + print_int_to_buffer(buf, 8*info.size) + } + + case Float: + match info.size { + case 4: print_string_to_buffer(buf, "f32") + case 8: print_string_to_buffer(buf, "f64") + } + case String: print_string_to_buffer(buf, "string") + case Boolean: print_string_to_buffer(buf, "bool") + case Pointer: + print_string_to_buffer(buf, "^") + print_type_to_buffer(buf, info.elem) + case Procedure: + print_string_to_buffer(buf, "proc") + if info.params == null { + print_string_to_buffer(buf, "()") + } else { + count := (info.params as ^Tuple).fields.count + if count == 1 { print_string_to_buffer(buf, "(") } + print_type_to_buffer(buf, info.params) + if count == 1 { print_string_to_buffer(buf, ")") } + } + if info.results != null { + print_string_to_buffer(buf, " -> ") + print_type_to_buffer(buf, info.results) + } + case Tuple: + count := info.fields.count + if count != 1 { print_string_to_buffer(buf, "(") } + for i := 0; i < count; i++ { + if i > 0 { print_string_to_buffer(buf, ", ") } + + f := info.fields[i] + + if f.name.count > 0 { + print_string_to_buffer(buf, f.name) + print_string_to_buffer(buf, ": ") + } + print_type_to_buffer(buf, f.type_info) + } + if count != 1 { print_string_to_buffer(buf, ")") } + + case Array: + print_string_to_buffer(buf, "[") + print_int_to_buffer(buf, info.count) + print_string_to_buffer(buf, "]") + print_type_to_buffer(buf, info.elem) + case Slice: + print_string_to_buffer(buf, "[") + print_string_to_buffer(buf, "]") + print_type_to_buffer(buf, info.elem) + case Vector: + print_string_to_buffer(buf, "{") + print_int_to_buffer(buf, info.count) + print_string_to_buffer(buf, "}") + print_type_to_buffer(buf, info.elem) + + case Struct: + print_string_to_buffer(buf, "struct ") + if info.packed { print_string_to_buffer(buf, "#packed ") } + if info.ordered { print_string_to_buffer(buf, "#ordered ") } + print_string_to_buffer(buf, "{") + for i := 0; i < info.fields.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + print_any_to_buffer(buf, info.fields[i].name) + print_string_to_buffer(buf, ": ") + print_type_to_buffer(buf, info.fields[i].type_info) + } + print_string_to_buffer(buf, "}") + + case Union: + print_string_to_buffer(buf, "union {") + for i := 0; i < info.fields.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + print_any_to_buffer(buf, info.fields[i].name) + print_string_to_buffer(buf, ": ") + print_type_to_buffer(buf, info.fields[i].type_info) + } + print_string_to_buffer(buf, "}") + + case Raw_Union: + print_string_to_buffer(buf, "raw_union {") + for i := 0; i < info.fields.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + print_any_to_buffer(buf, info.fields[i].name) + print_string_to_buffer(buf, ": ") + print_type_to_buffer(buf, info.fields[i].type_info) + } + print_string_to_buffer(buf, "}") + + case Enum: + print_string_to_buffer(buf, "enum ") + print_type_to_buffer(buf, info.base) + print_string_to_buffer(buf, "{}") + } +} + + +print_any_to_buffer :: proc(buf: ^[]byte, arg: any) { + using Type_Info + match type info : arg.type_info { + case Named: + a: any + a.type_info = info.base + a.data = arg.data + match type b : info.base { + case Struct: + print_string_to_buffer(buf, info.name) + print_string_to_buffer(buf, "{") + for i := 0; i < b.fields.count; i++ { + f := b.fields[i]; + if i > 0 { + print_string_to_buffer(buf, ", ") + } + print_any_to_buffer(buf, f.name) + print_string_to_buffer(buf, " = ") + v: any + v.type_info = f.type_info + v.data = ptr_offset(arg.data as ^byte, f.offset) + print_any_to_buffer(buf, v) + } + print_string_to_buffer(buf, "}") + + default: + print_any_to_buffer(buf, a) + } + + case Integer: + if info.signed { + i: int = 0; + if arg.data != null { + match info.size { + case 1: i = (arg.data as ^i8)^ as int + case 2: i = (arg.data as ^i16)^ as int + case 4: i = (arg.data as ^i32)^ as int + case 8: i = (arg.data as ^i64)^ as int + case 16: i = (arg.data as ^i128)^ as int + } + } + print_int_to_buffer(buf, i) + } else { + i: uint = 0; + if arg.data != null { + match info.size { + case 1: i = (arg.data as ^u8)^ as uint + case 2: i = (arg.data as ^u16)^ as uint + case 4: i = (arg.data as ^u32)^ as uint + case 8: i = (arg.data as ^u64)^ as uint + case 16: i = (arg.data as ^u128)^ as uint + } + } + print_uint_to_buffer(buf, i) + } + + case Float: + f: f64 = 0 + if arg.data != null { + match info.size { + case 4: f = (arg.data as ^f32)^ as f64 + case 8: f = (arg.data as ^f64)^ as f64 + } + } + print_f64_to_buffer(buf, f) + + case String: + s := "" + if arg.data != null { + s = (arg.data as ^string)^ + } + print_string_to_buffer(buf, s) + + case Boolean: + v := false; + if arg.data != null { + v = (arg.data as ^bool)^ + } + print_bool_to_buffer(buf, v) + + case Pointer: + v := null; + if arg.data != null { + v = (arg.data as ^rawptr)^ + } + print_pointer_to_buffer(buf, v) + + case Enum: + v: any + v.data = arg.data + v.type_info = info.base + print_any_to_buffer(buf, v) + + + case Array: + print_string_to_buffer(buf, "[") + defer print_string_to_buffer(buf, "]") + + for i := 0; i < info.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + + elem: any + elem.data = (arg.data as int + i*info.elem_size) as rawptr + elem.type_info = info.elem + print_any_to_buffer(buf, elem) + } + + case Slice: + slice := arg.data as ^[]byte + print_string_to_buffer(buf, "[") + defer print_string_to_buffer(buf, "]") + + for i := 0; i < slice.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + + elem: any + elem.data = ptr_offset(slice.data, i*info.elem_size) + elem.type_info = info.elem + print_any_to_buffer(buf, elem) + } + + case Vector: + print_string_to_buffer(buf, "<") + defer print_string_to_buffer(buf, ">") + + for i := 0; i < info.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + + elem: any + elem.data = ptr_offset(arg.data as ^byte, i*info.elem_size) + elem.type_info = info.elem + print_any_to_buffer(buf, elem) + } + + + case Struct: + print_string_to_buffer(buf, "struct") + print_string_to_buffer(buf, "{") + defer print_string_to_buffer(buf, "}") + + for i := 0; i < info.fields.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + print_any_to_buffer(buf, info.fields[i].name) + print_string_to_buffer(buf, " = ") + a: any + a.data = ptr_offset(arg.data as ^byte, info.fields[i].offset) + a.type_info = info.fields[i].type_info + print_any_to_buffer(buf, a) + } + + case Union: + print_string_to_buffer(buf, "(union)") + case Raw_Union: + print_string_to_buffer(buf, "(raw_union)") + case Procedure: + print_type_to_buffer(buf, arg.type_info) + print_string_to_buffer(buf, " @ 0x") + print_pointer_to_buffer(buf, (arg.data as ^rawptr)^) + + default: + } +} + +type_info_is_string :: proc(info: ^Type_Info) -> bool { + using Type_Info + if info == null { + return false + } + + for { + match type i : info { + case Named: + info = i.base + continue + case String: + return true + default: + return false + } + } + return false +} + + +print_to_buffer :: proc(buf: ^[]byte, fmt: string, args: ..any) { + is_digit :: proc(r: rune) -> bool #inline { + return r >= #rune "0" && r <= #rune "9" + } + + parse_int :: proc(s: string, offset: int) -> (int, int) { + result := 0 + + for ; offset < s.count; offset++ { + c := s[offset] as rune + if !is_digit(c) { + break + } + + result *= 10 + result += (c - #rune "0") as int + } + + return result, offset + } + + prev := 0 + implicit_index := 0 + + for i := 0; i < fmt.count; i++ { + r := fmt[i] as rune + index := implicit_index + + if r != #rune "%" { + continue + } + + print_string_to_buffer(buf, fmt[prev:i]) + i++ // Skip % + if i < fmt.count { + next := fmt[i] as rune + + if next == #rune "%" { + print_string_to_buffer(buf, "%") + i++ + prev = i + continue + } + + if is_digit(next) { + index, i = parse_int(fmt, i) + } + } + + if 0 <= index && index < args.count { + print_any_to_buffer(buf, args[index]) + implicit_index = index+1 + } else { + // TODO(bill): Error check index out bounds + print_string_to_buffer(buf, "<invalid>") + } + + prev = i + } + + print_string_to_buffer(buf, fmt[prev:]) +} + +PRINT_BUF_SIZE :: 1<<12 + +print_to_file :: proc(f: ^os.File, fmt: string, args: ..any) { + data: [PRINT_BUF_SIZE]byte + buf := data[:0] + print_to_buffer(^buf, fmt, ..args) + os.write(f, buf) +} + +println_to_file :: proc(f: ^os.File, fmt: string, args: ..any) { + data: [PRINT_BUF_SIZE]byte + buf := data[:0] + print_to_buffer(^buf, fmt, ..args) + print_nl_to_buffer(^buf) + os.write(f, buf) +} + + +print :: proc(fmt: string, args: ..any) { + print_to_file(os.get_standard_file(os.File_Standard.OUTPUT), fmt, ..args) +} +print_err :: proc(fmt: string, args: ..any) { + print_to_file(os.get_standard_file(os.File_Standard.ERROR), fmt, ..args) +} +println :: proc(fmt: string, args: ..any) { + println_to_file(os.get_standard_file(os.File_Standard.OUTPUT), fmt, ..args) +} +println_err :: proc(fmt: string, args: ..any) { + println_to_file(os.get_standard_file(os.File_Standard.ERROR), fmt, ..args) +} diff --git a/code/os.odin b/code/os.odin new file mode 100644 index 000000000..dbfd7f723 --- /dev/null +++ b/code/os.odin @@ -0,0 +1,107 @@ +#import "runtime.odin" as _ // TODO(bill): make the compile import this automatically +#import "win32.odin" as win32 + +File :: type struct { + Handle :: type win32.HANDLE + handle: Handle +} + +open :: proc(name: string) -> (File, bool) { + using win32 + buf: [300]byte + copy(buf[:], name as []byte) + f := File{CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, 0, null)} + success := f.handle != INVALID_HANDLE_VALUE as File.Handle + + return f, success +} + +create :: proc(name: string) -> (File, bool) { + using win32 + buf: [300]byte + copy(buf[:], name as []byte) + f := File{ + handle = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, null, CREATE_ALWAYS, 0, null), + } + success := f.handle != INVALID_HANDLE_VALUE as File.Handle + return f, success +} + + +close :: proc(using f: ^File) { + win32.CloseHandle(handle) +} + +write :: proc(using f: ^File, buf: []byte) -> bool { + bytes_written: i32 + return win32.WriteFile(handle, buf.data, buf.count as i32, ^bytes_written, null) != 0 +} + + +File_Standard :: type enum { + INPUT, + OUTPUT, + ERROR, + COUNT, +} + +__std_files := __set_file_standards(); + +__set_file_standards :: proc() -> [File_Standard.COUNT as int]File { + return [File_Standard.COUNT as int]File{ + File{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)}, + File{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)}, + File{handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE)}, + } +} + +get_standard_file :: proc(std: File_Standard) -> ^File { + return ^__std_files[std] +} + + +read_entire_file :: proc(name: string) -> (string, bool) { + buf: [300]byte + copy(buf[:], name as []byte) + + f, file_ok := open(name) + if !file_ok { + return "", false + } + defer close(^f) + + length: i64 + file_size_ok := win32.GetFileSizeEx(f.handle as win32.HANDLE, ^length) != 0 + if !file_size_ok { + return "", false + } + + data := new_slice(u8, length) + if data.data == null { + return "", false + } + + single_read_length: i32 + total_read: i64 + + for total_read < length { + remaining := length - total_read + to_read: u32 + MAX :: 1<<32-1 + if remaining <= MAX { + to_read = remaining as u32 + } else { + to_read = MAX + } + + win32.ReadFile(f.handle as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, null) + if single_read_length <= 0 { + free(data.data) + return "", false + } + + total_read += single_read_length as i64 + } + + return data as string, true +} diff --git a/code/punity.odin b/code/punity.odin new file mode 100644 index 000000000..585d0b4b5 --- /dev/null +++ b/code/punity.odin @@ -0,0 +1,484 @@ +#import "win32.odin" as win32 +#import "fmt.odin" as _ + +CANVAS_WIDTH :: 128 +CANVAS_HEIGHT :: 128 +CANVAS_SCALE :: 3 +FRAME_TIME :: 1.0/30.0 +WINDOW_TITLE : string : "Punity\x00" + +_ := compile_assert(CANVAS_WIDTH % 16 == 0) + +WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE +WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE + + +STACK_CAPACITY :: 1<<20 +STORAGE_CAPACITY :: 1<<20 + +DRAW_LIST_RESERVE :: 128 + +MAX_KEYS :: 256 + +Core :: struct { + stack: ^Bank + storage: ^Bank + + running: bool + key_modifiers: u32 + key_states: [MAX_KEYS]byte + key_deltas: [MAX_KEYS]byte + + perf_frame, + perf_frame_inner, + perf_step, + perf_audio, + perf_blit, + perf_blit_cvt, + perf_blit_gdi: Perf_Span + + frame: i64 + + canvas: Canvas + draw_list: ^Draw_List +} + +Perf_Span :: struct { + stamp: f64 + delta: f32 +} + +Bank :: struct { + memory: []byte + cursor: int +} + +Bank_State :: struct { + state: Bank + bank: ^Bank +} + + +Color :: raw_union { + using channels: struct{ a, b, g, r: byte } + rgba: u32 +} + +Palette :: struct { + colors: [256]Color + colors_count: byte +} + + +Rect :: raw_union { + using minmax: struct { + min_x, min_y, max_x, max_y: int + } + using pos: struct { + left, top, right, bottom: int + } + e: [4]int +} + +Bitmap :: struct { + pixels: []byte + width: int + height: int +} + +Font :: struct { + using bitmap: Bitmap + char_width: int + char_height: int +} + +Canvas :: struct { + using bitmap: ^Bitmap + palette: Palette + translate_x: int + translate_y: int + clip: Rect + font: ^Font +} + +DrawFlag :: enum { + NONE = 0, + FLIP_H = 1<<0, + FLIP_V = 1<<1, + MASK = 1<<2, +} + + +Draw_List :: struct { + Item :: struct { + + } + items: []Item +} + +Key :: enum { + MOD_SHIFT = 0x0001, + MOD_CONTROL = 0x0002, + MOD_ALT = 0x0004, + MOD_SUPER = 0x0008, + + UNKNOWN =-1, + INVALID =-2, + + LBUTTON = 1, + RBUTTON = 2, + CANCEL = 3, + MBUTTON = 4, + + BACK = 8, + TAB = 9, + CLEAR = 12, + RETURN = 13, + SHIFT = 16, + CONTROL = 17, + MENU = 18, + PAUSE = 19, + CAPITAL = 20, + KANA = 0x15, + HANGEUL = 0x15, + HANGUL = 0x15, + JUNJA = 0x17, + FINAL = 0x18, + HANJA = 0x19, + KANJI = 0x19, + ESCAPE = 0x1B, + CONVERT = 0x1C, + NONCONVERT = 0x1D, + ACCEPT = 0x1E, + MODECHANGE = 0x1F, + SPACE = 32, + PRIOR = 33, + NEXT = 34, + END = 35, + HOME = 36, + LEFT = 37, + UP = 38, + RIGHT = 39, + DOWN = 40, + SELECT = 41, + PRINT = 42, + EXEC = 43, + SNAPSHOT = 44, + INSERT = 45, + DELETE = 46, + HELP = 47, + LWIN = 0x5B, + RWIN = 0x5C, + APPS = 0x5D, + SLEEP = 0x5F, + NUMPAD0 = 0x60, + NUMPAD1 = 0x61, + NUMPAD2 = 0x62, + NUMPAD3 = 0x63, + NUMPAD4 = 0x64, + NUMPAD5 = 0x65, + NUMPAD6 = 0x66, + NUMPAD7 = 0x67, + NUMPAD8 = 0x68, + NUMPAD9 = 0x69, + MULTIPLY = 0x6A, + ADD = 0x6B, + SEPARATOR = 0x6C, + SUBTRACT = 0x6D, + DECIMAL = 0x6E, + DIVIDE = 0x6F, + F1 = 0x70, + F2 = 0x71, + F3 = 0x72, + F4 = 0x73, + F5 = 0x74, + F6 = 0x75, + F7 = 0x76, + F8 = 0x77, + F9 = 0x78, + F10 = 0x79, + F11 = 0x7A, + F12 = 0x7B, + F13 = 0x7C, + F14 = 0x7D, + F15 = 0x7E, + F16 = 0x7F, + F17 = 0x80, + F18 = 0x81, + F19 = 0x82, + F20 = 0x83, + F21 = 0x84, + F22 = 0x85, + F23 = 0x86, + F24 = 0x87, + NUMLOCK = 0x90, + SCROLL = 0x91, + LSHIFT = 0xA0, + RSHIFT = 0xA1, + LCONTROL = 0xA2, + RCONTROL = 0xA3, + LMENU = 0xA4, + RMENU = 0xA5, + + APOSTROPHE = 39, /* ' */ + COMMA = 44, /* , */ + MINUS = 45, /* - */ + PERIOD = 46, /* . */ + SLASH = 47, /* / */ + NUM0 = 48, + NUM1 = 49, + NUM2 = 50, + NUM3 = 51, + NUM4 = 52, + NUM5 = 53, + NUM6 = 54, + NUM7 = 55, + NUM8 = 56, + NUM9 = 57, + SEMICOLON = 59, /* ; */ + EQUAL = 61, /* = */ + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + LEFT_BRACKET = 91, /* [ */ + BACKSLASH = 92, /* \ */ + RIGHT_BRACKET = 93, /* ] */ + GRAVE_ACCENT = 96, /* ` */ +} + + +key_down :: proc(k: Key) -> bool { + return _core.key_states[k] != 0 +} + +key_pressed :: proc(k: Key) -> bool { + return (_core.key_deltas[k] != 0) && key_down(k) +} + + + + +win32_perf_count_freq := win32.GetQueryPerformanceFrequency() +time_now :: proc() -> f64 { + assert(win32_perf_count_freq != 0) + + counter: i64 + win32.QueryPerformanceCounter(^counter) + result := counter as f64 / win32_perf_count_freq as f64 + return result +} + +_core: Core + +run :: proc(user_init, user_step: proc(c: ^Core)) { + using win32 + + + _core.running = true + + win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline #stdcall { + win32_app_key_mods :: proc() -> u32 { + mods: u32 = 0 + + if is_key_down(Key_Code.SHIFT) { + mods |= Key.MOD_SHIFT as u32; + } + if is_key_down(Key_Code.CONTROL) { + mods |= Key.MOD_CONTROL as u32; + } + if is_key_down(Key_Code.MENU) { + mods |= Key.MOD_ALT as u32; + } + if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) { + mods |= Key.MOD_SUPER as u32; + } + + return mods + } + + match msg { + case WM_KEYDOWN: + _core.key_modifiers = win32_app_key_mods() + if wparam < MAX_KEYS { + _core.key_states[wparam] = 1 + _core.key_deltas[wparam] = 1 + } + return 0 + + case WM_KEYUP: + _core.key_modifiers = win32_app_key_mods() + if wparam < MAX_KEYS { + _core.key_states[wparam] = 0 + _core.key_deltas[wparam] = 1 + } + return 0 + + case WM_CLOSE: + PostQuitMessage(0) + _core.running = false + return 0 + } + + return DefWindowProcA(hwnd, msg, wparam, lparam) + } + + + window_class := WNDCLASSEXA{ + class_name = ("Punity\x00" as string).data, // C-style string + size = size_of(WNDCLASSEXA) as u32, + style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + instance = GetModuleHandleA(null) as HINSTANCE, + wnd_proc = win32_proc, + // wnd_proc = DefWindowProcA, + background = GetStockObject(BLACK_BRUSH) as HBRUSH, + } + + if RegisterClassExA(^window_class) == 0 { + /*fmt.*/println_err("RegisterClassExA failed") + return + } + + screen_width := GetSystemMetrics(SM_CXSCREEN) + screen_height := GetSystemMetrics(SM_CYSCREEN) + + rc: RECT + rc.left = (screen_width - WINDOW_WIDTH) / 2 + rc.top = (screen_height - WINDOW_HEIGHT) / 2 + rc.right = rc.left + WINDOW_WIDTH + rc.bottom = rc.top + WINDOW_HEIGHT + + style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX + assert(AdjustWindowRect(^rc, style, 0) != 0) + + wt := WINDOW_TITLE + + win32_window := CreateWindowExA(0, + window_class.class_name, + WINDOW_TITLE.data, + // wt.data, + style, + rc.left, rc.top, + rc.right-rc.left, rc.bottom-rc.top, + null, null, window_class.instance, + null); + + if win32_window == null { + /*fmt.*/println_err("CreateWindowExA failed") + return + } + + + window_bmi: BITMAPINFO; + window_bmi.size = size_of(BITMAPINFO.HEADER) as u32 + window_bmi.width = CANVAS_WIDTH + window_bmi.height = CANVAS_HEIGHT + window_bmi.planes = 1 + window_bmi.bit_count = 32 + window_bmi.compression = BI_RGB + + + user_init(^_core) + + + ShowWindow(win32_window, SW_SHOW) + + window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT); + assert(window_buffer.data != null) + defer free(window_buffer.data) + + for i := 0; i < window_buffer.count; i++ { + window_buffer[i] = 0xff00ff + } + + + prev_time, curr_time,dt: f64 + prev_time = time_now() + curr_time = time_now() + total_time : f64 = 0 + offset_x := 0; + offset_y := 0; + + message: MSG + for _core.running { + curr_time = time_now() + dt = curr_time - prev_time + prev_time = curr_time + total_time += dt + + offset_x += 1 + offset_y += 2 + + { + data: [128]byte + buf := data[:0] + /*fmt.*/print_to_buffer(^buf, "Punity: % ms\x00", dt*1000) + win32.SetWindowTextA(win32_window, buf.data) + } + + + for y := 0; y < CANVAS_HEIGHT; y++ { + for x := 0; x < CANVAS_WIDTH; x++ { + g := (x % 32) * 8 + b := (y % 32) * 8 + window_buffer[x + y*CANVAS_WIDTH] = (g << 8 | b) as u32 + } + } + + memory_zero(^_core.key_deltas[0], size_of(_core.key_deltas[0])) + + + for PeekMessageA(^message, null, 0, 0, PM_REMOVE) != 0 { + if message.message == WM_QUIT { + _core.running = false + } + TranslateMessage(^message) + DispatchMessageA(^message) + } + + user_step(^_core) + + dc := GetDC(win32_window); + StretchDIBits(dc, + 0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE, + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + window_buffer.data, + ^window_bmi, + DIB_RGB_COLORS, + SRCCOPY) + ReleaseDC(win32_window, dc) + + + { + delta := time_now() - prev_time + ms := ((FRAME_TIME - delta) * 1000) as i32 + if ms > 0 { + win32.Sleep(ms) + } + } + + _core.frame++ + } +} |