Ignore win32 Pong app #19
			
				
			
		
		
		
	|  | @ -45,6 +45,11 @@ if %target% == orca ( | |||
| 	        --guest-include graphics.h^ | ||||
| 	        --wasm3-bindings src\canvas_api_bind_gen.c | ||||
| 
 | ||||
| 	python3 scripts\bindgen2.py clock src\clock_api.json^ | ||||
| 	        --guest-stubs sdk\orca_clock.c^ | ||||
| 	        --guest-include platform_clock.h^ | ||||
| 	        --wasm3-bindings src\clock_api_bind_gen.c | ||||
| 
 | ||||
| 	python3 scripts\bindgen2.py io^ | ||||
| 	        src\io_api.json^ | ||||
| 	        --guest-stubs sdk\io_stubs.c^ | ||||
|  |  | |||
							
								
								
									
										6
									
								
								build.sh
								
								
								
								
							
							
						
						
									
										6
									
								
								build.sh
								
								
								
								
							|  | @ -67,6 +67,12 @@ elif [ $target = orca ] ; then | |||
| 		--guest-include graphics.h \ | ||||
| 		--wasm3-bindings ./src/canvas_api_bind_gen.c | ||||
| 
 | ||||
| 	python3 ./scripts/bindgen2.py clock \ | ||||
| 		src/clock_api.json \ | ||||
| 		--guest-stubs sdk/orca_clock.c \ | ||||
| 		--guest-include platform_clock.h \ | ||||
| 		--wasm3-bindings ./src/clock_api_bind_gen.c | ||||
| 
 | ||||
| 	python3 ./scripts/bindgen2.py io \ | ||||
| 		src/io_api.json \ | ||||
| 		--guest-stubs sdk/io_stubs.c \ | ||||
|  |  | |||
							
								
								
									
										2
									
								
								milepost
								
								
								
								
							
							
								
								
								
								
								
								
							
						
						
									
										2
									
								
								milepost
								
								
								
								
							|  | @ -1 +1 @@ | |||
| Subproject commit 59fdc27ac6dd84af4c9ed505213dea0fec641c32 | ||||
| Subproject commit 2a4e90cf9aa88507640527a06b7ae08f25937155 | ||||
|  | @ -26,7 +26,7 @@ wasmFlags="--target=wasm32 \ | |||
|   -D__ORCA__ \ | ||||
|   -I $STDLIB_DIR/include \ | ||||
|   -I $ORCA_SDK_DIR \ | ||||
|   -I $MILEPOST_DIR/ext -I $MILEPOST_DIR -I $MILEPOST_DIR/src -I $MILEPOST_DIR/src/util -I $MILEPOST_DIR/src/platform" | ||||
|   -I $MILEPOST_DIR/ext -I $MILEPOST_DIR -I $MILEPOST_DIR/src" | ||||
| 
 | ||||
| $CLANG $wasmFlags -o ./module.wasm ../../sdk/orca.c ../../cstdlib/src/*.c src/main.c | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| UI | ||||
|  | @ -0,0 +1,17 @@ | |||
| @echo off | ||||
| 
 | ||||
| :: compile wasm module | ||||
| set wasmFlags=--target=wasm32^ | ||||
|        --no-standard-libraries ^ | ||||
|        -fno-builtin ^ | ||||
|        -Wl,--no-entry ^ | ||||
|        -Wl,--export-dynamic ^ | ||||
|        -g ^ | ||||
|        -O2 ^ | ||||
|        -mbulk-memory ^ | ||||
|        -D__ORCA__ ^ | ||||
|        -isystem ..\..\cstdlib\include -I ..\..\sdk -I..\..\milepost\ext -I ..\..\milepost -I ..\..\milepost\src | ||||
| 
 | ||||
| clang %wasmFlags% -o .\module.wasm ..\..\sdk\orca.c ..\..\cstdlib\src\*.c src\main.c | ||||
| 
 | ||||
| python3 ..\..\scripts\mkapp.py --orca-dir ..\.. --name UI --resource-dir data module.wasm | ||||
|  | @ -0,0 +1,27 @@ | |||
| #!/bin/bash | ||||
| 
 | ||||
| set -euo pipefail | ||||
| 
 | ||||
| if [[ -x /usr/local/opt/llvm/bin/clang ]]; then | ||||
|   CLANG=/usr/local/opt/llvm/bin/clang | ||||
| elif [[ -x /opt/homebrew/opt/llvm/bin/clang ]]; then | ||||
|   CLANG=/opt/homebrew/opt/llvm/bin/clang | ||||
| else | ||||
|   echo "Could not find Homebrew clang; this script will probably not work." | ||||
|   CLANG=clang | ||||
| fi | ||||
| 
 | ||||
| wasmFlags="--target=wasm32 \ | ||||
|   --no-standard-libraries \ | ||||
|   -fno-builtin \ | ||||
|   -Wl,--no-entry \ | ||||
|   -Wl,--export-dynamic \ | ||||
|   -g \ | ||||
|   -O2 \ | ||||
|   -mbulk-memory \ | ||||
|   -D__ORCA__ \ | ||||
|   -isystem ../../cstdlib/include -I ../../sdk -I../../milepost/ext -I ../../milepost -I ../../milepost/src" | ||||
| 
 | ||||
| $CLANG $wasmFlags -o ./module.wasm ../../sdk/orca.c ../../cstdlib/src/*.c src/main.c | ||||
| 
 | ||||
| python3 ../../scripts/mkapp.py --orca-dir ../.. --name UI --resource-dir data module.wasm | ||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,407 @@ | |||
| #include"keys.h" | ||||
| #include"graphics.h" | ||||
| #include"ui.h" | ||||
| 
 | ||||
| #include"orca.h" | ||||
| 
 | ||||
| vec2 frameSize = {100, 100}; | ||||
| 
 | ||||
| mg_surface surface; | ||||
| mg_canvas canvas; | ||||
| mg_font font; | ||||
| ui_context ui; | ||||
| mem_arena textArena = {0}; | ||||
| 
 | ||||
| mg_surface mg_surface_main(void); | ||||
| 
 | ||||
| ORCA_EXPORT void OnInit(void) | ||||
| { | ||||
| 	//TODO create surface for main window
 | ||||
| 	surface = mg_surface_main(); | ||||
| 	canvas = mg_canvas_create(); | ||||
| 	ui_init(&ui); | ||||
| 
 | ||||
| 	//NOTE: load font
 | ||||
| 	{ | ||||
| 		file_handle file = file_open(STR8("/OpenSansLatinSubset.ttf"), FILE_ACCESS_READ, 0); | ||||
| 		if(file_last_error(file) != IO_OK) | ||||
| 		{ | ||||
| 			log_error("Couldn't open file OpenSansLatinSubset.ttf\n"); | ||||
| 		} | ||||
| 		u64 size = file_size(file); | ||||
| 		char* buffer = mem_arena_alloc(mem_scratch(), size); | ||||
| 		file_read(file, size, buffer); | ||||
| 		file_close(file); | ||||
| 		unicode_range ranges[5] = {UNICODE_RANGE_BASIC_LATIN, | ||||
| 		                       UNICODE_RANGE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT, | ||||
| 		                       UNICODE_RANGE_LATIN_EXTENDED_A, | ||||
| 		                       UNICODE_RANGE_LATIN_EXTENDED_B, | ||||
| 		                       UNICODE_RANGE_SPECIALS}; | ||||
| 		// TODO: Decide whether we're using strings or explicit pointer + length
 | ||||
| 		font = mg_font_create_from_memory(size, (byte*)buffer, 5, ranges); | ||||
| 	} | ||||
| 
 | ||||
| 	mem_arena_clear(mem_scratch()); | ||||
| 	mem_arena_init(&textArena); | ||||
| } | ||||
| 
 | ||||
| ORCA_EXPORT void OnFrameResize(u32 width, u32 height) | ||||
| { | ||||
| 	log_info("frame resize %u, %u", width, height); | ||||
| 	frameSize.x = width; | ||||
| 	frameSize.y = height; | ||||
| } | ||||
| 
 | ||||
| ORCA_EXPORT void OnRawEvent(mp_event *event) | ||||
| { | ||||
| 	ui_process_event(event); | ||||
| } | ||||
| 
 | ||||
| void widget_begin_view(char* str) | ||||
| { | ||||
| 	ui_style_next(&(ui_style){.layout.axis = UI_AXIS_Y, | ||||
| 	                          .layout.spacing = 10, | ||||
| 	                          .layout.margin.x = 10, | ||||
| 	                          .layout.margin.y = 10, | ||||
| 	                          .layout.align.x = UI_ALIGN_CENTER, | ||||
| 	                          .layout.align.y = UI_ALIGN_START}, | ||||
| 	               UI_STYLE_LAYOUT); | ||||
| 
 | ||||
| 	ui_box_begin(str, UI_FLAG_DRAW_BORDER); | ||||
| 	ui_label(str); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void widget_end_view(void) | ||||
| { | ||||
| 	ui_box_end(); | ||||
| } | ||||
| 
 | ||||
| #define widget_view(s) defer_loop(widget_begin_view(s), widget_end_view()) | ||||
| 
 | ||||
| ORCA_EXPORT void OnFrameRefresh(void) | ||||
| { | ||||
| 	ui_style defaultStyle = {.bgColor = {0}, | ||||
| 	                         .color = {1, 1, 1, 1}, | ||||
| 	                         .font = font, | ||||
| 	                         .fontSize = 16, | ||||
| 	                         .borderColor = {0.278, 0.333, 0.412, 1}, | ||||
| 		                     .borderSize = 2}; | ||||
| 
 | ||||
| 	ui_style_mask defaultMask = UI_STYLE_BG_COLOR | ||||
| 	                          | UI_STYLE_COLOR | ||||
| 	                          | UI_STYLE_BORDER_COLOR | ||||
| 	                          | UI_STYLE_BORDER_SIZE | ||||
| 	                          | UI_STYLE_FONT | ||||
| 	                          | UI_STYLE_FONT_SIZE; | ||||
| 
 | ||||
| 	ui_frame(frameSize, &defaultStyle, defaultMask) | ||||
| 	{ | ||||
| 		ui_style_match_before(ui_pattern_all(), &defaultStyle, defaultMask); | ||||
| 
 | ||||
| 		ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1}, | ||||
| 		                          .size.height = {UI_SIZE_PARENT, 1}, | ||||
| 		                          .layout.axis = UI_AXIS_Y, | ||||
| 		                          .layout.align.x = UI_ALIGN_CENTER, | ||||
| 		                          .layout.align.y = UI_ALIGN_START, | ||||
| 		                          .layout.spacing = 10, | ||||
| 		                          .layout.margin.x = 10, | ||||
| 		                          .layout.margin.y = 10, | ||||
| 		                          .bgColor = {0.11, 0.11, 0.11, 1}}, | ||||
| 		                UI_STYLE_SIZE | ||||
| 		              | UI_STYLE_LAYOUT | ||||
| 		              | UI_STYLE_BG_COLOR); | ||||
| 
 | ||||
| 		ui_container("background", UI_FLAG_DRAW_BACKGROUND) | ||||
| 		{ | ||||
| 			ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1}, | ||||
| 			                          .size.height = {UI_SIZE_CHILDREN}, | ||||
| 			                          .layout.align.x = UI_ALIGN_CENTER}, | ||||
| 			               UI_STYLE_SIZE | ||||
| 			              |UI_STYLE_LAYOUT_ALIGN_X); | ||||
| 			ui_container("title", 0) | ||||
| 			{ | ||||
| 				ui_style_next(&(ui_style){.fontSize = 26}, UI_STYLE_FONT_SIZE); | ||||
| 				ui_label("Orca UI Demo"); | ||||
| 
 | ||||
| 				if(ui_box_sig(ui_box_top()).hovering) | ||||
| 				{ | ||||
| 					ui_tooltip("tooltip") | ||||
| 					{ | ||||
| 						ui_style_next(&(ui_style){.bgColor = {1, 0.99, 0.82, 1}}, | ||||
| 						               UI_STYLE_BG_COLOR); | ||||
| 
 | ||||
| 						ui_container("background", UI_FLAG_DRAW_BACKGROUND) | ||||
| 						{ | ||||
| 							ui_style_next(&(ui_style){.color = {0, 0, 0, 1}}, | ||||
| 							               UI_STYLE_COLOR); | ||||
| 
 | ||||
| 							ui_label("That is a tooltip!"); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			ui_menu_bar("Menu bar") | ||||
| 			{ | ||||
| 				ui_menu("Menu 1") | ||||
| 				{ | ||||
| 					if(ui_menu_button("Option 1.1").pressed) | ||||
| 					{ | ||||
| 						log_info("Pressed option 1.1\n"); | ||||
| 					} | ||||
| 					ui_menu_button("Option 1.2"); | ||||
| 					ui_menu_button("Option 1.3"); | ||||
| 					ui_menu_button("Option 1.4"); | ||||
| 				} | ||||
| 
 | ||||
| 				ui_menu("Menu 2") | ||||
| 				{ | ||||
| 					ui_menu_button("Option 2.1"); | ||||
| 					ui_menu_button("Option 2.2"); | ||||
| 					ui_menu_button("Option 2.3"); | ||||
| 					ui_menu_button("Option 2.4"); | ||||
| 				} | ||||
| 
 | ||||
| 				ui_menu("Menu 3") | ||||
| 				{ | ||||
| 					ui_menu_button("Option 3.1"); | ||||
| 					ui_menu_button("Option 3.2"); | ||||
| 					ui_menu_button("Option 3.3"); | ||||
| 					ui_menu_button("Option 3.4"); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1}, | ||||
| 			                          .size.height = {UI_SIZE_PARENT, 1, 1}}, | ||||
| 			                          UI_STYLE_SIZE); | ||||
| 
 | ||||
| 			ui_style_next(&(ui_style){.layout.axis = UI_AXIS_X}, UI_STYLE_LAYOUT_AXIS); | ||||
| 			ui_container("contents", 0) | ||||
| 			{ | ||||
| 				ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 0.5}, | ||||
| 				                          .size.height = {UI_SIZE_PARENT, 1}}, | ||||
| 				              UI_STYLE_SIZE); | ||||
| 
 | ||||
| 				ui_container("left", 0) | ||||
| 				{ | ||||
| 					ui_style_next(&(ui_style){.layout.axis = UI_AXIS_X, | ||||
| 					                          .layout.spacing = 10, | ||||
| 					                          .layout.margin.x = 10, | ||||
| 					                          .layout.margin.y = 10, | ||||
| 					                          .size.width = {UI_SIZE_PARENT, 1}, | ||||
| 					                          .size.height = {UI_SIZE_PARENT, 0.5}}, | ||||
| 					               UI_STYLE_LAYOUT_AXIS | ||||
| 					              |UI_STYLE_LAYOUT_SPACING | ||||
| 					              |UI_STYLE_LAYOUT_MARGIN_X | ||||
| 					              |UI_STYLE_LAYOUT_MARGIN_Y | ||||
| 					              |UI_STYLE_SIZE); | ||||
| 
 | ||||
| 					ui_container("up", 0) | ||||
| 					{ | ||||
| 						ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 0.5}, | ||||
| 						                          .size.height = {UI_SIZE_PARENT, 1}}, | ||||
| 						             UI_STYLE_SIZE); | ||||
| 						widget_view("Buttons") | ||||
| 						{ | ||||
| 							if(ui_button("Button A").clicked) | ||||
| 							{ | ||||
| 								log_info("A clicked"); | ||||
| 							} | ||||
| 
 | ||||
| 							if(ui_button("Button B").clicked) | ||||
| 							{ | ||||
| 								log_info("B clicked"); | ||||
| 							} | ||||
| 
 | ||||
| 							if(ui_button("Button C").clicked) | ||||
| 							{ | ||||
| 								log_info("C clicked"); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 0.5}, | ||||
| 						                          .size.height = {UI_SIZE_PARENT, 1}}, | ||||
| 						              UI_STYLE_SIZE); | ||||
| 
 | ||||
| 
 | ||||
| 						ui_pattern pattern = {0}; | ||||
| 						ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TAG, .tag = ui_tag_make("checkbox")}); | ||||
| 						ui_style_match_after(pattern, | ||||
| 						                     &(ui_style){.bgColor = {0, 1, 0, 1}, | ||||
| 						                                 .color = {1, 1, 1, 1}}, | ||||
| 						                     UI_STYLE_COLOR | UI_STYLE_BG_COLOR); | ||||
| 
 | ||||
| 						widget_view("checkboxes") | ||||
| 						{ | ||||
| 							static bool check1 = true; | ||||
| 							static bool check2 = false; | ||||
| 							static bool check3 = false; | ||||
| 
 | ||||
| 							ui_checkbox("check1", &check1); | ||||
| 							ui_checkbox("check2", &check2); | ||||
| 							ui_checkbox("check3", &check3); | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					ui_style_next(&(ui_style){.layout.axis = UI_AXIS_X, | ||||
| 					                          .size.width = {UI_SIZE_PARENT, 1}, | ||||
| 					                          .size.height = {UI_SIZE_PARENT, 0.5}}, | ||||
| 					               UI_STYLE_LAYOUT_AXIS | ||||
| 					              |UI_STYLE_SIZE); | ||||
| 
 | ||||
| 					ui_container("down", 0) | ||||
| 					{ | ||||
| 						widget_view("Vertical Sliders") | ||||
| 						{ | ||||
| 							ui_style_next(&(ui_style){.layout.axis = UI_AXIS_X, | ||||
| 							                          .layout.spacing = 10}, | ||||
| 							               UI_STYLE_LAYOUT_AXIS | ||||
| 							              |UI_STYLE_LAYOUT_SPACING); | ||||
| 							ui_container("contents", 0) | ||||
| 							{ | ||||
| 								ui_style_next(&(ui_style){.size.width = {UI_SIZE_PIXELS, 20}, | ||||
| 								                          .size.height = {UI_SIZE_PIXELS, 200}}, | ||||
| 								              UI_STYLE_SIZE); | ||||
| 								static f32 slider1 = 0; | ||||
| 								ui_slider("slider1", 0.2, &slider1); | ||||
| 
 | ||||
| 								ui_style_next(&(ui_style){.size.width = {UI_SIZE_PIXELS, 20}, | ||||
| 								                          .size.height = {UI_SIZE_PIXELS, 200}}, | ||||
| 								              UI_STYLE_SIZE); | ||||
| 								static f32 slider2 = 0; | ||||
| 								ui_slider("slider2", 0.2, &slider2); | ||||
| 
 | ||||
| 								ui_style_next(&(ui_style){.size.width = {UI_SIZE_PIXELS, 20}, | ||||
| 								                          .size.height = {UI_SIZE_PIXELS, 200}}, | ||||
| 								              UI_STYLE_SIZE); | ||||
| 								static f32 slider3 = 0; | ||||
| 								ui_slider("slider3", 0.2, &slider3); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						widget_view("Horizontal Sliders") | ||||
| 						{ | ||||
| 							ui_style_next(&(ui_style){.size.width = {UI_SIZE_PIXELS, 200}, | ||||
| 							                          .size.height = {UI_SIZE_PIXELS, 20}}, | ||||
| 							              UI_STYLE_SIZE); | ||||
| 							static f32 slider1 = 0; | ||||
| 							ui_slider("slider1", 0.2, &slider1); | ||||
| 
 | ||||
| 							ui_style_next(&(ui_style){.size.width = {UI_SIZE_PIXELS, 200}, | ||||
| 							                          .size.height = {UI_SIZE_PIXELS, 20}}, | ||||
| 							              UI_STYLE_SIZE); | ||||
| 							static f32 slider2 = 0; | ||||
| 							ui_slider("slider2", 0.2, &slider2); | ||||
| 
 | ||||
| 							ui_style_next(&(ui_style){.size.width = {UI_SIZE_PIXELS, 200}, | ||||
| 							                          .size.height = {UI_SIZE_PIXELS, 20}}, | ||||
| 							              UI_STYLE_SIZE); | ||||
| 							static f32 slider3 = 0; | ||||
| 							ui_slider("slider3", 0.2, &slider3); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 0.5}, | ||||
| 				                          .size.height = {UI_SIZE_PARENT, 1}}, | ||||
| 				              UI_STYLE_SIZE); | ||||
| 
 | ||||
| 				ui_container("right", 0) | ||||
| 				{ | ||||
| 
 | ||||
| 					ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1}, | ||||
| 					                          .size.height = {UI_SIZE_PARENT, 0.33}}, | ||||
| 					             UI_STYLE_SIZE); | ||||
| 					widget_view("Text box") | ||||
| 					{ | ||||
| 						ui_style_next(&(ui_style){.size.width = {UI_SIZE_PIXELS, 300}, | ||||
| 						                          .size.height = {UI_SIZE_TEXT}}, | ||||
| 						              UI_STYLE_SIZE); | ||||
| 						static str8 text = {0}; | ||||
| 						ui_text_box_result res = ui_text_box("textbox", mem_scratch(), text); | ||||
| 						if(res.changed) | ||||
| 						{ | ||||
| 							mem_arena_clear(&textArena); | ||||
| 							text = str8_push_copy(&textArena, res.text); | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1}, | ||||
| 					                          .size.height = {UI_SIZE_PARENT, 0.33}}, | ||||
| 					             UI_STYLE_SIZE); | ||||
| 					widget_view("Test") | ||||
| 					{ | ||||
| 						ui_pattern pattern = {0}; | ||||
| 						ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = STR8("panel")}); | ||||
| 						ui_style_match_after(pattern, &(ui_style){.bgColor = {0.3, 0.3, 1, 1}}, UI_STYLE_BG_COLOR); | ||||
| 
 | ||||
| 						static int selected = 0; | ||||
| 						str8 options[] = {STR8("option 1"), | ||||
| 						                  STR8("option 2"), | ||||
| 						                  STR8("long option 3"), | ||||
| 						                  STR8("option 4"), | ||||
| 						                  STR8("option 5")}; | ||||
| 						ui_select_popup_info info = {.selectedIndex = selected, | ||||
| 						                             .optionCount = 5, | ||||
| 						                             .options = options}; | ||||
| 
 | ||||
| 						ui_select_popup_info result = ui_select_popup("popup", &info); | ||||
| 						selected = result.selectedIndex; | ||||
| 					} | ||||
| 
 | ||||
| 					ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1}, | ||||
| 					                          .size.height = {UI_SIZE_PARENT, 0.33}}, | ||||
| 					             UI_STYLE_SIZE); | ||||
| 					widget_view("Color") | ||||
| 					{ | ||||
| 						ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1}, | ||||
| 						                          .size.height = {UI_SIZE_PARENT, 0.7}, | ||||
| 						                          .layout.axis = UI_AXIS_X}, | ||||
| 						               UI_STYLE_SIZE | ||||
| 						              |UI_STYLE_LAYOUT_AXIS); | ||||
| 
 | ||||
| 						ui_panel("Panel", UI_FLAG_DRAW_BORDER) | ||||
| 						{ | ||||
| 							ui_style_next(&(ui_style){.layout.axis = UI_AXIS_X}, | ||||
| 							              UI_STYLE_LAYOUT_AXIS); | ||||
| 							ui_container("contents", 0) | ||||
| 							{ | ||||
| 								ui_style_next(&(ui_style){.layout.spacing = 20}, | ||||
| 								              UI_STYLE_LAYOUT_SPACING); | ||||
| 								ui_container("buttons", 0) | ||||
| 								{ | ||||
| 									ui_button("Button A"); | ||||
| 									ui_button("Button B"); | ||||
| 									ui_button("Button C"); | ||||
| 									ui_button("Button D"); | ||||
| 								} | ||||
| 
 | ||||
| 								ui_style_next(&(ui_style){.layout.axis = UI_AXIS_X, | ||||
| 								                          .layout.spacing = 20}, | ||||
| 								              UI_STYLE_LAYOUT_SPACING | ||||
| 								              |UI_STYLE_LAYOUT_AXIS); | ||||
| 
 | ||||
| 								ui_container("buttons2", 0) | ||||
| 								{ | ||||
| 									ui_button("Button A"); | ||||
| 									ui_button("Button B"); | ||||
| 									ui_button("Button C"); | ||||
| 									ui_button("Button D"); | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	mg_canvas_set_current(canvas); | ||||
|     mg_surface_prepare(surface); | ||||
| 	ui_draw(); | ||||
|     mg_render(surface, canvas); | ||||
|     mg_surface_present(surface); | ||||
| } | ||||
|  | @ -124,7 +124,7 @@ for decl in data: | |||
| 			s += '*((i64*)&_sp[0]) = ' | ||||
| 		elif retTag == 'f': | ||||
| 			s += '*((f32*)&_sp[0]) = ' | ||||
| 		elif retTag == 'd': | ||||
| 		elif retTag == 'F': | ||||
| 			s += '*((f64*)&_sp[0]) = ' | ||||
| 		elif retTag == 'S': | ||||
| 			retTypeName = decl['ret']['name'] | ||||
|  | @ -147,7 +147,7 @@ for decl in data: | |||
| 				s += '*(i64*)&_sp[' + str(firstArgIndex + i) + ']' | ||||
| 			elif argTag == 'f': | ||||
| 				s += '*(f32*)&_sp[' + str(firstArgIndex + i) + ']' | ||||
| 			elif argTag == 'd': | ||||
| 			elif argTag == 'F': | ||||
| 				s += '*(f64*)&_sp[' + str(firstArgIndex + i) + ']' | ||||
| 			elif argTag == 'p': | ||||
| 				s += '(void*)((char*)_mem + *(i32*)&_sp[' + str(firstArgIndex + i) + '])' | ||||
|  |  | |||
|  | @ -6,17 +6,22 @@ | |||
| * | ||||
| *****************************************************************/ | ||||
| 
 | ||||
| #include"platform/orca_clock.c" | ||||
| #include"platform/orca_memory.c" | ||||
| #include"platform/orca_malloc.c" | ||||
| #include"platform/orca_strings.c" | ||||
| #include"platform/orca_log.c" | ||||
| #include"platform/platform_io_common.c" | ||||
| 
 | ||||
| #include"util/hash.c" | ||||
| #include"util/memory.c" | ||||
| #include"util/strings.c" | ||||
| #include"util/utf8.c" | ||||
| 
 | ||||
| #include"graphics_common.c" | ||||
| #include"input_state.c" | ||||
| #include"orca_exports.c" | ||||
| #include"orca_surface.c" | ||||
| #include"ui.c" | ||||
| 
 | ||||
| #include"io_stubs.c" | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| #include"platform/platform.h" | ||||
| #include"platform/platform_log.h" | ||||
| #include"platform/platform_assert.h" | ||||
| #include"platform/platform_clock.h" | ||||
| #include"platform/platform_io.h" | ||||
| 
 | ||||
| #if COMPILER_CLANG | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| #include"orca.h" | ||||
| 
 | ||||
| ORCA_EXPORT mp_event _OrcaRawEvent; | ||||
|  | @ -0,0 +1,9 @@ | |||
| [ | ||||
| { | ||||
| 	"name": "mp_get_time", | ||||
| 	"cname": "mp_get_time", | ||||
| 	"ret": {"name": "time", "tag": "F"}, | ||||
| 	"args": [ {"name": "clock", | ||||
| 	           "type": {"name": "mp_clock_kind", "tag": "i"}}] | ||||
| } | ||||
| ] | ||||
							
								
								
									
										63
									
								
								src/main.c
								
								
								
								
							
							
						
						
									
										63
									
								
								src/main.c
								
								
								
								
							|  | @ -300,6 +300,7 @@ void orca_runtime_init(orca_runtime* runtime) | |||
| 
 | ||||
| #include"bindgen_core_api.c" | ||||
| #include"canvas_api_bind.c" | ||||
| #include"clock_api_bind_gen.c" | ||||
| #include"io_api_bind_gen.c" | ||||
| 
 | ||||
| #include"bindgen_gles_api.c" | ||||
|  | @ -356,6 +357,7 @@ void* orca_runloop(void* user) | |||
| 	//NOTE: bind orca APIs
 | ||||
| 	bindgen_link_core_api(app->runtime.m3Module); | ||||
| 	bindgen_link_canvas_api(app->runtime.m3Module); | ||||
| 	bindgen_link_clock_api(app->runtime.m3Module); | ||||
| 	bindgen_link_io_api(app->runtime.m3Module); | ||||
| 	bindgen_link_gles_api(app->runtime.m3Module); | ||||
| 	manual_link_gles_api(app->runtime.m3Module); | ||||
|  | @ -380,9 +382,9 @@ void* orca_runloop(void* user) | |||
| 	} | ||||
| 
 | ||||
