diff --git a/meson.build b/meson.build index 64a309d..eeaa393 100644 --- a/meson.build +++ b/meson.build @@ -7,8 +7,14 @@ project('tiwiq', 'cpp_std=c++17', ]) +py3 = import('python') +py3_inst = py3.find_installation('python3') +py3_dep = py3_inst.dependency(embed: true) + twq_lib = static_library('tiwiq', sources: [ + 'src/Global.cpp', + 'src/core/Binding.cpp', 'src/core/Command.cpp', 'src/core/Context.cpp', @@ -33,9 +39,13 @@ twq_lib = static_library('tiwiq', 'src/term/Controller.cpp', 'src/term/View.cpp', 'src/term/Term.cpp', + + 'src/script/Scripts.cpp', + 'src/script/Loader.cpp', ], dependencies: [ - dependency('ncursesw') + py3_dep, + dependency('ncursesw'), ]) twq_dep = declare_dependency( @@ -48,7 +58,8 @@ executable('twq', 'src/main.cpp' ], dependencies: [ - twq_dep + twq_dep, + py3_dep, ], install: true) @@ -60,5 +71,6 @@ executable('twq-tests', ], dependencies: [ twq_dep, + py3_dep, dependency('catch2') ]) diff --git a/src/Global.cpp b/src/Global.cpp new file mode 100644 index 0000000..14f02b2 --- /dev/null +++ b/src/Global.cpp @@ -0,0 +1,7 @@ +#include "Global.hpp" + +namespace twq +{ + std::shared_ptr Global::tiwiq = nullptr; + std::shared_ptr Global::term = nullptr; +} diff --git a/src/Global.hpp b/src/Global.hpp new file mode 100644 index 0000000..0d5bdee --- /dev/null +++ b/src/Global.hpp @@ -0,0 +1,18 @@ +#ifndef twq_GLOBAL_HPP +#define twq_GLOBAL_HPP + +#include "commons.hpp" +#include "core/Tiwiq.hpp" +#include "term/Term.hpp" + +namespace twq +{ + class Global + { + public: + static std::shared_ptr tiwiq; + static std::shared_ptr term; + }; +} + +#endif diff --git a/src/commons.hpp b/src/commons.hpp index d13b796..5893c10 100644 --- a/src/commons.hpp +++ b/src/commons.hpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #define GEN_ENUM(X) X #define GEN_STRING(X) #X diff --git a/src/core/Color.hpp b/src/core/Color.hpp index cff0438..192b07a 100644 --- a/src/core/Color.hpp +++ b/src/core/Color.hpp @@ -26,11 +26,10 @@ namespace twq bool equals(Color const& rhs) const; - private: + protected: int m_red; int m_green; int m_blue; - }; } } diff --git a/src/core/Plugin.hpp b/src/core/Plugin.hpp index 6640f27..6b9e7fc 100644 --- a/src/core/Plugin.hpp +++ b/src/core/Plugin.hpp @@ -20,7 +20,7 @@ namespace twq std::weak_ptr ui() { return m_ui; } std::weak_ptr executor(); - private: + protected: std::string m_name; std::shared_ptr m_executor; std::shared_ptr m_ui = diff --git a/src/core/Tiwiq.cpp b/src/core/Tiwiq.cpp index 69b5807..38a09fb 100644 --- a/src/core/Tiwiq.cpp +++ b/src/core/Tiwiq.cpp @@ -1,6 +1,7 @@ #include "Tiwiq.hpp" #include "Event.hpp" #include "Executor.hpp" +#include "src/commons.hpp" namespace twq { @@ -29,14 +30,25 @@ namespace twq m_plugins.push_back(plugin); } + void Tiwiq::rem_plugin(std::shared_ptr plugin) + { + auto itr = std::remove_if(std::begin(m_plugins), + std::end(m_plugins), + [&plugin](auto const& p){ + return p->name() == plugin->name(); + }); + + m_plugins.erase(itr, std::end(m_plugins)); + } + void Tiwiq::update(Event& event) /*override*/ { if (event.type == EVENT_KEY) { - if (!m_plugins.empty()) + for (auto plugin: m_plugins) { Context ctx { *this }; - m_plugins.back()->executor().lock()->update(ctx, *event.key); + plugin->executor().lock()->update(ctx, *event.key); } } } diff --git a/src/core/Tiwiq.hpp b/src/core/Tiwiq.hpp index 35464ed..b8fa542 100644 --- a/src/core/Tiwiq.hpp +++ b/src/core/Tiwiq.hpp @@ -21,6 +21,8 @@ namespace twq void stop(); void add_plugin(std::shared_ptr plugin); + void rem_plugin(std::shared_ptr plugin); + void update(Event& event) override; diff --git a/src/main.cpp b/src/main.cpp index f2f006c..9dc0d4d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,27 +1,48 @@ #include +#include "Global.hpp" #include "core/Tiwiq.hpp" #include "term/Term.hpp" #include "plugins/CorePlugin.hpp" +#include "script/Decl.hpp" +#include "script/Scripts.hpp" +#include "script/Loader.hpp" int main(int, char**) { - auto tiwiq = std::make_shared(); - auto term = std::make_shared(); + pybind11::scoped_interpreter m_scope; + auto tiwiq = std::make_shared(); + twq::Global::tiwiq = tiwiq; + auto term = std::make_shared(); + twq::Global::term = term; term->controller().lock()->add_observer(tiwiq); // Build and add plugins // --------------------- auto core_plugin = std::make_shared(); - core_plugin->ui().lock()->add_observer(term->view().lock()); - core_plugin->ui().lock()->resize(term->view().lock()->dim()); + term->add_plugin(core_plugin); tiwiq->add_plugin(core_plugin); + // Load scripts + // ------------ + twq::script::Loader loader; + + try + { + loader.load(); + } + catch(std::exception const& err) + { + endwin(); + std::cerr << err.what() << std::endl; + return -1; + } + // Start TiwiQ // ----------- + tiwiq->start(); - bool a = true; while (tiwiq->is_running()) { diff --git a/src/script/Decl.hpp b/src/script/Decl.hpp new file mode 100644 index 0000000..6e92427 --- /dev/null +++ b/src/script/Decl.hpp @@ -0,0 +1,30 @@ +#ifndef twq_script_DECL_HPP +#define twq_script_DECL_HPP + +#include +#include +#include "Scripts.hpp" + +namespace twq +{ + namespace script + { + PYBIND11_EMBEDDED_MODULE(tiwiq, m) + { + py::class_>(m, "Plugin") + .def(py::init()) + .def("bind", &ScriptPlugin::bind) + .def("write_char", &ScriptPlugin::write_char); + + py::class_(m, "Color") + .def(py::init<>()) + .def(py::init()); + + m.def("mount", &mount); + m.def("umount", &umount); + } + } +} + +#endif diff --git a/src/script/Loader.cpp b/src/script/Loader.cpp new file mode 100644 index 0000000..6922af7 --- /dev/null +++ b/src/script/Loader.cpp @@ -0,0 +1,61 @@ +#include + +#include "Loader.hpp" + +namespace twq +{ + namespace script + { + /*explicit*/ Loader::Loader() + { + } + + /*virtual*/ Loader::~Loader() + { + } + + void Loader::load() + { + auto dir = get_twq_dir(); + + if (!std::filesystem::exists(dir)) + { + std::filesystem::create_directory(dir); + } + + auto autodir = dir / "autoloads"; + + if (std::filesystem::exists(autodir)) + { + for (auto entry: std::filesystem::directory_iterator(autodir)) + { + if (entry.path().has_extension() + && entry.path().extension() == ".py") + { + load_py(entry.path()); + } + } + } + + auto init_file = dir / "init.py"; + + if (std::filesystem::exists(init_file)) + { + load_py(init_file); + } + } + + std::filesystem::path Loader::get_twq_dir() + { + auto home = std::filesystem::path(getenv("HOME")); + TWQ_ASSERT(std::filesystem::exists(home), "cannot find home directory"); + + return home / ".tiwiq.d"; + } + + void Loader::load_py(std::filesystem::path script_path) + { + pybind11::eval_file(script_path.string()); + } + } +} diff --git a/src/script/Loader.hpp b/src/script/Loader.hpp new file mode 100644 index 0000000..057939c --- /dev/null +++ b/src/script/Loader.hpp @@ -0,0 +1,29 @@ +#ifndef twq_script_LOADER_HPP +#define twq_script_LOADER_HPP + +#include +#include + +#include "../commons.hpp" + +namespace twq +{ + namespace script + { + class Loader + { + public: + explicit Loader(); + virtual ~Loader(); + + void load(); + + private: + std::filesystem::path get_twq_dir(); + void load_py(std::filesystem::path script_path); + + }; + } +} + +#endif diff --git a/src/script/Scripts.cpp b/src/script/Scripts.cpp new file mode 100644 index 0000000..c392cd7 --- /dev/null +++ b/src/script/Scripts.cpp @@ -0,0 +1,19 @@ +#include "Scripts.hpp" + +namespace twq +{ + namespace script + { + void mount(std::shared_ptr plugin) + { + Global::tiwiq->add_plugin(plugin); + Global::term->add_plugin(plugin); + } + + void umount(std::shared_ptr plugin) + { + Global::tiwiq->rem_plugin(plugin); + Global::term->rem_plugin(plugin); + } + } +} diff --git a/src/script/Scripts.hpp b/src/script/Scripts.hpp new file mode 100644 index 0000000..4307245 --- /dev/null +++ b/src/script/Scripts.hpp @@ -0,0 +1,89 @@ +#ifndef twq_script_SCRIPTS_HPP +#define twq_script_SCRIPTS_HPP + +#include +#include +#include +#include + +#include "../Global.hpp" +#include "../commons.hpp" + +#include "../core/Executor.hpp" +#include "../core/Plugin.hpp" +#include "../core/Command.hpp" +#include "../core/Shortcut.hpp" +#include "../core/Binding.hpp" + +#include "../core/Context.hpp" + +namespace py = pybind11; + +namespace twq +{ + namespace script + { + using command_t = std::function; + + struct ScriptColor: public core::Color { + ScriptColor() + : core::Color() + { + } + + ScriptColor(int r, int g, int b) + : core::Color(r, g, b) + { + } + }; + + struct ScriptCommand: public core::Command { + command_t cmd; + + ScriptCommand(command_t mycmd) + : cmd { mycmd } + { + } + + void execute(core::Context&) override + { + cmd(); + } + + void undo(core::Context&) override + { + } + }; + + struct ScriptPlugin: public core::Plugin { + ScriptPlugin(std::string const& name) + : core::Plugin(name) + { + } + + void bind(std::string const& shortcut_str, command_t cmd) + { + auto command = std::make_shared(cmd); + std::wstring wstr(std::begin(shortcut_str), + std::end(shortcut_str)); + auto shortcut = std::make_shared(wstr); + core::Binding binding {shortcut, command}; + m_executor->register_binding(binding); + } + + void write_char(int row, int col, + ScriptColor bg, + ScriptColor fg, + wchar_t text) + { + m_ui->write(core::Pixel {bg, fg, text}, core::Coord {row, col}); + } + }; + + // Mount and umount plugins + void mount(std::shared_ptr plugin); + void umount(std::shared_ptr plugin); + } +} + +#endif diff --git a/src/term/Term.cpp b/src/term/Term.cpp index 4a5c489..6af129d 100644 --- a/src/term/Term.cpp +++ b/src/term/Term.cpp @@ -23,5 +23,15 @@ namespace twq { endwin(); } + + void Term::add_plugin(std::shared_ptr plugin) + { + plugin->ui().lock()->add_observer(m_view); + plugin->ui().lock()->resize(m_view->dim()); + } + + void Term::rem_plugin(std::shared_ptr) + { + } } } diff --git a/src/term/Term.hpp b/src/term/Term.hpp index 44496dd..0d99331 100644 --- a/src/term/Term.hpp +++ b/src/term/Term.hpp @@ -3,7 +3,7 @@ #include #include "../commons.hpp" - +#include "../core/Plugin.hpp" #include "Controller.hpp" #include "View.hpp" @@ -20,6 +20,9 @@ namespace twq std::weak_ptr controller() { return m_controller; } std::weak_ptr view() { return m_view; } + void add_plugin(std::shared_ptr plugin); + void rem_plugin(std::shared_ptr plugin); + private: std::shared_ptr m_controller = std::make_shared(); diff --git a/src/term/View.cpp b/src/term/View.cpp index c9d6a1d..bb7e835 100644 --- a/src/term/View.cpp +++ b/src/term/View.cpp @@ -45,7 +45,7 @@ namespace twq }); attron(COLOR_PAIR(pair)); - mvprintw(j, i, "%lc", event.draw->pixel.text); + mvprintw(i, j, "%lc", event.draw->pixel.text); attroff(COLOR_PAIR(pair)); refresh();