Compare commits
9 Commits
1080adbde0
...
1d9fb74368
Author | SHA1 | Date | |
---|---|---|---|
1d9fb74368 | |||
7a5e51d948 | |||
40feab49a8 | |||
71c73f70f1 | |||
671a5e9903 | |||
72cf28cd14 | |||
c5d6dd996f | |||
99f3e039dc | |||
ff2b85184e |
23
cmake/imgui4lua.cmake
Normal file
23
cmake/imgui4lua.cmake
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
function(_imgui4lua_main)
|
||||||
|
set(dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||||
|
set(py "${PROJECT_SOURCE_DIR}/cmake/imgui4lua.py")
|
||||||
|
set(src "${imgui_SOURCE_DIR}/imgui.h")
|
||||||
|
set(dst "${dir}/imgui4lua.inc")
|
||||||
|
|
||||||
|
find_program(SH sh REQUIRED)
|
||||||
|
find_program(PYTHON3 python3 REQUIRED)
|
||||||
|
find_program(CLANGXX clang++ REQUIRED)
|
||||||
|
|
||||||
|
make_directory("${dir}")
|
||||||
|
add_custom_command(
|
||||||
|
COMMAND ${SH} -c "${PYTHON3} '${py}' '${src}' > '${dst}'"
|
||||||
|
OUTPUT "${dst}"
|
||||||
|
DEPENDS "${src}" "${py}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(imgui4lua INTERFACE)
|
||||||
|
target_sources(imgui4lua PUBLIC "${dst}")
|
||||||
|
target_include_directories(imgui4lua INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
endfunction()
|
||||||
|
_imgui4lua_main()
|
208
cmake/imgui4lua.py
Executable file
208
cmake/imgui4lua.py
Executable file
@ -0,0 +1,208 @@
|
|||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# ---- GENERATOR DEFINITIONS
|
||||||
|
def gen_enum(item):
|
||||||
|
name = item.get("name", "")
|
||||||
|
if name not in kEnumWhitelist:
|
||||||
|
return
|
||||||
|
|
||||||
|
short_name = name[5:-1]
|
||||||
|
|
||||||
|
print(f"{{ // {name}")
|
||||||
|
for child in item.get("inner", []):
|
||||||
|
member_name = child["name"]
|
||||||
|
if not member_name.startswith(name): continue
|
||||||
|
member_short_name = member_name[len(name):]
|
||||||
|
print(f" lua_pushinteger(L, static_cast<lua_Integer>({member_name})); "+
|
||||||
|
f"lua_setfield(L, -2, \"{short_name}_{member_short_name}\");")
|
||||||
|
print(f"}} // {name}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
def gen_func(name, ovlds):
|
||||||
|
ovld_root = (0, {}, None)
|
||||||
|
for ovld in ovlds:
|
||||||
|
ovld_node = ovld_root
|
||||||
|
idx = 0
|
||||||
|
for arg in ovld.get("inner", []):
|
||||||
|
if "ParmVarDecl" != arg.get("kind"): continue
|
||||||
|
tname = get_type_from_argument(arg)
|
||||||
|
if tname not in kLuaTypeChecker:
|
||||||
|
break
|
||||||
|
ovld_node[1][tname] = (idx, {}, None)
|
||||||
|
ovld_node = ovld_node[1][tname]
|
||||||
|
idx += 1
|
||||||
|
ovld_node[1]["$"] = (idx, {}, ovld)
|
||||||
|
|
||||||
|
def _output(ovld_node, depth=1):
|
||||||
|
indent = " "*depth
|
||||||
|
if 0 == len(ovld_node[1]):
|
||||||
|
gen_single_func(ovld_node[2], indent=indent)
|
||||||
|
elif 1 == len(ovld_node[1]):
|
||||||
|
_output(*ovld_node[1].values(), depth=depth)
|
||||||
|
else:
|
||||||
|
for tname in ovld_node[1]:
|
||||||
|
checker = kLuaTypeChecker[tname].replace("#", str(ovld_node[0]+1))
|
||||||
|
print(f"{indent}if ({checker}) {{")
|
||||||
|
_output(ovld_node[1][tname], depth=depth+1)
|
||||||
|
print(f"{indent}}}")
|
||||||
|
print(f"{indent}return luaL_error(L, \"unexpected type in param#{ovld_node[0]}\");")
|
||||||
|
print("lua_pushcfunction(L, [](auto L) {")
|
||||||
|
print(" (void) L;")
|
||||||
|
_output(ovld_root)
|
||||||
|
print("});")
|
||||||
|
print(f"lua_setfield(L, -2, \"{name}\");")
|
||||||
|
print()
|
||||||
|
|
||||||
|
def gen_single_func(item, indent=""):
|
||||||
|
name = item["name"]
|
||||||
|
pops = []
|
||||||
|
params = []
|
||||||
|
pcount = 0
|
||||||
|
for arg in item.get("inner", []):
|
||||||
|
if "ParmVarDecl" != arg.get("kind"): continue
|
||||||
|
pop, param, push = gen_argument(pcount, arg)
|
||||||
|
pcount += 1
|
||||||
|
pops .extend(pop)
|
||||||
|
params.extend(param)
|
||||||
|
push .extend(push)
|
||||||
|
|
||||||
|
pushes = gen_return(item)
|
||||||
|
use_ret = 0 < len(pushes)
|
||||||
|
|
||||||
|
# text output
|
||||||
|
nl = "\n"+indent
|
||||||
|
cm = ", "
|
||||||
|
if 0 < len(pops):
|
||||||
|
print(f"{indent}{nl.join(pops)}")
|
||||||
|
if use_ret:
|
||||||
|
print(f"{indent}const auto r = ImGui::{name}({cm.join(params)});")
|
||||||
|
else:
|
||||||
|
print(f"{indent}ImGui::{name}({cm.join(params)});")
|
||||||
|
if 0 < len(pushes):
|
||||||
|
print(f"{indent}{nl.join(pushes)}")
|
||||||
|
print(f"{indent}return {len(pushes)};")
|
||||||
|
|
||||||
|
def gen_return(item):
|
||||||
|
ftype = item["type"]["qualType"]
|
||||||
|
type = ftype[0:ftype.find("(")-1]
|
||||||
|
|
||||||
|
if "void" == type:
|
||||||
|
return []
|
||||||
|
if "bool" == type:
|
||||||
|
return ["lua_pushboolean(L, r);"]
|
||||||
|
if "float" == type:
|
||||||
|
return ["lua_pushnumber(L, static_cast<lua_Number>(r));"]
|
||||||
|
if "ImVec2" == type:
|
||||||
|
return [
|
||||||
|
"lua_pushnumber(L, static_cast<lua_Number>(r.x));",
|
||||||
|
"lua_pushnumber(L, static_cast<lua_Number>(r.y));",
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"unknown return type: {type}", file=sys.stderr)
|
||||||
|
return []
|
||||||
|
|
||||||
|
def gen_argument(pc, item):
|
||||||
|
type = item["type"].get("desugaredQualType", item["type"]["qualType"])
|
||||||
|
n = pc+1
|
||||||
|
pn = f"p{pc}"
|
||||||
|
|
||||||
|
if type in ["int", "unsigned int"]:
|
||||||
|
return ([f"const int {pn} = static_cast<{type}>(luaL_checkinteger(L, {n}));"], [pn], [])
|
||||||
|
|
||||||
|
if "bool" == type:
|
||||||
|
return ([f"const bool {pn} = lua_toboolean(L, {n});"], [pn], [])
|
||||||
|
|
||||||
|
if "const char *" == type:
|
||||||
|
return ([f"const char* {pn} = luaL_checkstring(L, {n});"], [pn], [])
|
||||||
|
|
||||||
|
if "const ImVec2 &" == type:
|
||||||
|
return ([
|
||||||
|
f"const float {pn}_1 = static_cast<float>(luaL_checknumber(L, {n}));",
|
||||||
|
f"const float {pn}_2 = static_cast<float>(luaL_checknumber(L, {n}));",
|
||||||
|
], [f"ImVec2 {{{pn}_1, {pn}_2}}"], [])
|
||||||
|
|
||||||
|
if "bool *" == type:
|
||||||
|
return ([f"bool {pn};"], [f"&{pn}"], [f"lua_pushboolean(L, {pn});"])
|
||||||
|
|
||||||
|
print(f"unknown argument type: {type}", file=sys.stderr)
|
||||||
|
return ([], [], [])
|
||||||
|
|
||||||
|
|
||||||
|
# ---- GENERATOR UTILITIES
|
||||||
|
def get_type_from_argument(item):
|
||||||
|
return item["type"].get("desugaredQualType", item["type"]["qualType"])
|
||||||
|
|
||||||
|
|
||||||
|
# ---- WALKER DEFINITIONS
|
||||||
|
class Walker:
|
||||||
|
def __init__(self):
|
||||||
|
self._funcs = {}
|
||||||
|
self._enums = {}
|
||||||
|
|
||||||
|
def emit(self):
|
||||||
|
for x in self._enums: gen_enum(self._enums[x])
|
||||||
|
for x in self._funcs: gen_func(x, self._funcs[x])
|
||||||
|
|
||||||
|
def walk(self, item):
|
||||||
|
kind = item.get("kind")
|
||||||
|
name = item.get("name")
|
||||||
|
if "EnumDecl" == kind:
|
||||||
|
if name in kEnumWhitelist:
|
||||||
|
self._enums[name] = item
|
||||||
|
else:
|
||||||
|
w = self.walk
|
||||||
|
if "NamespaceDecl" == kind:
|
||||||
|
w = self._walk_ns
|
||||||
|
for child in item.get("inner", []):
|
||||||
|
w(child)
|
||||||
|
|
||||||
|
def _walk_ns(self, item):
|
||||||
|
kind = item.get("kind")
|
||||||
|
name = item.get("name")
|
||||||
|
if "FunctionDecl" == kind:
|
||||||
|
if name in kFuncWhitelist:
|
||||||
|
if name not in self._funcs:
|
||||||
|
self._funcs[name] = [item]
|
||||||
|
else:
|
||||||
|
self._funcs[name].append(item)
|
||||||
|
else:
|
||||||
|
self.walk(item)
|
||||||
|
|
||||||
|
|
||||||
|
# ---- DATA DEFINITIONS
|
||||||
|
kFuncWhitelist = [
|
||||||
|
"Begin",
|
||||||
|
"End",
|
||||||
|
"BeginChild",
|
||||||
|
"EndChild",
|
||||||
|
"IsWindowAppearing",
|
||||||
|
"IsWindowCollapsed",
|
||||||
|
"IsWindowFocused",
|
||||||
|
"IsWindowHovered",
|
||||||
|
"GetWindowPos",
|
||||||
|
"GetWindowSize",
|
||||||
|
"GetWindowWidth",
|
||||||
|
"GetWindowHeight",
|
||||||
|
"Text",
|
||||||
|
]
|
||||||
|
kEnumWhitelist = [
|
||||||
|
"ImGuiWindowFlags_",
|
||||||
|
]
|
||||||
|
kLuaTypeChecker = {
|
||||||
|
"int": "LUA_TNUMBER == lua_type(L, #)",
|
||||||
|
"const char *": "LUA_TSTRING == lua_type(L, #)",
|
||||||
|
"$": "lua_isnone(L, #)",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ---- ENTRYPOINT
|
||||||
|
proc = subprocess.run(["clang++", "-x", "c++", "-std=c++2b", "-Xclang", "-ast-dump=json", "-fsyntax-only", sys.argv[1]], capture_output=True)
|
||||||
|
if 0 == proc.returncode:
|
||||||
|
walker = Walker()
|
||||||
|
walker.walk(json.loads(proc.stdout))
|
||||||
|
walker.emit()
|
||||||
|
else:
|
||||||
|
print(proc.stderr.decode("utf-8"))
|
||||||
|
exit(1)
|
@ -3,6 +3,7 @@ target_link_libraries(nf7_core
|
|||||||
PUBLIC
|
PUBLIC
|
||||||
git_hash
|
git_hash
|
||||||
imgui
|
imgui
|
||||||
|
imgui4lua
|
||||||
luajit
|
luajit
|
||||||
nf7_config
|
nf7_config
|
||||||
nf7_iface
|
nf7_iface
|
||||||
|
@ -12,7 +12,15 @@ namespace nf7::core::imgui {
|
|||||||
|
|
||||||
std::shared_ptr<luajit::Value> LuaJITDriver::MakeExtensionObject(
|
std::shared_ptr<luajit::Value> LuaJITDriver::MakeExtensionObject(
|
||||||
luajit::TaskContext& lua) {
|
luajit::TaskContext& lua) {
|
||||||
lua_pushnil(*lua);
|
auto L = *lua;
|
||||||
|
|
||||||
|
lua_newuserdata(L, 0);
|
||||||
|
if (luaL_newmetatable(L, "nf7::core::imgui::LuaJITDriver::Extension")) {
|
||||||
|
lua_createtable(L, 0, 0);
|
||||||
|
# include "generated/imgui4lua.inc"
|
||||||
|
lua_setfield(L, -2, "__index");
|
||||||
|
}
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
return lua.Register();
|
return lua.Register();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,15 +38,17 @@ TEST_F(ImGuiLuaJITDriver, CompileAndInstall) {
|
|||||||
|
|
||||||
auto fu = nf7::core::imgui::LuaJITDriver::CompileAndInstall(
|
auto fu = nf7::core::imgui::LuaJITDriver::CompileAndInstall(
|
||||||
*subenv,
|
*subenv,
|
||||||
toVector("local ctx = ...\nctx:trace(\"hello world\")"),
|
toVector(
|
||||||
|
"local ctx = ...\nctx:trace(\"hello world\")\n"
|
||||||
|
"local imgui = ctx:recv():lua()\n"
|
||||||
|
"imgui.Begin(\"helloworld\", true, imgui.WindowFlags_NoResize)\n"
|
||||||
|
"imgui.End()"),
|
||||||
"test chunk");
|
"test chunk");
|
||||||
|
|
||||||
concurrency->Push(
|
concurrency->Push(
|
||||||
nf7::SyncTask {
|
nf7::SyncTask {
|
||||||
clock->now() + std::chrono::seconds {2},
|
clock->now() + std::chrono::seconds {10},
|
||||||
[&](auto&) { DropEnv(); },
|
[&](auto&) { DropEnv(); },
|
||||||
});
|
});
|
||||||
ConsumeTasks();
|
ConsumeTasks();
|
||||||
|
|
||||||
// TODO FIXME fix leak
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// No copyright
|
// No copyright
|
||||||
#include "core/luajit/context.hh"
|
#include "core/luajit/context.hh"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -39,6 +40,17 @@ void TaskContext::Push(const nf7::Value& v) noexcept {
|
|||||||
return 1;
|
return 1;
|
||||||
});
|
});
|
||||||
lua_setfield(state_, -2, "type");
|
lua_setfield(state_, -2, "type");
|
||||||
|
|
||||||
|
lua_pushcfunction(state_, [](auto L) {
|
||||||
|
const nf7::Value& v = CheckUserData<nf7::Value>(L, 1, "nf7::Value");
|
||||||
|
try {
|
||||||
|
v.data<luajit::Value>()->Push(L);
|
||||||
|
} catch (...) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
lua_setfield(state_, -2, "lua");
|
||||||
}
|
}
|
||||||
lua_setfield(state_, -2, "__index");
|
lua_setfield(state_, -2, "__index");
|
||||||
|
|
||||||
@ -84,13 +96,16 @@ class SyncContext final :
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Push(Task&& task) noexcept override {
|
void Push(Task&& task) noexcept override {
|
||||||
|
++refcnt_;
|
||||||
|
|
||||||
auto self = std::dynamic_pointer_cast<SyncContext>(shared_from_this());
|
auto self = std::dynamic_pointer_cast<SyncContext>(shared_from_this());
|
||||||
concurrency_->Push({
|
concurrency_->Push({
|
||||||
task.after(),
|
task.after(),
|
||||||
[self, task = std::move(task)](auto&) mutable {
|
[this, self, task = std::move(task)](auto&) mutable {
|
||||||
TaskContext ctx {self, self->state()};
|
TaskContext ctx {self, self->state()};
|
||||||
lua_settop(*ctx, 0);
|
lua_settop(*ctx, 0);
|
||||||
task(ctx);
|
task(ctx);
|
||||||
|
if (0 == --refcnt_) { lua_gc(*ctx, LUA_GCCOLLECT, 0); }
|
||||||
},
|
},
|
||||||
task.location()
|
task.location()
|
||||||
});
|
});
|
||||||
@ -100,7 +115,8 @@ class SyncContext final :
|
|||||||
using Context::shared_from_this;
|
using Context::shared_from_this;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<subsys::Concurrency> concurrency_;
|
const std::shared_ptr<subsys::Concurrency> concurrency_;
|
||||||
|
uint64_t refcnt_ {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncContext final :
|
class AsyncContext final :
|
||||||
@ -114,6 +130,8 @@ class AsyncContext final :
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Push(Task&& task) noexcept override {
|
void Push(Task&& task) noexcept override {
|
||||||
|
++refcnt_;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> k {mtx_};
|
std::unique_lock<std::mutex> k {mtx_};
|
||||||
const auto first = tasks_.empty();
|
const auto first = tasks_.empty();
|
||||||
tasks_.push_back(std::move(task));
|
tasks_.push_back(std::move(task));
|
||||||
@ -142,7 +160,9 @@ class AsyncContext final :
|
|||||||
TaskContext ctx {self, state()};
|
TaskContext ctx {self, state()};
|
||||||
for (auto& task : tasks) {
|
for (auto& task : tasks) {
|
||||||
task(ctx);
|
task(ctx);
|
||||||
|
--refcnt_;
|
||||||
}
|
}
|
||||||
|
if (0 == refcnt_) { lua_gc(*ctx, LUA_GCCOLLECT, 0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -150,6 +170,8 @@ class AsyncContext final :
|
|||||||
|
|
||||||
std::mutex mtx_;
|
std::mutex mtx_;
|
||||||
std::vector<Task> tasks_;
|
std::vector<Task> tasks_;
|
||||||
|
|
||||||
|
std::atomic<uint64_t> refcnt_ {0};
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <lua.hpp>
|
||||||
|
|
||||||
#include "iface/common/future.hh"
|
#include "iface/common/future.hh"
|
||||||
#include "iface/common/leak_detector.hh"
|
#include "iface/common/leak_detector.hh"
|
||||||
#include "iface/common/value.hh"
|
#include "iface/common/value.hh"
|
||||||
@ -45,13 +47,17 @@ class Value final : public nf7::Value::Data, public LeakDetector<Value> {
|
|||||||
Value& operator=(const Value&) = delete;
|
Value& operator=(const Value&) = delete;
|
||||||
Value& operator=(Value&&) = delete;
|
Value& operator=(Value&&) = delete;
|
||||||
|
|
||||||
|
void Push(lua_State* L) const noexcept {
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, index_);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::shared_ptr<Context>& context() const noexcept { return ctx_; }
|
const std::shared_ptr<Context>& context() const noexcept { return ctx_; }
|
||||||
int index() const noexcept { return index_; }
|
int index() const noexcept { return index_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Context> ctx_;
|
const std::shared_ptr<Context> ctx_;
|
||||||
int index_;
|
const int index_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nf7::core::luajit
|
} // namespace nf7::core::luajit
|
||||||
|
1
thirdparty/CMakeLists.txt
vendored
1
thirdparty/CMakeLists.txt
vendored
@ -19,6 +19,7 @@ FetchContent_Declare(
|
|||||||
)
|
)
|
||||||
FetchContent_Populate(imgui)
|
FetchContent_Populate(imgui)
|
||||||
include(imgui.cmake)
|
include(imgui.cmake)
|
||||||
|
include(../cmake/imgui4lua.cmake)
|
||||||
|
|
||||||
# ---- luajit (MIT)
|
# ---- luajit (MIT)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user