| 	//NOTE: Find and type check event handlers.
 | ||||
| 	for(int i=0; i<G_EVENT_COUNT; i++) | ||||
| 	for(int i=0; i<G_EXPORT_COUNT; i++) | ||||
| 	{ | ||||
| 		const g_event_handler_desc* desc = &G_EVENT_HANDLER_DESC[i]; | ||||
| 		const g_export_desc* desc = &G_EXPORT_DESC[i]; | ||||
| 		IM3Function handler = 0; | ||||
| 		m3_FindFunction(&handler, app->runtime.m3Runtime, desc->name.ptr); | ||||
| 
 | ||||
|  | @ -425,7 +427,7 @@ void* orca_runloop(void* user) | |||
| 
 | ||||
| 			if(checked) | ||||
| 			{ | ||||
| 				app->runtime.eventHandlers[i] = handler; | ||||
| 				app->runtime.exports[i] = handler; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
|  | @ -434,6 +436,10 @@ void* orca_runloop(void* user) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	//NOTE: get location of the raw event slot
 | ||||
| 	IM3Global rawEventGlobal = m3_FindGlobal(app->runtime.m3Module, "_OrcaRawEvent"); | ||||
| 	app->runtime.rawEventOffset = (u32)rawEventGlobal->intValue; | ||||
| 
 | ||||
| 	//NOTE: preopen the app local root dir
 | ||||
| 	{ | ||||
| 		str8 localRootPath = path_executable_relative(mem_scratch(), STR8("../app/data")); | ||||
|  | @ -449,12 +455,12 @@ void* orca_runloop(void* user) | |||
| 	//NOTE: prepare GL surface
 | ||||
| 	mg_surface_prepare(app->surface); | ||||
| 
 | ||||
| 	IM3Function* eventHandlers = app->runtime.eventHandlers; | ||||
| 	IM3Function* exports = app->runtime.exports; | ||||
| 
 | ||||
| 	//NOTE: call init handler
 | ||||
| 	if(eventHandlers[G_EVENT_START]) | ||||
| 	if(exports[G_EXPORT_ON_INIT]) | ||||
| 	{ | ||||
| 		M3Result err = m3_Call(eventHandlers[G_EVENT_START], 0, 0); | ||||
| 		M3Result err = m3_Call(exports[G_EXPORT_ON_INIT], 0, 0); | ||||
| 		if(err != NULL) | ||||
| 		{ | ||||
| 			log_error("runtime error: %s\n", err); | ||||
|  | @ -471,13 +477,13 @@ void* orca_runloop(void* user) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if(eventHandlers[G_EVENT_FRAME_RESIZE]) | ||||
| 	if(exports[G_EXPORT_FRAME_RESIZE]) | ||||
| 	{ | ||||
| 		mp_rect frame = mg_surface_get_frame(app->surface); | ||||
| 		u32 width = (u32)frame.w; | ||||
| 		u32 height = (u32)frame.h; | ||||
| 		const void* args[2] = {&width, &height}; | ||||
| 		m3_Call(eventHandlers[G_EVENT_FRAME_RESIZE], 2, args); | ||||
| 		m3_Call(exports[G_EXPORT_FRAME_RESIZE], 2, args); | ||||
| 	} | ||||
| 
 | ||||
| 	ui_set_context(&app->debugOverlay.ui); | ||||
|  | @ -493,6 +499,19 @@ void* orca_runloop(void* user) | |||
| 				ui_process_event(event); | ||||
| 			} | ||||
| 
 | ||||
| 			if(exports[G_EXPORT_RAW_EVENT]) | ||||
| 			{ | ||||
| 				#ifndef M3_BIG_ENDIAN | ||||
| 				mp_event* eventPtr = (mp_event*)wasm_memory_offset_to_ptr(&app->runtime.wasmMemory, app->runtime.rawEventOffset); | ||||
| 				memcpy(eventPtr, event, sizeof(*event)); | ||||
| 
 | ||||
| 				const void* args[1] = {&app->runtime.rawEventOffset}; | ||||
| 				m3_Call(exports[G_EXPORT_RAW_EVENT], 1, args); | ||||
| 				#else | ||||
| 				log_error("OnRawEvent() is not supported on big endian platforms"); | ||||
| 				#endif | ||||
| 			} | ||||
| 
 | ||||
| 			switch(event->type) | ||||
| 			{ | ||||
| 				case MP_EVENT_WINDOW_CLOSE: | ||||
|  | @ -507,12 +526,12 @@ void* orca_runloop(void* user) | |||
| 
 | ||||
| //					mg_surface_set_frame(app->debugOverlay.surface, frame);
 | ||||
| 
 | ||||
| 					if(eventHandlers[G_EVENT_FRAME_RESIZE]) | ||||
| 					if(exports[G_EXPORT_FRAME_RESIZE]) | ||||
| 					{ | ||||
| 						u32 width = (u32)event->frame.rect.w; | ||||
| 						u32 height = (u32)event->frame.rect.h; | ||||
| 						const void* args[2] = {&width, &height}; | ||||
| 						m3_Call(eventHandlers[G_EVENT_FRAME_RESIZE], 2, args); | ||||
| 						m3_Call(exports[G_EXPORT_FRAME_RESIZE], 2, args); | ||||
| 					} | ||||
| 				} break; | ||||
| 
 | ||||
|  | @ -520,30 +539,30 @@ void* orca_runloop(void* user) | |||
| 				{ | ||||
| 					if(event->key.action == MP_KEY_PRESS) | ||||
| 					{ | ||||
| 						if(eventHandlers[G_EVENT_MOUSE_DOWN]) | ||||
| 						if(exports[G_EXPORT_MOUSE_DOWN]) | ||||
| 						{ | ||||
| 							int key = event->key.code; | ||||
| 							const void* args[1] = {&key}; | ||||
| 							m3_Call(eventHandlers[G_EVENT_MOUSE_DOWN], 1, args); | ||||
| 							m3_Call(exports[G_EXPORT_MOUSE_DOWN], 1, args); | ||||
| 						} | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						if(eventHandlers[G_EVENT_MOUSE_UP]) | ||||
| 						if(exports[G_EXPORT_MOUSE_UP]) | ||||
| 						{ | ||||
| 							int key = event->key.code; | ||||
| 							const void* args[1] = {&key}; | ||||
| 							m3_Call(eventHandlers[G_EVENT_MOUSE_UP], 1, args); | ||||
| 							m3_Call(exports[G_EXPORT_MOUSE_UP], 1, args); | ||||
| 						} | ||||
| 					} | ||||
| 				} break; | ||||
| 
 | ||||
| 				case MP_EVENT_MOUSE_MOVE: | ||||
| 				{ | ||||
| 					if(eventHandlers[G_EVENT_MOUSE_MOVE]) | ||||
| 					if(exports[G_EXPORT_MOUSE_MOVE]) | ||||
| 					{ | ||||
| 						const void* args[4] = {&event->move.x, &event->move.y, &event->move.deltaX, &event->move.deltaY}; | ||||
| 						m3_Call(eventHandlers[G_EVENT_MOUSE_MOVE], 4, args); | ||||
| 						m3_Call(exports[G_EXPORT_MOUSE_MOVE], 4, args); | ||||
| 					} | ||||
| 				} break; | ||||
| 
 | ||||
|  | @ -558,18 +577,18 @@ void* orca_runloop(void* user) | |||
| 						#endif | ||||
| 						} | ||||
| 
 | ||||
| 						if(eventHandlers[G_EVENT_KEY_DOWN]) | ||||
| 						if(exports[G_EXPORT_KEY_DOWN]) | ||||
| 						{ | ||||
| 							const void* args[1] = {&event->key.code}; | ||||
| 							m3_Call(eventHandlers[G_EVENT_KEY_DOWN], 1, args); | ||||
| 							m3_Call(exports[G_EXPORT_KEY_DOWN], 1, args); | ||||
| 						} | ||||
| 					} | ||||
| 					else if(event->key.action == MP_KEY_RELEASE) | ||||
| 					{ | ||||
| 						if(eventHandlers[G_EVENT_KEY_UP]) | ||||
| 						if(exports[G_EXPORT_KEY_UP]) | ||||
| 						{ | ||||
| 							const void* args[1] = {&event->key.code}; | ||||
| 							m3_Call(eventHandlers[G_EVENT_KEY_UP], 1, args); | ||||
| 							m3_Call(exports[G_EXPORT_KEY_UP], 1, args); | ||||
| 						} | ||||
| 					} | ||||
| 				} break; | ||||
|  | @ -716,10 +735,10 @@ void* orca_runloop(void* user) | |||
| 			mg_render(app->debugOverlay.surface, app->debugOverlay.canvas); | ||||
| 		} | ||||
| 
 | ||||
