#include "noesis_shim.h"
#include <NsCore/Noesis.h>
#include <NsCore/Ptr.h>
#include <NsCore/Delegate.h>
#include <NsCore/DynamicCast.h>
#include <NsGui/FrameworkElement.h>
#include <NsGui/InputEnums.h>
#include <NsGui/IntegrationAPI.h>
#include <NsGui/IRenderer.h>
#include <NsGui/IView.h>
#include <NsGui/MemoryStream.h>
#include <NsGui/ResourceDictionary.h>
#include <NsGui/UICollection.h>
#include <NsGui/Stream.h>
#include <NsGui/Uri.h>
#include <NsGui/XamlProvider.h>
#include <NsMath/Matrix.h>
#include <NsRender/RenderDevice.h>
#include <cstdint>
#include <cstring>
namespace {
class RustXamlProvider final : public Noesis::XamlProvider {
public:
RustXamlProvider(const noesis_xaml_provider_vtable* vtable, void* userdata)
: mVtable(*vtable), mUserdata(userdata)
{}
Noesis::Ptr<Noesis::Stream> LoadXaml(const Noesis::Uri& uri) override {
const char* uriChars = uri.Str();
const uint8_t* data = nullptr;
uint32_t len = 0;
bool ok = mVtable.load_xaml(mUserdata, uriChars ? uriChars : "", &data, &len);
if (!ok || data == nullptr) {
return nullptr;
}
return Noesis::MakePtr<Noesis::MemoryStream>(data, len);
}
private:
noesis_xaml_provider_vtable mVtable;
void* mUserdata;
};
}
extern "C" void* noesis_xaml_provider_create(
const noesis_xaml_provider_vtable* vtable, void* userdata)
{
if (!vtable) return nullptr;
Noesis::Ptr<RustXamlProvider> p =
Noesis::MakePtr<RustXamlProvider>(vtable, userdata);
return p.GiveOwnership();
}
extern "C" void noesis_xaml_provider_destroy(void* provider) {
if (!provider) return;
static_cast<Noesis::XamlProvider*>(provider)->Release();
}
extern "C" void noesis_set_xaml_provider(void* provider) {
Noesis::GUI::SetXamlProvider(static_cast<Noesis::XamlProvider*>(provider));
}
extern "C" void* noesis_gui_load_xaml(const char* uri) {
if (!uri) return nullptr;
Noesis::Ptr<Noesis::BaseComponent> component =
Noesis::GUI::LoadXaml(Noesis::Uri(uri));
if (!component) return nullptr;
Noesis::Ptr<Noesis::FrameworkElement> element =
Noesis::DynamicPtrCast<Noesis::FrameworkElement>(component);
if (!element) return nullptr;
return element.GiveOwnership();
}
extern "C" void* noesis_gui_parse_xaml(const char* text) {
if (!text) return nullptr;
Noesis::Ptr<Noesis::BaseComponent> component = Noesis::GUI::ParseXaml(text);
if (!component) return nullptr;
Noesis::Ptr<Noesis::FrameworkElement> element =
Noesis::DynamicPtrCast<Noesis::FrameworkElement>(component);
if (!element) return nullptr;
return element.GiveOwnership();
}
extern "C" bool noesis_gui_load_component(void* component, const char* uri) {
if (!component || !uri) return false;
Noesis::GUI::LoadComponent(
static_cast<Noesis::BaseComponent*>(component), Noesis::Uri(uri));
return true;
}
extern "C" void noesis_base_component_release(void* obj) {
if (!obj) return;
static_cast<Noesis::BaseComponent*>(obj)->Release();
}
extern "C" void* noesis_base_component_add_reference(void* obj) {
if (!obj) return nullptr;
static_cast<Noesis::BaseComponent*>(obj)->AddReference();
return obj;
}
extern "C" int32_t noesis_base_component_get_num_references(void* obj) {
if (!obj) return 0;
return static_cast<Noesis::BaseComponent*>(obj)->GetNumReferences();
}
extern "C" bool noesis_gui_load_application_resources(const char* uri) {
if (!uri) return false;
Noesis::Ptr<Noesis::ResourceDictionary> dict =
Noesis::GUI::LoadXaml<Noesis::ResourceDictionary>(Noesis::Uri(uri));
if (!dict) return false;
Noesis::GUI::SetApplicationResources(dict);
return true;
}
extern "C" bool noesis_gui_install_app_resources_chain(
const char* const* uris, uint32_t count)
{
if (!uris || count == 0) return false;
Noesis::Ptr<Noesis::ResourceDictionary> parent = *new Noesis::ResourceDictionary();
Noesis::GUI::SetApplicationResources(parent);
for (uint32_t i = 0; i < count; ++i) {
const char* leafUri = uris[i];
if (!leafUri) continue;
Noesis::Ptr<Noesis::ResourceDictionary> child = *new Noesis::ResourceDictionary();
parent->GetMergedDictionaries()->Add(child);
child->SetSource(Noesis::Uri(leafUri));
}
return true;
}
extern "C" void* noesis_view_create(void* framework_element) {
if (!framework_element) return nullptr;
Noesis::Ptr<Noesis::IView> view = Noesis::GUI::CreateView(
static_cast<Noesis::FrameworkElement*>(framework_element));
if (!view) return nullptr;
return view.GiveOwnership();
}
extern "C" void noesis_view_destroy(void* view) {
if (!view) return;
static_cast<Noesis::IView*>(view)->Release();
}
extern "C" void* noesis_view_add_reference(void* view) {
if (!view) return nullptr;
static_cast<Noesis::IView*>(view)->AddReference();
return view;
}
extern "C" void noesis_view_set_size(void* view, uint32_t width, uint32_t height) {
static_cast<Noesis::IView*>(view)->SetSize(width, height);
}
extern "C" void noesis_view_set_scale(void* view, float scale) {
static_cast<Noesis::IView*>(view)->SetScale(scale);
}
extern "C" void noesis_view_set_projection_matrix(void* view, const float* matrix) {
Noesis::Matrix4 m(matrix);
static_cast<Noesis::IView*>(view)->SetProjectionMatrix(m);
}
extern "C" bool noesis_view_update(void* view, double time_seconds) {
return static_cast<Noesis::IView*>(view)->Update(time_seconds);
}
extern "C" void noesis_view_set_flags(void* view, uint32_t flags) {
static_cast<Noesis::IView*>(view)->SetFlags(flags);
}
extern "C" void* noesis_view_get_renderer(void* view) {
return static_cast<Noesis::IView*>(view)->GetRenderer();
}
extern "C" void* noesis_view_get_content(void* view) {
if (!view) return nullptr;
Noesis::FrameworkElement* content = static_cast<Noesis::IView*>(view)->GetContent();
if (!content) return nullptr;
content->AddReference();
return content;
}
extern "C" void noesis_renderer_init(void* renderer, void* render_device) {
static_cast<Noesis::IRenderer*>(renderer)->Init(
static_cast<Noesis::RenderDevice*>(render_device));
}
extern "C" void noesis_renderer_shutdown(void* renderer) {
static_cast<Noesis::IRenderer*>(renderer)->Shutdown();
}
extern "C" bool noesis_renderer_update_render_tree(void* renderer) {
return static_cast<Noesis::IRenderer*>(renderer)->UpdateRenderTree();
}
extern "C" bool noesis_renderer_render_offscreen(void* renderer) {
return static_cast<Noesis::IRenderer*>(renderer)->RenderOffscreen();
}
extern "C" void noesis_renderer_render(void* renderer, bool flip_y, bool clear) {
static_cast<Noesis::IRenderer*>(renderer)->Render(flip_y, clear);
}
extern "C" void noesis_renderer_render_stereo(
void* renderer, const float* eye_matrix, bool flip_y, bool clear)
{
if (!renderer || !eye_matrix) return;
Noesis::Matrix4 eye(eye_matrix);
static_cast<Noesis::IRenderer*>(renderer)->RenderStereo(eye, flip_y, clear);
}
extern "C" void noesis_renderer_render_stereo_both(
void* renderer, const float* left_eye_matrix, const float* right_eye_matrix,
bool flip_y, bool clear)
{
if (!renderer || !left_eye_matrix || !right_eye_matrix) return;
Noesis::Matrix4 left(left_eye_matrix);
Noesis::Matrix4 right(right_eye_matrix);
static_cast<Noesis::IRenderer*>(renderer)->RenderStereo(left, right, flip_y, clear);
}
static_assert((int32_t)Noesis::MouseButton_Left == 0, "MouseButton::Left");
static_assert((int32_t)Noesis::MouseButton_Right == 1, "MouseButton::Right");
static_assert((int32_t)Noesis::MouseButton_Middle == 2, "MouseButton::Middle");
static_assert((int32_t)Noesis::MouseButton_XButton1 == 3, "MouseButton::XButton1");
static_assert((int32_t)Noesis::MouseButton_XButton2 == 4, "MouseButton::XButton2");
static_assert((int32_t)Noesis::Key_None == 0, "Key::None");
static_assert((int32_t)Noesis::Key_Back == 2, "Key::Back");
static_assert((int32_t)Noesis::Key_Tab == 3, "Key::Tab");
static_assert((int32_t)Noesis::Key_Return == 6, "Key::Return");
static_assert((int32_t)Noesis::Key_Pause == 7, "Key::Pause");
static_assert((int32_t)Noesis::Key_CapsLock == 8, "Key::CapsLock");
static_assert((int32_t)Noesis::Key_Escape == 13, "Key::Escape");
static_assert((int32_t)Noesis::Key_Space == 18, "Key::Space");
static_assert((int32_t)Noesis::Key_PageUp == 19, "Key::PageUp");
static_assert((int32_t)Noesis::Key_PageDown == 20, "Key::PageDown");
static_assert((int32_t)Noesis::Key_End == 21, "Key::End");
static_assert((int32_t)Noesis::Key_Home == 22, "Key::Home");
static_assert((int32_t)Noesis::Key_Left == 23, "Key::Left");
static_assert((int32_t)Noesis::Key_Up == 24, "Key::Up");
static_assert((int32_t)Noesis::Key_Right == 25, "Key::Right");
static_assert((int32_t)Noesis::Key_Down == 26, "Key::Down");
static_assert((int32_t)Noesis::Key_PrintScreen == 30, "Key::PrintScreen");
static_assert((int32_t)Noesis::Key_Insert == 31, "Key::Insert");
static_assert((int32_t)Noesis::Key_Delete == 32, "Key::Delete");
static_assert((int32_t)Noesis::Key_Help == 33, "Key::Help");
static_assert((int32_t)Noesis::Key_D0 == 34, "Key::D0");
static_assert((int32_t)Noesis::Key_D9 == 43, "Key::D9");
static_assert((int32_t)Noesis::Key_A == 44, "Key::A");
static_assert((int32_t)Noesis::Key_Z == 69, "Key::Z");
static_assert((int32_t)Noesis::Key_LWin == 70, "Key::LWin");
static_assert((int32_t)Noesis::Key_RWin == 71, "Key::RWin");
static_assert((int32_t)Noesis::Key_Apps == 72, "Key::Apps");
static_assert((int32_t)Noesis::Key_NumPad0 == 74, "Key::NumPad0");
static_assert((int32_t)Noesis::Key_NumPad9 == 83, "Key::NumPad9");
static_assert((int32_t)Noesis::Key_Multiply == 84, "Key::Multiply");
static_assert((int32_t)Noesis::Key_Add == 85, "Key::Add");
static_assert((int32_t)Noesis::Key_Subtract == 87, "Key::Subtract");
static_assert((int32_t)Noesis::Key_Decimal == 88, "Key::Decimal");
static_assert((int32_t)Noesis::Key_Divide == 89, "Key::Divide");
static_assert((int32_t)Noesis::Key_F1 == 90, "Key::F1");
static_assert((int32_t)Noesis::Key_F24 == 113, "Key::F24");
static_assert((int32_t)Noesis::Key_NumLock == 114, "Key::NumLock");
static_assert((int32_t)Noesis::Key_Scroll == 115, "Key::ScrollLock");
static_assert((int32_t)Noesis::Key_LeftShift == 116, "Key::LeftShift");
static_assert((int32_t)Noesis::Key_RightShift == 117, "Key::RightShift");
static_assert((int32_t)Noesis::Key_LeftCtrl == 118, "Key::LeftCtrl");
static_assert((int32_t)Noesis::Key_RightCtrl == 119, "Key::RightCtrl");
static_assert((int32_t)Noesis::Key_LeftAlt == 120, "Key::LeftAlt");
static_assert((int32_t)Noesis::Key_RightAlt == 121, "Key::RightAlt");
static_assert((int32_t)Noesis::Key_OemSemicolon == 140, "Key::OemSemicolon");
static_assert((int32_t)Noesis::Key_OemPlus == 141, "Key::OemPlus");
static_assert((int32_t)Noesis::Key_OemComma == 142, "Key::OemComma");
static_assert((int32_t)Noesis::Key_OemMinus == 143, "Key::OemMinus");
static_assert((int32_t)Noesis::Key_OemPeriod == 144, "Key::OemPeriod");
static_assert((int32_t)Noesis::Key_OemQuestion == 145, "Key::OemSlash");
static_assert((int32_t)Noesis::Key_OemTilde == 146, "Key::OemTilde");
static_assert((int32_t)Noesis::Key_OemOpenBrackets == 149, "Key::OemOpenBrackets");
static_assert((int32_t)Noesis::Key_OemPipe == 150, "Key::OemPipe");
static_assert((int32_t)Noesis::Key_OemCloseBrackets == 151, "Key::OemCloseBrackets");
static_assert((int32_t)Noesis::Key_OemQuotes == 152, "Key::OemQuotes");
static_assert((int32_t)Noesis::Key_GamepadLeft == 175, "Key::GamepadLeft");
static_assert((int32_t)Noesis::Key_GamepadUp == 176, "Key::GamepadUp");
static_assert((int32_t)Noesis::Key_GamepadRight == 177, "Key::GamepadRight");
static_assert((int32_t)Noesis::Key_GamepadDown == 178, "Key::GamepadDown");
static_assert((int32_t)Noesis::Key_GamepadAccept == 179, "Key::GamepadAccept");
static_assert((int32_t)Noesis::Key_GamepadCancel == 180, "Key::GamepadCancel");
static_assert((int32_t)Noesis::Key_GamepadMenu == 181, "Key::GamepadMenu");
static_assert((int32_t)Noesis::Key_GamepadView == 182, "Key::GamepadView");
static_assert((int32_t)Noesis::Key_GamepadPageUp == 183, "Key::GamepadPageUp");
static_assert((int32_t)Noesis::Key_GamepadPageDown == 184, "Key::GamepadPageDown");
static_assert((int32_t)Noesis::Key_GamepadPageLeft == 185, "Key::GamepadPageLeft");
static_assert((int32_t)Noesis::Key_GamepadPageRight == 186, "Key::GamepadPageRight");
static_assert((int32_t)Noesis::Key_GamepadContext1 == 187, "Key::GamepadContext1");
static_assert((int32_t)Noesis::Key_GamepadContext2 == 188, "Key::GamepadContext2");
static_assert((int32_t)Noesis::Key_GamepadContext3 == 189, "Key::GamepadContext3");
static_assert((int32_t)Noesis::Key_GamepadContext4 == 190, "Key::GamepadContext4");
extern "C" bool noesis_view_mouse_move(void* view, int32_t x, int32_t y) {
return static_cast<Noesis::IView*>(view)->MouseMove(x, y);
}
extern "C" bool noesis_view_mouse_button_down(void* view, int32_t x, int32_t y, int32_t button) {
return static_cast<Noesis::IView*>(view)
->MouseButtonDown(x, y, static_cast<Noesis::MouseButton>(button));
}
extern "C" bool noesis_view_mouse_button_up(void* view, int32_t x, int32_t y, int32_t button) {
return static_cast<Noesis::IView*>(view)
->MouseButtonUp(x, y, static_cast<Noesis::MouseButton>(button));
}
extern "C" bool noesis_view_mouse_double_click(void* view, int32_t x, int32_t y, int32_t button) {
return static_cast<Noesis::IView*>(view)
->MouseDoubleClick(x, y, static_cast<Noesis::MouseButton>(button));
}
extern "C" bool noesis_view_mouse_wheel(void* view, int32_t x, int32_t y, int32_t delta) {
return static_cast<Noesis::IView*>(view)->MouseWheel(x, y, delta);
}
extern "C" bool noesis_view_scroll(void* view, int32_t x, int32_t y, float value) {
return static_cast<Noesis::IView*>(view)->Scroll(x, y, value);
}
extern "C" bool noesis_view_hscroll(void* view, int32_t x, int32_t y, float value) {
return static_cast<Noesis::IView*>(view)->HScroll(x, y, value);
}
extern "C" bool noesis_view_touch_down(void* view, int32_t x, int32_t y, uint64_t id) {
return static_cast<Noesis::IView*>(view)->TouchDown(x, y, id);
}
extern "C" bool noesis_view_touch_move(void* view, int32_t x, int32_t y, uint64_t id) {
return static_cast<Noesis::IView*>(view)->TouchMove(x, y, id);
}
extern "C" bool noesis_view_touch_up(void* view, int32_t x, int32_t y, uint64_t id) {
return static_cast<Noesis::IView*>(view)->TouchUp(x, y, id);
}
extern "C" bool noesis_view_key_down(void* view, int32_t key) {
return static_cast<Noesis::IView*>(view)->KeyDown(static_cast<Noesis::Key>(key));
}
extern "C" bool noesis_view_key_up(void* view, int32_t key) {
return static_cast<Noesis::IView*>(view)->KeyUp(static_cast<Noesis::Key>(key));
}
extern "C" bool noesis_view_char(void* view, uint32_t codepoint) {
return static_cast<Noesis::IView*>(view)->Char(codepoint);
}
extern "C" void noesis_view_activate(void* view) {
static_cast<Noesis::IView*>(view)->Activate();
}
extern "C" void noesis_view_deactivate(void* view) {
static_cast<Noesis::IView*>(view)->Deactivate();
}
extern "C" bool noesis_view_mouse_hwheel(void* view, int32_t x, int32_t y, int32_t delta) {
return static_cast<Noesis::IView*>(view)->MouseHWheel(x, y, delta);
}
extern "C" uint32_t noesis_view_get_flags(void* view) {
if (!view) return 0;
return static_cast<Noesis::IView*>(view)->GetFlags();
}
extern "C" void noesis_view_set_tessellation_max_pixel_error(void* view, float error) {
if (!view) return;
static_cast<Noesis::IView*>(view)->SetTessellationMaxPixelError(
Noesis::TessellationMaxPixelError(error));
}
extern "C" float noesis_view_get_tessellation_max_pixel_error(void* view) {
if (!view) return 0.0f;
return static_cast<Noesis::IView*>(view)->GetTessellationMaxPixelError().error;
}
extern "C" void noesis_view_set_holding_time_threshold(void* view, uint32_t ms) {
if (!view) return;
static_cast<Noesis::IView*>(view)->SetHoldingTimeThreshold(ms);
}
extern "C" void noesis_view_set_holding_distance_threshold(void* view, uint32_t pixels) {
if (!view) return;
static_cast<Noesis::IView*>(view)->SetHoldingDistanceThreshold(pixels);
}
extern "C" void noesis_view_set_manipulation_distance_threshold(void* view, uint32_t pixels) {
if (!view) return;
static_cast<Noesis::IView*>(view)->SetManipulationDistanceThreshold(pixels);
}
extern "C" void noesis_view_set_double_tap_time_threshold(void* view, uint32_t ms) {
if (!view) return;
static_cast<Noesis::IView*>(view)->SetDoubleTapTimeThreshold(ms);
}
extern "C" void noesis_view_set_double_tap_distance_threshold(void* view, uint32_t pixels) {
if (!view) return;
static_cast<Noesis::IView*>(view)->SetDoubleTapDistanceThreshold(pixels);
}
extern "C" void noesis_view_set_emulate_touch(void* view, bool emulate) {
if (!view) return;
static_cast<Noesis::IView*>(view)->SetEmulateTouch(emulate);
}
extern "C" void noesis_view_set_stereo_offscreen_scale_factor(void* view, float factor) {
if (!view) return;
static_cast<Noesis::IView*>(view)->SetStereoOffscreenScaleFactor(factor);
}
static_assert(sizeof(noesis_view_stats) == sizeof(Noesis::ViewStats),
"noesis_view_stats must match Noesis::ViewStats layout");
extern "C" void noesis_view_get_stats(void* view, noesis_view_stats* out) {
if (!view || !out) return;
const Noesis::ViewStats s = static_cast<Noesis::IView*>(view)->GetStats();
out->frame_time = s.frameTime;
out->update_time = s.updateTime;
out->render_time = s.renderTime;
out->triangles = s.triangles;
out->draws = s.draws;
out->batches = s.batches;
out->tessellations = s.tessellations;
out->flushes = s.flushes;
out->geometry_size = s.geometrySize;
out->masks = s.masks;
out->opacities = s.opacities;
out->render_target_switches = s.renderTargetSwitches;
out->uploaded_ramps = s.uploadedRamps;
out->rasterized_glyphs = s.rasterizedGlyphs;
out->discarded_glyph_tiles = s.discardedGlyphTiles;
}
namespace {
class RustTimer {
public:
RustTimer(Noesis::IView* view, noesis_timer_fn cb, void* userdata,
noesis_timer_free_fn free_handler)
: mView(view), mCb(cb), mUserdata(userdata), mFree(free_handler), mId(0)
{
if (mView) {
mView->AddReference();
}
}
~RustTimer() {
void* ud = mUserdata;
mUserdata = nullptr;
if (mFree && ud) {
mFree(ud);
}
if (mView) {
mView->Release();
}
}
RustTimer(const RustTimer&) = delete;
RustTimer& operator=(const RustTimer&) = delete;
uint32_t Tick() {
if (mCb) {
return mCb(mUserdata);
}
return 0;
}
void setId(uint32_t id) { mId = id; }
uint32_t id() const { return mId; }
Noesis::IView* view() const { return mView; }
private:
Noesis::IView* mView; noesis_timer_fn mCb;
void* mUserdata;
noesis_timer_free_fn mFree;
uint32_t mId;
};
}
extern "C" void* noesis_view_create_timer(
void* view, uint32_t interval_ms, noesis_timer_fn cb, void* userdata,
noesis_timer_free_fn free_handler)
{
if (!view || !cb) return nullptr;
auto* v = static_cast<Noesis::IView*>(view);
auto* timer = new RustTimer(v, cb, userdata, free_handler);
const uint32_t id = v->CreateTimer(interval_ms, Noesis::MakeDelegate(timer, &RustTimer::Tick));
timer->setId(id);
return timer;
}
extern "C" void noesis_view_restart_timer(void* token, uint32_t interval_ms) {
if (!token) return;
auto* timer = static_cast<RustTimer*>(token);
if (auto* v = timer->view()) {
v->RestartTimer(timer->id(), interval_ms);
}
}
extern "C" void noesis_view_cancel_timer(void* token) {
if (!token) return;
auto* timer = static_cast<RustTimer*>(token);
if (auto* v = timer->view()) {
v->CancelTimer(timer->id());
}
delete timer; }
namespace {
class RustRenderingHandler {
public:
RustRenderingHandler(Noesis::IView* view, noesis_rendering_fn cb,
void* userdata, noesis_rendering_free_fn free_handler)
: mView(view), mCb(cb), mUserdata(userdata), mFree(free_handler)
{
mView->AddReference();
mView->Rendering() += Noesis::MakeDelegate(this, &RustRenderingHandler::OnRendering);
}
~RustRenderingHandler() {
mView->Rendering() -= Noesis::MakeDelegate(this, &RustRenderingHandler::OnRendering);
void* ud = mUserdata;
mUserdata = nullptr;
if (mFree && ud) {
mFree(ud);
}
mView->Release();
}
RustRenderingHandler(const RustRenderingHandler&) = delete;
RustRenderingHandler& operator=(const RustRenderingHandler&) = delete;
private:
void OnRendering(Noesis::IView* view) {
if (mCb) {
mCb(mUserdata, view);
}
}
Noesis::IView* mView; noesis_rendering_fn mCb;
void* mUserdata;
noesis_rendering_free_fn mFree;
};
}
extern "C" void* noesis_view_add_rendering_handler(
void* view, noesis_rendering_fn cb, void* userdata,
noesis_rendering_free_fn free_handler)
{
if (!view || !cb) return nullptr;
auto* v = static_cast<Noesis::IView*>(view);
return new RustRenderingHandler(v, cb, userdata, free_handler);
}
extern "C" void noesis_view_remove_rendering_handler(void* token) {
if (!token) return;
delete static_cast<RustRenderingHandler*>(token);
}