const str = decoder.decode(data);
document.getElementById("main_canvas").style.cursor = str;
+ },
+
+ local_storage_store: function(keyptr, keylen, valueptr, valuelen) {
+ const decoder = new TextDecoder();
+
+ const key = decoder.decode(new Uint8Array(window.ONYX_MEMORY.buffer, keyptr, keylen));
+ const value = decoder.decode(new Uint8Array(window.ONYX_MEMORY.buffer, valueptr, valuelen));
+
+ localStorage[key] = value;
+ },
+
+ local_storage_value_length: function(keyptr, keylen) {
+ const decoder = new TextDecoder();
+ const encoder = new TextEncoder();
+
+ const key = decoder.decode(new Uint8Array(window.ONYX_MEMORY.buffer, keyptr, keylen));
+
+ return encoder.encode(localStorage[key]).length;
+ },
+
+ local_storage_load: function(keyptr, keylen, bufferptr, bufferlen) {
+ const decoder = new TextDecoder();
+ const encoder = new TextEncoder();
+
+ const key = decoder.decode(new Uint8Array(window.ONYX_MEMORY.buffer, keyptr, keylen));
+
+ const value_data = encoder.encode(localStorage[key]);
+
+ let WASM_U8 = new Uint8Array(window.ONYX_MEMORY.buffer);
+ WASM_U8.set(value_data, bufferptr);
}
});
package app
-use package core
-
#private_file {
events :: package js_events
gl :: package gl
config :: package config
wasm :: package wasm_utils
debug :: package debug
-}
-use package debug { init as debug_init, debug_log, draw_debug_log }
-use package core.intrinsics.onyx { __initialize }
+ use package core
+
+ use package debug { init as debug_init, debug_log, draw_debug_log }
+ use package core.intrinsics.onyx { __initialize }
+}
@Relocate search_buffer: string.String_Buffer;
on_file_load_callbacks : Map(u32, (file_event: ^events.Event) -> void);
state : Application_State;
Application_State :: struct {
- colorscheme: Colorscheme;
+ settings := Application_Settings.{};
has_active_file := false;
file := Active_File.{};
__initialize(^state);
window_buffer := memory.make_slice(Application_Window, MAXIMUM_WINDOWS);
state.windows_store = alloc.pool.make(window_buffer);
- map.init(^state.windows_map, hash_count=16);
+ map.init(^state.windows_map);
array.init(^state.windows_sorted);
state.window_switcher_state->init();
array.init(^registered_tools);
map.init(^on_file_load_callbacks);
- colorscheme_switch(.Dark);
-
load_background_tile_texture();
load_fonts();
+ settings_load(^state.settings);
+ colorscheme_switch(state.settings.colorscheme);
+
search_buffer = string.buffer_make(memory.make_slice(u8, 256));
gl.enable(gl.BLEND);
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
- registered_tools << Tool.{
+ registered_tools << .{
id = "load_file_info",
name = "File Info",
open = () {
if event.keyboard->get_name() == "F7" {
debug.debug_log_toggle();
+ settings_save(^state.settings);
break;
}
state.has_active_file = true;
state.file.name = name;
state.file.data = data;
+
+ invoke_feature_function("file_loaded");
}
}
}
ui.workspace_start(main_area, state=^state.workspace_state);
draw_background_lines(~~window_width, ~~window_height, line_color=config.Colors.background);
- // Used for detecting when a window is completely off-screen and shouldn't be rendered.
- transformed_window_rect := window_rectangle;
- {
- trans := gfx.global_renderer->get_transform();
- transformed_window_rect.x0 -= trans.translation.x / trans.scale.x;
- transformed_window_rect.y0 -= trans.translation.y / trans.scale.y;
- transformed_window_rect.x1 = transformed_window_rect.x0 + (~~window_width / trans.scale.x);
- transformed_window_rect.y1 = transformed_window_rect.y0 + (~~window_height / trans.scale.y);
- }
-
i := 0;
new_top: ^Application_Window = null;
for window: state.windows_sorted {
defer i += 1;
- if !(window.window_state->get_rectangle() |> ui.Rectangle.intersects(transformed_window_rect)) do continue;
+ if !(window.window_state->get_transformed_rectangle() |> ui.Rectangle.intersects(window_rectangle)) do continue;
if ui.window_start(^window.window_state, increment=hash.to_u32(window.id)) {
new_top = window;
}
colorscheme_switch :: (new_scheme: Colorscheme) {
- if state.colorscheme == new_scheme do return;
-
- state.colorscheme = new_scheme;
+ state.settings.colorscheme = new_scheme;
colors_scheme_file: str;
- switch state.colorscheme {
+ switch state.settings.colorscheme {
case .Dark do colors_scheme_file = config.dark_color_scheme_file;
case .Light do colors_scheme_file = config.light_color_scheme_file;
case #default do assert(false, "Bad colorscheme setting.");
};
}
- debug_log(.Info, "Successfully loaded colorscheme '{}'.", state.colorscheme);
+ debug_log(.Info, "Successfully loaded colorscheme '{}'.", state.settings.colorscheme);
}
update_ui_colors :: () {
if ui.button(clear_button_rect, "Clear log") {
debug_log_clear();
}
-
- app :: package app
- colorscheme := app.state.colorscheme;
-
- clear_button_rect.x0 += 220;
- clear_button_rect.x1 += 220;
- switched := ui.radio(clear_button_rect, ^colorscheme, .Dark, "Dark mode");
- clear_button_rect.x0 += 200;
- clear_button_rect.x1 += 200;
- switched = switched || ui.radio(clear_button_rect, ^colorscheme, .Light, "Light mode");
-
- if switched {
- app.colorscheme_switch(colorscheme);
- }
}
#private_file log_buffer : struct {
--- /dev/null
+package app
+
+#private_file {
+ json :: package json
+ __initialize :: (package core.intrinsics.onyx).__initialize
+ init :: (package core.intrinsics.onyx).init
+
+ use package core
+
+ Settings_Key :: "debugger_settings"
+}
+
+Application_Settings :: struct {
+ colorscheme := Colorscheme.Undefined;
+}
+
+settings_save :: (settings: ^Application_Settings) {
+ use type_info;
+
+ root := init(json.Value_Object);
+ array.init(^root.object_, allocator=context.allocator);
+ defer json.free(^root, context.allocator);
+
+ json.set(^root, "colorscheme", enum_name(settings.colorscheme), true, true);
+
+ store_value, _ := json.encode_string(^root);
+ defer string.free(store_value);
+
+ storage.store(Settings_Key, store_value);
+}
+
+settings_load :: (settings: ^Application_Settings) {
+ use type_info;
+
+ value := storage.load(Settings_Key);
+ if value.data == null do value = "{}";
+ defer if value != "{}" do string.free(value);
+
+ root := json.decode(value);
+ defer json.free(root);
+
+ colorscheme := enum_value(Colorscheme, root.root["colorscheme"]->as_str());
+ if colorscheme != .Undefined do settings.colorscheme = colorscheme;
+ else do settings.colorscheme = .Dark;
+}
--- /dev/null
+// Simple local-storage wrapper
+
+package app.storage
+
+#private_file {
+ memory :: package core.memory
+}
+
+store :: (key: str, value: [] u8) {
+ __local_storage_store(key, value);
+}
+
+load :: (key: str, allocator := context.allocator) -> str {
+ buffer_length := __local_storage_value_length(key);
+
+ buffer := memory.make_slice(u8, buffer_length, allocator=allocator);
+ __local_storage_load(key, buffer);
+
+ return buffer;
+}
+
+#private_file {
+ __local_storage_store :: (key: str, value: str) -> void #foreign "decompiler" "local_storage_store" ---
+ __local_storage_value_length :: (key: str) -> i32 #foreign "decompiler" "local_storage_value_length" ---
+ __local_storage_load :: (key: str, buffer: str) -> void #foreign "decompiler" "local_storage_load" ---
+}
+
#load "src/app/window_switcher"
#load "src/app/window_management"
#load "src/app/colors"
+ #load "src/app/storage"
+ #load "src/app/settings"
#load "src/features/load_features"
setup :: () {
debug_log(.Debug, "Initializing hex viewer...", 0);
- app.registered_tools << app.Tool.{
+ app.registered_tools << .{
id = window_id,
name = window_name,
open = open_window,
setup :: () {
debug_log(.Debug, "Initializing text editor...", 0);
- array.push(^app.registered_tools, .{
+ app.registered_tools << .{
id = window_id,
name = window_name,
open = open_text_editor,
- });
+ };
}
Feature_Wasm_Loader :: struct {
setup := setup;
-}
\ No newline at end of file
+ file_loaded := file_loaded;
+}
package feature.wasm
#private_file {
- app :: package app
- ui :: package ui
- config :: package config
+ app :: package app
+ ui :: package ui
+ config :: package config
+
+ wasm_utils :: package wasm_utils
+ WasmBinary :: wasm_utils.WasmBinary
+ WasmSections :: wasm_utils.WasmSections
use package core
use package debug { debug_log }
-
- use package wasm_utils
}
#private {
wasm_state : WasmBinary;
wasm_sections : WasmSections;
+
+ wasm_analyzed := false;
}
setup :: () {
debug_log(.Info, "Wasm Loader Loaded from {}", #file);
+
+ app.registered_tools << .{
+ id = "wasm_info",
+ name = "WASM info",
+ open = () {
+ app.open_window("wasm_info", "WASM info", .{ 0, 0 }, info_window_draw, .{ 1300, 650 });
+ app.move_window_to_top("wasm_info");
+ app.focus_window("wasm_info");
+ }
+ };
+}
+
+if_visible :: macro (r: ui.Rectangle, w: ui.Rectangle, draw: Code) {
+ if ui.Rectangle.intersects(w, r) {
+ #insert draw;
+ }
+}
+
+info_window_draw :: (_, win) => {
+ if !wasm_analyzed do return;
+
+ rect := ui.Rectangle.{ 0, 0, win.window_state.size.x, win.window_state.size.y };
+
+ #persist scrollable_region := ui.Scrollable_Region_State.{};
+ ui.scrollable_region_start(rect, state=^scrollable_region);
+ defer ui.scrollable_region_stop();
+
+ text_theme := ui.default_text_theme;
+ text_theme.font = config.Fonts.FiraCode.index;
+
+ win_rect := rect;
+ v := 0x7F7FFFFF;
+ rect.y1 = *cast(^f32)^v;
+ rect.x1 = *cast(^f32)^v;
+ text_rect : ui.Rectangle;
+ msg_buffer: [512] u8;
+
+ {
+ text_rect, rect = ui.Flow.split_horizontal(rect, top_height=40);
+ if_visible(text_rect, win_rect,
+ #(ui.draw_text(text_rect, conv.str_format(msg_buffer, "Imports: {}", wasm_sections.import_section.count), theme=^text_theme)));
+ }
+
+ for ^import: wasm_sections.import_section {
+ text_rect, rect = ui.Flow.split_horizontal(rect, top_height=32);
+ if_visible(text_rect, win_rect,
+ #(ui.draw_text(text_rect, conv.str_format(msg_buffer, "{*}", import), theme=^text_theme)));
+ }
+
+ {
+ text_rect, rect = ui.Flow.split_horizontal(rect, top_height=40);
+ if_visible(text_rect, win_rect,
+ #(ui.draw_text(text_rect, conv.str_format(msg_buffer, "Exports: {}", wasm_sections.export_section.count), theme=^text_theme)));
+ }
+
+ {
+ text_rect, rect = ui.Flow.split_horizontal(rect, top_height=40);
+ if_visible(text_rect, win_rect,
+ #(ui.draw_text(text_rect, conv.str_format(msg_buffer, "Functions: {}", wasm_sections.code_section.count), theme=^text_theme)));
+ }
+
+ for ^code: wasm_sections.code_section {
+ text_rect, rect = ui.Flow.split_horizontal(rect, top_height=32);
+ if_visible(text_rect, win_rect,
+ #(ui.draw_text(text_rect, conv.str_format(msg_buffer, "{*}", code), theme=^text_theme)));
+ }
+}
+
+file_loaded :: () {
+ if wasm_analyzed {
+ wasm_utils.free(^wasm_state);
+ wasm_utils.free_sections(^wasm_sections);
+ }
+
+ wasm_analyzed = false;
+
+ file_data := app.state.file.data;
+ debug_log(.Info, "Wasm feature noticed file dropped with size {}", file_data.count);
+
+ if !string.starts_with(file_data, u8.[ 0, #char "a", #char "s", #char "m" ]) do return;
+
+ debug_log(.Info, "THIS IS PROBABLY A WASM BINARY. ANALYZING!!!");
+
+ wasm_state = wasm_utils.load(file_data, context.allocator);
+ wasm_sections = wasm_utils.parse_sections(^wasm_state, context.allocator);
+
+ debug_log(.Info, "There are {} functions in this binary.", wasm_sections.code_section.count);
+
+ for ^import: wasm_sections.import_section {
+ debug_log(.Info, "Import: {*}", import);
+ }
+
+ wasm_analyzed = true;
}
#private_file {
map :: package core.map
+ // gfx :: package immediate_mode
}
// This will be stored by the end user's library.
border_color := gfx.Color4.{ 1, 0, 0 };
background_color := gfx.Color4.{ 0.2, 0.2, 0.2 };
- bar_height := 34.0f;
+ bar_height := 28.0f;
draggable := true;
resizable := true;
return .{ position.x - bw, position.y - bw - bar_height,
position.x + size.x + bw, position.y + size.y + bw };
}
+
+ get_transformed_rectangle :: (use w: ^Window_State) -> Rectangle {
+ bw := border_width;
+ if dragging do bw *= 5;
+
+ r := Rectangle.{
+ position.x - bw, position.y - bw - bar_height,
+ position.x + size.x + bw, position.y + size.y + bw };
+
+
+ trans := gfx.global_renderer->get_transform();
+ r.x0 *= trans.scale.x;
+ r.y0 *= trans.scale.y;
+ r.x0 += trans.translation.x;
+ r.y0 += trans.translation.y;
+ r.x1 *= trans.scale.x;
+ r.y1 *= trans.scale.y;
+ r.x1 += trans.translation.x;
+ r.y1 += trans.translation.y;
+
+ return r;
+ }
+
+ get_origin_rectangle :: (use w: ^Window_State) -> Rectangle {
+ bw := border_width;
+ if dragging do bw *= 5;
+
+ return .{ bw, bw - bar_height,
+ size.x + bw, size.y + bw };
+ }
}
window_start :: (use state: ^Window_State, site := #callsite, increment := 0) -> bool {
draw_rect(x, y, w, h, color=state.background_color);
title_theme := default_text_theme;
- title_theme.font_size = 1.0f;
+ title_theme.font_size = bar_height / 32.0f;
title_theme.text_color = .{ 1, 1, 1 }; @ThemeConfiguration
draw_text(.{ x, y - bar_height, x + w, y }, state.title, theme=^title_theme);
- if button(.{ x + w - 48, y - bar_height - border_width, x + w + border_width, y }, "X", increment=increment + hash) {
+ close_theme := default_button_theme;
+ close_theme.border_width = 0;
+ if button(.{ x + w - 48, y - bar_height - border_width, x + w + border_width, y }, "X", theme=^close_theme, increment=increment + hash) {
state.should_close = true;
}