| 		if(eventHandlers[G_EVENT_FRAME_REFRESH]) | ||||
| 		if(exports[G_EXPORT_FRAME_REFRESH]) | ||||
| 		{ | ||||
| 			mg_surface_prepare(app->surface); | ||||
| 			m3_Call(eventHandlers[G_EVENT_FRAME_REFRESH], 0, 0); | ||||
| 			m3_Call(exports[G_EXPORT_FRAME_REFRESH], 0, 0); | ||||
| 		} | ||||
| 
 | ||||
| 		if(app->debugOverlay.show) | ||||
|  |  | |||
|  | @ -57,3 +57,10 @@ extern u32 orca_mem_grow(u64 size) | |||
| 
 | ||||
| 	return(addr); | ||||
| } | ||||
| 
 | ||||
| void* wasm_memory_offset_to_ptr(wasm_memory* memory, u32 offset) | ||||
| { | ||||
| 	M3MemoryHeader* header = (M3MemoryHeader*)(memory->ptr); | ||||
| 	DEBUG_ASSERT(offset < header->length, "Wasm offset exceeds memory length") | ||||
| 	return memory->ptr + sizeof(M3MemoryHeader) + offset; | ||||
| } | ||||
|  | @ -14,40 +14,41 @@ | |||
| #include"m3_env.h" | ||||
| #include"m3_compile.h" | ||||
| 
 | ||||
| #define G_EVENTS(X) \ | ||||
| 	X(G_EVENT_START, "OnInit", "", "") \ | ||||
| 	X(G_EVENT_MOUSE_DOWN, "OnMouseDown", "", "i") \ | ||||
| 	X(G_EVENT_MOUSE_UP, "OnMouseUp", "", "i") \ | ||||
| 	X(G_EVENT_MOUSE_ENTER, "OnMouseEnter", "", "") \ | ||||
| 	X(G_EVENT_MOUSE_LEAVE, "OnMouseLeave", "", "") \ | ||||
| 	X(G_EVENT_MOUSE_MOVE, "OnMouseMove", "", "ffff") \ | ||||
| 	X(G_EVENT_MOUSE_WHEEL, "OnMouseWheel", "", "ff") \ | ||||
| 	X(G_EVENT_KEY_DOWN, "OnKeyDown", "", "i") \ | ||||
| 	X(G_EVENT_KEY_UP, "OnKeyUp", "", "i") \ | ||||
| 	X(G_EVENT_FRAME_REFRESH, "OnFrameRefresh", "", "") \ | ||||
| 	X(G_EVENT_FRAME_RESIZE, "OnFrameResize", "", "ii") | ||||
| #define G_EXPORTS(X) \ | ||||
| 	X(G_EXPORT_ON_INIT, "OnInit", "", "") \ | ||||
| 	X(G_EXPORT_MOUSE_DOWN, "OnMouseDown", "", "i") \ | ||||
| 	X(G_EXPORT_MOUSE_UP, "OnMouseUp", "", "i") \ | ||||
| 	X(G_EXPORT_MOUSE_ENTER, "OnMouseEnter", "", "") \ | ||||
| 	X(G_EXPORT_MOUSE_LEAVE, "OnMouseLeave", "", "") \ | ||||
| 	X(G_EXPORT_MOUSE_MOVE, "OnMouseMove", "", "ffff") \ | ||||
| 	X(G_EXPORT_MOUSE_WHEEL, "OnMouseWheel", "", "ff") \ | ||||
| 	X(G_EXPORT_KEY_DOWN, "OnKeyDown", "", "i") \ | ||||
| 	X(G_EXPORT_KEY_UP, "OnKeyUp", "", "i") \ | ||||
| 	X(G_EXPORT_FRAME_REFRESH, "OnFrameRefresh", "", "") \ | ||||
| 	X(G_EXPORT_FRAME_RESIZE, "OnFrameResize", "", "ii") \ | ||||
| 	X(G_EXPORT_RAW_EVENT, "OnRawEvent", "", "i") \ | ||||
| 
 | ||||
| typedef enum { | ||||
| 	#define G_EVENT_KIND(kind, ...) kind, | ||||
| 	G_EVENTS(G_EVENT_KIND) | ||||
| 	G_EVENT_COUNT | ||||
| } guest_event_kind; | ||||
| 	#define G_EXPORT_KIND(kind, ...) kind, | ||||
| 	G_EXPORTS(G_EXPORT_KIND) | ||||
| 	G_EXPORT_COUNT | ||||
| } guest_export_kind; | ||||
| 
 | ||||
| 
 | ||||
| typedef struct g_event_handler_desc | ||||
| typedef struct g_export_desc | ||||
| { | ||||
| 	str8 name; | ||||
| 	str8 retTags; | ||||
| 	str8 argTags; | ||||
| } g_event_handler_desc; | ||||
| } g_export_desc; | ||||
| 
 | ||||
| const g_event_handler_desc G_EVENT_HANDLER_DESC[] = { | ||||
| const g_export_desc G_EXPORT_DESC[] = { | ||||
| 	#define STR8LIT(s) {sizeof(s)-1, s} //NOTE: msvc doesn't accept STR8(s) as compile-time constant...
 | ||||
| 	#define G_EVENT_HANDLER_DESC_ENTRY(kind, name, rets, args) {STR8LIT(name), STR8LIT(rets), STR8LIT(args)}, | ||||
| 	#define G_EXPORT_DESC_ENTRY(kind, name, rets, args) {STR8LIT(name), STR8LIT(rets), STR8LIT(args)}, | ||||
| 
 | ||||
| 	G_EVENTS(G_EVENT_HANDLER_DESC_ENTRY) | ||||
| 	G_EXPORTS(G_EXPORT_DESC_ENTRY) | ||||
| 
 | ||||
| 	#undef G_EVENT_HANDLER_DESC_ENTRY | ||||
| 	#undef G_EXPORT_DESC_ENTRY | ||||
| 	#undef STR8LIT | ||||
| }; | ||||
| 
 | ||||
|  | @ -68,7 +69,8 @@ typedef struct orca_runtime | |||
| 	IM3Environment m3Env; | ||||
| 	IM3Runtime m3Runtime; | ||||
| 	IM3Module m3Module; | ||||
| 	IM3Function eventHandlers[G_EVENT_COUNT]; | ||||
| 	IM3Function exports[G_EXPORT_COUNT]; | ||||
| 	u32 rawEventOffset; | ||||
| 
 | ||||
| } orca_runtime; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue