✨ can now quit tiwiq using C-C C-C keys.
parent
1d2394068d
commit
5188860936
19
meson.build
19
meson.build
|
@ -9,12 +9,25 @@ project('tiwiq',
|
|||
|
||||
twq_lib = static_library('tiwiq',
|
||||
sources: [
|
||||
'src/core/KeyMod.cpp',
|
||||
'src/core/Shortcut.cpp',
|
||||
'src/core/Binding.cpp',
|
||||
'src/core/Command.cpp',
|
||||
'src/core/Context.cpp',
|
||||
'src/core/Binding.cpp',
|
||||
'src/core/Executor.cpp',
|
||||
'src/core/KeyMod.cpp',
|
||||
'src/core/Plugin.cpp',
|
||||
'src/core/Shortcut.cpp',
|
||||
'src/core/Tiwiq.cpp',
|
||||
'src/core/UI.cpp',
|
||||
'src/core/Observer.cpp',
|
||||
'src/core/Observable.cpp',
|
||||
'src/core/Event.cpp',
|
||||
|
||||
'src/cmd/QuitCmd.cpp',
|
||||
|
||||
'src/plugins/CorePlugin.cpp',
|
||||
|
||||
'src/term/Controller.cpp',
|
||||
'src/term/Term.cpp',
|
||||
],
|
||||
dependencies: [
|
||||
dependency('ncursesw')
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#include "QuitCmd.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace cmd
|
||||
{
|
||||
/*explicit*/ QuitCmd::QuitCmd()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ QuitCmd::~QuitCmd()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ void QuitCmd::execute(core::Context& context) /*override*/
|
||||
{
|
||||
context.tiwiq().stop();
|
||||
}
|
||||
|
||||
/*virtual*/ void QuitCmd::undo(core::Context&) /*override*/
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef twq_cmd_QUITCMD_HPP
|
||||
#define twq_cmd_QUITCMD_HPP
|
||||
|
||||
#include "../core/Command.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace cmd
|
||||
{
|
||||
class QuitCmd: public core::Command
|
||||
{
|
||||
public:
|
||||
explicit QuitCmd();
|
||||
virtual ~QuitCmd();
|
||||
|
||||
virtual void execute(core::Context& context) override;
|
||||
virtual void undo(core::Context& context) override;
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -4,7 +4,8 @@ namespace twq
|
|||
{
|
||||
namespace core
|
||||
{
|
||||
/*explicit*/ Context::Context()
|
||||
/*explicit*/ Context::Context(Tiwiq& tiwiq)
|
||||
: m_tiwiq { tiwiq }
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef twq_core_CONTEXT_HPP
|
||||
#define twq_core_CONTEXT_HPP
|
||||
|
||||
#include "Tiwiq.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
|
@ -8,10 +10,13 @@ namespace twq
|
|||
class Context
|
||||
{
|
||||
public:
|
||||
explicit Context();
|
||||
explicit Context(Tiwiq& tiwiq);
|
||||
virtual ~Context();
|
||||
|
||||
Tiwiq& tiwiq() const { return m_tiwiq; }
|
||||
|
||||
private:
|
||||
Tiwiq& m_tiwiq;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#include "Event.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef twq_core_EVENT_HPP
|
||||
#define twq_core_EVENT_HPP
|
||||
|
||||
#include "../commons.hpp"
|
||||
#include "KeyMod.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
struct Event
|
||||
{
|
||||
std::optional<KeyMod> key;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -10,18 +10,18 @@ namespace twq
|
|||
return KeyMod { key_type, mods, std::nullopt };
|
||||
}
|
||||
|
||||
/*explicit*/ KeyMod::KeyMod(std::string const& repr)
|
||||
/*explicit*/ KeyMod::KeyMod(std::wstring const& repr)
|
||||
{
|
||||
std::vector<ModType> mods;
|
||||
|
||||
if (repr.find("C-") != std::string::npos)
|
||||
if (repr.find(L"C-") != std::string::npos)
|
||||
{
|
||||
mods.push_back(MOD_LCTRL);
|
||||
mods.push_back(KM_MOD_LCTRL);
|
||||
}
|
||||
|
||||
if (repr.find("A-") != std::string::npos)
|
||||
if (repr.find(L"A-") != std::string::npos)
|
||||
{
|
||||
mods.push_back(MOD_ALT);
|
||||
mods.push_back(KM_MOD_ALT);
|
||||
}
|
||||
|
||||
ssize_t k = repr.size() - 1;
|
||||
|
@ -30,7 +30,7 @@ namespace twq
|
|||
k--;
|
||||
}
|
||||
|
||||
std::string value;
|
||||
std::wstring value;
|
||||
|
||||
while (k >= 0
|
||||
&& !std::isspace(repr[k])
|
||||
|
@ -42,16 +42,17 @@ namespace twq
|
|||
|
||||
if (value.size() == 1)
|
||||
{
|
||||
m_key_type = KEY_TEXT;
|
||||
m_key_type = KM_KEY_TEXT;
|
||||
m_mods = mods;
|
||||
m_text = value.front();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i=0; i<KEY_COUNT; i++)
|
||||
for (size_t i=0; i<KM_KEY_COUNT; i++)
|
||||
{
|
||||
std::string val = KeyTypeStr[i] + strlen("KEY_");
|
||||
std::string v = KeyTypeStr[i] + strlen("KM_KEY_");
|
||||
std::wstring val(std::begin(v), std::end(v));
|
||||
|
||||
if (val == value)
|
||||
{
|
||||
|
@ -62,15 +63,16 @@ namespace twq
|
|||
}
|
||||
|
||||
throw invalid_keymod_error {"cannot convert '"
|
||||
+ repr
|
||||
+ std::string(std::begin(repr),
|
||||
std::end(repr))
|
||||
+ "' to keymod."};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*explicit*/ KeyMod::KeyMod(char text,
|
||||
/*explicit*/ KeyMod::KeyMod(wchar_t text,
|
||||
std::vector<ModType> const& mods)
|
||||
: m_key_type { KEY_TEXT }
|
||||
: m_key_type { KM_KEY_TEXT }
|
||||
, m_mods { mods }
|
||||
, m_text { text }
|
||||
{
|
||||
|
@ -78,7 +80,7 @@ namespace twq
|
|||
|
||||
/*explicit*/ KeyMod::KeyMod(KeyType key_type,
|
||||
std::vector<ModType> const& mods,
|
||||
std::optional<char> text)
|
||||
std::optional<wchar_t> text)
|
||||
: m_key_type { key_type }
|
||||
, m_mods { mods }
|
||||
, m_text { text }
|
||||
|
@ -103,7 +105,7 @@ namespace twq
|
|||
return false;
|
||||
}
|
||||
|
||||
if (m_key_type == KEY_TEXT
|
||||
if (m_key_type == KM_KEY_TEXT
|
||||
&& *m_text != *rhs.m_text)
|
||||
{
|
||||
return false;
|
||||
|
@ -126,27 +128,27 @@ namespace twq
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string KeyMod::string() const
|
||||
std::wstring KeyMod::wstring() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
std::wstringstream ss;
|
||||
|
||||
if (has_mod(MOD_LCTRL))
|
||||
if (has_mod(KM_MOD_LCTRL))
|
||||
{
|
||||
ss << "C-";
|
||||
}
|
||||
|
||||
if (has_mod(MOD_ALT))
|
||||
if (has_mod(KM_MOD_ALT))
|
||||
{
|
||||
ss << "A-";
|
||||
}
|
||||
|
||||
if (m_key_type == KEY_TEXT)
|
||||
if (m_key_type == KM_KEY_TEXT)
|
||||
{
|
||||
ss << *m_text;
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << (KeyTypeStr[m_key_type] + strlen("KEY_"));
|
||||
ss << (KeyTypeStr[m_key_type] + strlen("KM_KEY_"));
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
|
|
|
@ -4,17 +4,17 @@
|
|||
#include "../commons.hpp"
|
||||
|
||||
#define KEY_TYPES(G) \
|
||||
G(KEY_TEXT), \
|
||||
G(KEY_UP), \
|
||||
G(KEY_DOWN), \
|
||||
G(KEY_LEFT), \
|
||||
G(KEY_RIGHT), \
|
||||
G(KEY_COUNT),
|
||||
G(KM_KEY_TEXT), \
|
||||
G(KM_KEY_UP), \
|
||||
G(KM_KEY_DOWN), \
|
||||
G(KM_KEY_LEFT), \
|
||||
G(KM_KEY_RIGHT), \
|
||||
G(KM_KEY_COUNT),
|
||||
|
||||
#define MOD_TYPES(G)\
|
||||
G(MOD_LCTRL), \
|
||||
G(MOD_ALT), \
|
||||
G(MOD_COUNT),
|
||||
G(KM_MOD_LCTRL), \
|
||||
G(KM_MOD_ALT), \
|
||||
G(KM_MOD_COUNT),
|
||||
|
||||
namespace twq
|
||||
{
|
||||
|
@ -31,31 +31,31 @@ namespace twq
|
|||
static KeyMod key(KeyType key_type,
|
||||
std::vector<ModType> const& mods={});
|
||||
|
||||
explicit KeyMod(std::string const& repr);
|
||||
explicit KeyMod(std::wstring const& repr);
|
||||
|
||||
explicit KeyMod(char text, std::vector<ModType> const& mods={});
|
||||
explicit KeyMod(wchar_t text, std::vector<ModType> const& mods={});
|
||||
|
||||
|
||||
explicit KeyMod(KeyType key_type,
|
||||
std::vector<ModType> const& mods,
|
||||
std::optional<char> text);
|
||||
std::optional<wchar_t> text);
|
||||
|
||||
KeyType key_type() const { return m_key_type; }
|
||||
std::vector<ModType> mods() const { return m_mods; }
|
||||
std::optional<char> text() const { return m_text; }
|
||||
std::optional<wchar_t> text() const { return m_text; }
|
||||
|
||||
bool has_mod(ModType mod_type) const;
|
||||
|
||||
bool equals(KeyMod const& rhs) const;
|
||||
|
||||
std::string string() const;
|
||||
std::wstring wstring() const;
|
||||
|
||||
virtual ~KeyMod();
|
||||
|
||||
private:
|
||||
KeyType m_key_type;
|
||||
std::vector<ModType> m_mods;
|
||||
std::optional<char> m_text;
|
||||
std::optional<wchar_t> m_text;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#include "Observable.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
/*explicit*/ Observable::Observable()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Observable::~Observable()
|
||||
{
|
||||
}
|
||||
|
||||
void Observable::add_observer(std::weak_ptr<Observer> observer)
|
||||
{
|
||||
m_observers.push_back(observer);
|
||||
}
|
||||
|
||||
void Observable::notify(Event& event)
|
||||
{
|
||||
for (auto observer: m_observers)
|
||||
{
|
||||
if (auto obs = observer.lock();
|
||||
obs)
|
||||
{
|
||||
obs->update(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef twq_core_OBSERVABLE_HPP
|
||||
#define twq_core_OBSERVABLE_HPP
|
||||
|
||||
#include "../commons.hpp"
|
||||
#include "Observer.hpp"
|
||||
#include "Event.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
class Observable
|
||||
{
|
||||
public:
|
||||
explicit Observable();
|
||||
virtual ~Observable();
|
||||
|
||||
void add_observer(std::weak_ptr<Observer> observer);
|
||||
void notify(Event& event);
|
||||
|
||||
private:
|
||||
std::vector<std::weak_ptr<Observer>> m_observers;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#include "Observer.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
/*explicit*/ Observer::Observer()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Observer::~Observer()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef twq_core_OBSERVER_HPP
|
||||
#define twq_core_OBSERVER_HPP
|
||||
|
||||
#include "../commons.hpp"
|
||||
#include "Event.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
class Observer
|
||||
{
|
||||
public:
|
||||
explicit Observer();
|
||||
virtual ~Observer();
|
||||
|
||||
virtual void update(Event&) {}
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#include "Plugin.hpp"
|
||||
#include "Executor.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
/*explicit*/ Plugin::Plugin(std::string const& name)
|
||||
: m_name { name }
|
||||
, m_executor { std::make_shared<Executor>() }
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Plugin::~Plugin()
|
||||
{
|
||||
}
|
||||
|
||||
std::weak_ptr<Executor> Plugin::executor()
|
||||
{
|
||||
return m_executor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef twq_core_PLUGIN_HPP
|
||||
#define twq_core_PLUGIN_HPP
|
||||
|
||||
#include "../commons.hpp"
|
||||
#include "UI.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
class Executor;
|
||||
|
||||
class Plugin
|
||||
{
|
||||
public:
|
||||
explicit Plugin(std::string const& name);
|
||||
virtual ~Plugin();
|
||||
|
||||
std::string name() const { return m_name; }
|
||||
std::weak_ptr<UI> ui() { return m_ui; }
|
||||
|
||||
std::weak_ptr<Executor> executor();
|
||||
private:
|
||||
std::string m_name;
|
||||
std::shared_ptr<Executor> m_executor;
|
||||
|
||||
std::shared_ptr<UI> m_ui =
|
||||
std::make_shared<UI>();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -10,14 +10,14 @@ namespace twq
|
|||
{
|
||||
}
|
||||
|
||||
/*explicit*/ Shortcut::Shortcut(std::string const& repr)
|
||||
/*explicit*/ Shortcut::Shortcut(std::wstring const& repr)
|
||||
{
|
||||
std::string buffer;
|
||||
std::wstring buffer;
|
||||
std::vector<ModType> mods;
|
||||
|
||||
for (size_t i=0; i<repr.size(); i++)
|
||||
{
|
||||
char c = repr[i];
|
||||
wchar_t c = repr[i];
|
||||
|
||||
if (std::isspace(c))
|
||||
{
|
||||
|
@ -29,8 +29,10 @@ namespace twq
|
|||
}
|
||||
catch (invalid_keymod_error const& err)
|
||||
{
|
||||
throw invalid_shortcut_error {"cannot convert '"
|
||||
+ repr
|
||||
throw invalid_shortcut_error {
|
||||
"cannot convert '"
|
||||
+ std::string(std::begin(repr),
|
||||
std::end(repr))
|
||||
+ "' to shortcut"};
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +55,8 @@ namespace twq
|
|||
catch (invalid_keymod_error const& err)
|
||||
{
|
||||
throw invalid_shortcut_error {"cannot convert '"
|
||||
+ repr
|
||||
+ std::string(std::begin(repr),
|
||||
std::end(repr))
|
||||
+ "' to shortcut"};
|
||||
}
|
||||
}
|
||||
|
@ -77,15 +80,15 @@ namespace twq
|
|||
m_keymods.push_back(keymod);
|
||||
}
|
||||
|
||||
std::string Shortcut::string() const
|
||||
std::wstring Shortcut::wstring() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
std::string sep;
|
||||
std::wstringstream ss;
|
||||
std::wstring sep;
|
||||
|
||||
for (auto const& km: m_keymods)
|
||||
{
|
||||
ss << sep << km.string();
|
||||
sep = " ";
|
||||
ss << sep << km.wstring();
|
||||
sep = L" ";
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
|
|
|
@ -13,14 +13,14 @@ namespace twq
|
|||
{
|
||||
public:
|
||||
explicit Shortcut();
|
||||
explicit Shortcut(std::string const& repr);
|
||||
explicit Shortcut(std::wstring const& repr);
|
||||
virtual ~Shortcut();
|
||||
|
||||
size_t count() const { return m_keymods.size(); }
|
||||
KeyMod get(size_t index) const;
|
||||
|
||||
void push(KeyMod const& keymod);
|
||||
std::string string() const;
|
||||
std::wstring wstring() const;
|
||||
|
||||
private:
|
||||
std::vector<KeyMod> m_keymods;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#include "Tiwiq.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "Executor.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
/*explicit*/ Tiwiq::Tiwiq()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Tiwiq::~Tiwiq()
|
||||
{
|
||||
}
|
||||
|
||||
void Tiwiq::start()
|
||||
{
|
||||
m_is_running = true;
|
||||
}
|
||||
|
||||
void Tiwiq::stop()
|
||||
{
|
||||
m_is_running = false;
|
||||
}
|
||||
|
||||
void Tiwiq::add_plugin(std::shared_ptr<Plugin> plugin)
|
||||
{
|
||||
m_plugins.push_back(plugin);
|
||||
}
|
||||
|
||||
void Tiwiq::update(Event& event) /*override*/
|
||||
{
|
||||
if (event.key)
|
||||
{
|
||||
if (!m_plugins.empty())
|
||||
{
|
||||
Context ctx { *this };
|
||||
m_plugins.back()->executor().lock()->update(ctx, *event.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef twq_core_TIWIQ_HPP
|
||||
#define twq_core_TIWIQ_HPP
|
||||
|
||||
#include "../commons.hpp"
|
||||
#include "Plugin.hpp"
|
||||
#include "Observer.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
class Tiwiq: public Observer
|
||||
{
|
||||
public:
|
||||
explicit Tiwiq();
|
||||
virtual ~Tiwiq();
|
||||
|
||||
bool is_running() const { return m_is_running; }
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void add_plugin(std::shared_ptr<Plugin> plugin);
|
||||
|
||||
void update(Event& event) override;
|
||||
|
||||
private:
|
||||
bool m_is_running = false;
|
||||
std::vector<std::shared_ptr<Plugin>> m_plugins;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#include "UI.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
/*explicit*/ UI::UI()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ UI::~UI()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef twq_core_UI_HPP
|
||||
#define twq_core_UI_HPP
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
class UI
|
||||
{
|
||||
public:
|
||||
explicit UI();
|
||||
virtual ~UI();
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
21
src/main.cpp
21
src/main.cpp
|
@ -1,6 +1,27 @@
|
|||
#include <iostream>
|
||||
#include "core/Tiwiq.hpp"
|
||||
#include "term/Term.hpp"
|
||||
#include "plugins/CorePlugin.hpp"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
auto tiwiq = std::make_shared<twq::core::Tiwiq>();
|
||||
auto term = std::make_shared<twq::term::Term>();
|
||||
term->controller().lock()->add_observer(tiwiq);
|
||||
|
||||
// Build and add plugins
|
||||
// ---------------------
|
||||
auto core_plugin = std::make_shared<twq::plugins::CorePlugin>();
|
||||
tiwiq->add_plugin(core_plugin);
|
||||
|
||||
// Start TiwiQ
|
||||
// -----------
|
||||
tiwiq->start();
|
||||
|
||||
while (tiwiq->is_running())
|
||||
{
|
||||
term->controller().lock()->update();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#include "CorePlugin.hpp"
|
||||
#include "../core/Executor.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace plugins
|
||||
{
|
||||
/*explicit*/ CorePlugin::CorePlugin()
|
||||
: core::Plugin {"core"}
|
||||
{
|
||||
auto exec = executor().lock();
|
||||
|
||||
auto sc = std::make_shared<core::Shortcut>(L"C-C C-C");
|
||||
auto cmd = std::make_shared<cmd::QuitCmd>();
|
||||
auto binding = core::Binding{sc, cmd};
|
||||
exec->register_binding(binding);
|
||||
}
|
||||
|
||||
/*virtual*/ CorePlugin::~CorePlugin()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef twq_plugins_COREPLUGIN_HPP
|
||||
#define twq_plugins_COREPLUGIN_HPP
|
||||
|
||||
#include "../core/Plugin.hpp"
|
||||
#include "../cmd/QuitCmd.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace plugins
|
||||
{
|
||||
class CorePlugin: public core::Plugin
|
||||
{
|
||||
public:
|
||||
explicit CorePlugin();
|
||||
virtual ~CorePlugin();
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,61 @@
|
|||
#include <curses.h>
|
||||
#include "Controller.hpp"
|
||||
#include "../core/KeyMod.hpp"
|
||||
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace term
|
||||
{
|
||||
/*explicit*/ Controller::Controller()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Controller::~Controller()
|
||||
{
|
||||
}
|
||||
|
||||
void Controller::update()
|
||||
{
|
||||
core::Event event;
|
||||
event.key = get();
|
||||
notify(event);
|
||||
}
|
||||
|
||||
core::KeyMod Controller::get() const
|
||||
{
|
||||
wint_t c;
|
||||
get_wch(&c);
|
||||
|
||||
core::KeyType key_type = core::KM_KEY_TEXT;
|
||||
std::optional<wchar_t> text;
|
||||
std::vector<core::ModType> mods;
|
||||
|
||||
if (c == 27)
|
||||
{
|
||||
get_wch(&c);
|
||||
mods.push_back(core::KM_MOD_ALT);
|
||||
}
|
||||
|
||||
std::string name = keyname(c);
|
||||
std::wstring wname {std::begin(name), std::end(name)};
|
||||
|
||||
if (wname.size() > 1 && wname[0] == L'^')
|
||||
{
|
||||
text = wname[1];
|
||||
mods.push_back(core::KM_MOD_LCTRL);
|
||||
}
|
||||
else if (std::iswprint((wchar_t) c))
|
||||
{
|
||||
text = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
TWQ_ASSERT(0, "keymod not found");
|
||||
}
|
||||
|
||||
auto keymod = core::KeyMod {key_type, mods, text};
|
||||
return keymod;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef twq_term_CONTROLLER_HPP
|
||||
#define twq_term_CONTROLLER_HPP
|
||||
|
||||
#include "../commons.hpp"
|
||||
#include "../core/KeyMod.hpp"
|
||||
#include "../core/Observable.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace term
|
||||
{
|
||||
class Controller: public core::Observable
|
||||
{
|
||||
public:
|
||||
explicit Controller();
|
||||
virtual ~Controller();
|
||||
|
||||
void update();
|
||||
core::KeyMod get() const;
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
#include "Term.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace term
|
||||
{
|
||||
/*explicit*/ Term::Term()
|
||||
{
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
initscr();
|
||||
raw();
|
||||
keypad(stdscr, TRUE);
|
||||
noecho();
|
||||
}
|
||||
|
||||
/*virtual*/ Term::~Term()
|
||||
{
|
||||
endwin();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef twq_term_TERM_HPP
|
||||
#define twq_term_TERM_HPP
|
||||
|
||||
#include <ncurses.h>
|
||||
#include "../commons.hpp"
|
||||
|
||||
#include "Controller.hpp"
|
||||
|
||||
namespace twq
|
||||
{
|
||||
namespace term
|
||||
{
|
||||
class Term
|
||||
{
|
||||
public:
|
||||
explicit Term();
|
||||
virtual ~Term();
|
||||
|
||||
std::weak_ptr<Controller> controller() { return m_controller; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Controller> m_controller =
|
||||
std::make_shared<Controller>();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,4 +1,5 @@
|
|||
#include <catch2/catch.hpp>
|
||||
#include "../src/core/Tiwiq.hpp"
|
||||
#include "../src/core/Executor.hpp"
|
||||
|
||||
using namespace twq::core;
|
||||
|
@ -9,7 +10,13 @@ public:
|
|||
explicit ExecutorTest() {}
|
||||
virtual ~ExecutorTest() {}
|
||||
|
||||
Context get_context()
|
||||
{
|
||||
return Context {m_tiwiq};
|
||||
}
|
||||
|
||||
protected:
|
||||
twq::core::Tiwiq m_tiwiq;
|
||||
};
|
||||
|
||||
struct CommandMock: public Command
|
||||
|
@ -28,95 +35,95 @@ struct CommandMock: public Command
|
|||
|
||||
TEST_CASE_METHOD(ExecutorTest, "Executor_one_key")
|
||||
{
|
||||
Context ctx;
|
||||
Context ctx = get_context();
|
||||
auto cmd = std::make_shared<CommandMock>();
|
||||
Binding b {std::make_shared<Shortcut>("a"), cmd};
|
||||
Binding b {std::make_shared<Shortcut>(L"a"), cmd};
|
||||
|
||||
Executor exec;
|
||||
exec.register_binding(b);
|
||||
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(1 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(2 == cmd->execute_count);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ExecutorTest, "Executor_two_same_keys")
|
||||
{
|
||||
Context ctx;
|
||||
Context ctx = get_context();
|
||||
auto cmd = std::make_shared<CommandMock>();
|
||||
Binding b {std::make_shared<Shortcut>("a a"), cmd};
|
||||
Binding b {std::make_shared<Shortcut>(L"a a"), cmd};
|
||||
|
||||
Executor exec;
|
||||
exec.register_binding(b);
|
||||
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(1 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(1 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(2 == cmd->execute_count);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ExecutorTest, "Executor_two_keys")
|
||||
{
|
||||
Context ctx;
|
||||
Context ctx = get_context();
|
||||
|
||||
auto cmd = std::make_shared<CommandMock>();
|
||||
Binding b {std::make_shared<Shortcut>("a b"), cmd};
|
||||
Binding b {std::make_shared<Shortcut>(L"a b"), cmd};
|
||||
|
||||
Executor exec;
|
||||
exec.register_binding(b);
|
||||
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"b"});
|
||||
exec.update(ctx, KeyMod {L"b"});
|
||||
REQUIRE(1 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(1 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"b"});
|
||||
exec.update(ctx, KeyMod {L"b"});
|
||||
REQUIRE(2 == cmd->execute_count);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE_METHOD(ExecutorTest, "Executor_wrong_key")
|
||||
{
|
||||
Context ctx;
|
||||
Context ctx = get_context();
|
||||
|
||||
auto cmd = std::make_shared<CommandMock>();
|
||||
Binding b {std::make_shared<Shortcut>("a b"), cmd};
|
||||
Binding b {std::make_shared<Shortcut>(L"a b"), cmd};
|
||||
|
||||
Executor exec;
|
||||
exec.register_binding(b);
|
||||
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"c"});
|
||||
exec.update(ctx, KeyMod {L"c"});
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"b"});
|
||||
exec.update(ctx, KeyMod {L"b"});
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"a"});
|
||||
exec.update(ctx, KeyMod {L"a"});
|
||||
REQUIRE(0 == cmd->execute_count);
|
||||
exec.update(ctx, KeyMod {"b"});
|
||||
exec.update(ctx, KeyMod {L"b"});
|
||||
REQUIRE(1 == cmd->execute_count);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
|
||||
{
|
||||
Context ctx;
|
||||
Context ctx = get_context();
|
||||
|
||||
auto cmd0 = std::make_shared<CommandMock>();
|
||||
Binding b0 {std::make_shared<Shortcut>("C-a b"), cmd0};
|
||||
Binding b0 {std::make_shared<Shortcut>(L"C-a b"), cmd0};
|
||||
|
||||
auto cmd1 = std::make_shared<CommandMock>();
|
||||
Binding b1 {std::make_shared<Shortcut>("C-a c"), cmd1};
|
||||
Binding b1 {std::make_shared<Shortcut>(L"C-a c"), cmd1};
|
||||
|
||||
Executor exec;
|
||||
exec.register_binding(b0);
|
||||
|
@ -126,11 +133,11 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
|
|||
{
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"C-a"});
|
||||
exec.update(ctx, KeyMod {L"C-a"});
|
||||
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"b"});
|
||||
exec.update(ctx, KeyMod {L"b"});
|
||||
|
||||
REQUIRE(1 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
|
@ -140,11 +147,11 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
|
|||
{
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"C-a"});
|
||||
exec.update(ctx, KeyMod {L"C-a"});
|
||||
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"c"});
|
||||
exec.update(ctx, KeyMod {L"c"});
|
||||
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(1 == cmd1->execute_count);
|
||||
|
@ -154,15 +161,15 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
|
|||
{
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"C-a"});
|
||||
exec.update(ctx, KeyMod {L"C-a"});
|
||||
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"d"});
|
||||
exec.update(ctx, KeyMod {L"d"});
|
||||
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"b"});
|
||||
exec.update(ctx, KeyMod {L"b"});
|
||||
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
|
@ -171,13 +178,13 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_commands")
|
|||
|
||||
TEST_CASE_METHOD(ExecutorTest, "Executor_two_sequentials_commands")
|
||||
{
|
||||
Context ctx;
|
||||
Context ctx = get_context();
|
||||
|
||||
auto cmd0 = std::make_shared<CommandMock>();
|
||||
Binding b0 {std::make_shared<Shortcut>("C-a b"), cmd0};
|
||||
Binding b0 {std::make_shared<Shortcut>(L"C-a b"), cmd0};
|
||||
|
||||
auto cmd1 = std::make_shared<CommandMock>();
|
||||
Binding b1 {std::make_shared<Shortcut>("b c"), cmd1};
|
||||
Binding b1 {std::make_shared<Shortcut>(L"b c"), cmd1};
|
||||
|
||||
Executor exec;
|
||||
exec.register_binding(b0);
|
||||
|
@ -187,23 +194,23 @@ TEST_CASE_METHOD(ExecutorTest, "Executor_two_sequentials_commands")
|
|||
{
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"C-a"});
|
||||
exec.update(ctx, KeyMod {L"C-a"});
|
||||
|
||||
REQUIRE(0 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"b"});
|
||||
exec.update(ctx, KeyMod {L"b"});
|
||||
|
||||
REQUIRE(1 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"c"});
|
||||
exec.update(ctx, KeyMod {L"c"});
|
||||
|
||||
REQUIRE(1 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"b"});
|
||||
exec.update(ctx, KeyMod {L"b"});
|
||||
|
||||
REQUIRE(1 == cmd0->execute_count);
|
||||
REQUIRE(0 == cmd1->execute_count);
|
||||
exec.update(ctx, KeyMod {"c"});
|
||||
exec.update(ctx, KeyMod {L"c"});
|
||||
|
||||
REQUIRE(1 == cmd0->execute_count);
|
||||
REQUIRE(1 == cmd1->execute_count);
|
||||
|
|
|
@ -9,7 +9,7 @@ public:
|
|||
explicit ShortcutTest() {}
|
||||
virtual ~ShortcutTest() {}
|
||||
|
||||
void test_to_string(std::string const& oracle,
|
||||
void test_to_string(std::wstring const& oracle,
|
||||
std::vector<KeyMod> keymods)
|
||||
{
|
||||
Shortcut sc;
|
||||
|
@ -19,16 +19,15 @@ public:
|
|||
sc.push(km);
|
||||
}
|
||||
|
||||
INFO("Expected '" << oracle << "' got '" << sc.string() << "'");
|
||||
REQUIRE(oracle == sc.string());
|
||||
REQUIRE(oracle == sc.wstring());
|
||||
}
|
||||
|
||||
void test_from_string(std::string const& oracle,
|
||||
std::string const& shortcut)
|
||||
void test_from_string(std::wstring const& oracle,
|
||||
std::wstring const& shortcut)
|
||||
{
|
||||
Shortcut sc { shortcut };
|
||||
INFO("Expected '" << oracle << "' got '" << sc.string() << "'");
|
||||
REQUIRE(oracle == sc.string());
|
||||
|
||||
REQUIRE(oracle == sc.wstring());
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -36,71 +35,71 @@ protected:
|
|||
|
||||
TEST_CASE_METHOD(ShortcutTest, "Shortcut_string")
|
||||
{
|
||||
test_to_string("f", {KeyMod {'f'}});
|
||||
test_to_string(L"f", {KeyMod {'f'}});
|
||||
|
||||
test_to_string("a b c", {
|
||||
KeyMod {'a'},
|
||||
KeyMod {'b'},
|
||||
KeyMod {'c'},
|
||||
test_to_string(L"a b c", {
|
||||
KeyMod {L'a'},
|
||||
KeyMod {L'b'},
|
||||
KeyMod {L'c'},
|
||||
});
|
||||
|
||||
test_to_string("C-g", {KeyMod {'g', {MOD_LCTRL}}});
|
||||
test_to_string("A-g", {KeyMod {'g', {MOD_ALT}}});
|
||||
test_to_string("C-A-g", {KeyMod {'g', {MOD_LCTRL, MOD_ALT}}});
|
||||
test_to_string(L"C-g", {KeyMod {L'g', {KM_MOD_LCTRL}}});
|
||||
test_to_string(L"A-g", {KeyMod {L'g', {KM_MOD_ALT}}});
|
||||
test_to_string(L"C-A-g", {KeyMod {L'g', {KM_MOD_LCTRL, KM_MOD_ALT}}});
|
||||
|
||||
test_to_string("A-a C-A-b C-c d", {
|
||||
KeyMod {'a', {MOD_ALT}},
|
||||
KeyMod {'b', {MOD_LCTRL, MOD_ALT}},
|
||||
KeyMod {'c', {MOD_LCTRL}},
|
||||
KeyMod {'d'},
|
||||
test_to_string(L"A-a C-A-b C-c d", {
|
||||
KeyMod {L'a', {KM_MOD_ALT}},
|
||||
KeyMod {L'b', {KM_MOD_LCTRL, KM_MOD_ALT}},
|
||||
KeyMod {L'c', {KM_MOD_LCTRL}},
|
||||
KeyMod {L'd'},
|
||||
});
|
||||
|
||||
test_to_string("UP", {
|
||||
KeyMod::key(KEY_UP)
|
||||
test_to_string(L"UP", {
|
||||
KeyMod::key(KM_KEY_UP)
|
||||
});
|
||||
|
||||
test_to_string("DOWN", {
|
||||
KeyMod::key(KEY_DOWN)
|
||||
test_to_string(L"DOWN", {
|
||||
KeyMod::key(KM_KEY_DOWN)
|
||||
});
|
||||
|
||||
test_to_string("LEFT", {
|
||||
KeyMod::key(KEY_LEFT)
|
||||
test_to_string(L"LEFT", {
|
||||
KeyMod::key(KM_KEY_LEFT)
|
||||
});
|
||||
|
||||
test_to_string("RIGHT", {
|
||||
KeyMod::key(KEY_RIGHT)
|
||||
test_to_string(L"RIGHT", {
|
||||
KeyMod::key(KM_KEY_RIGHT)
|
||||
});
|
||||
|
||||
test_to_string("C-UP", {
|
||||
KeyMod::key(KEY_UP, {MOD_LCTRL})
|
||||
test_to_string(L"C-UP", {
|
||||
KeyMod::key(KM_KEY_UP, {KM_MOD_LCTRL})
|
||||
});
|
||||
|
||||
test_to_string("A-LEFT", {
|
||||
KeyMod::key(KEY_LEFT, {MOD_ALT}),
|
||||
test_to_string(L"A-LEFT", {
|
||||
KeyMod::key(KM_KEY_LEFT, {KM_MOD_ALT}),
|
||||
});
|
||||
|
||||
test_to_string("C-A-RIGHT", {
|
||||
KeyMod::key(KEY_RIGHT, {MOD_LCTRL, MOD_ALT}),
|
||||
test_to_string(L"C-A-RIGHT", {
|
||||
KeyMod::key(KM_KEY_RIGHT, {KM_MOD_LCTRL, KM_MOD_ALT}),
|
||||
});
|
||||
|
||||
test_from_string("", "");
|
||||
test_from_string(L"", L"");
|
||||
|
||||
test_from_string("a", "a");
|
||||
test_from_string("a", " a");
|
||||
test_from_string("a", "a ");
|
||||
test_from_string(L"a", L"a");
|
||||
test_from_string(L"a", L" a");
|
||||
test_from_string(L"a", L"a ");
|
||||
|
||||
test_from_string("C-a", "C-a");
|
||||
test_from_string("C-a", " C-a ");
|
||||
test_from_string("A-a", "A-a");
|
||||
test_from_string("A-a", " A-a ");
|
||||
test_from_string(L"C-a", L"C-a");
|
||||
test_from_string(L"C-a", L" C-a ");
|
||||
test_from_string(L"A-a", L"A-a");
|
||||
test_from_string(L"A-a", L" A-a ");
|
||||
|
||||
test_from_string("C-A-a", " A-C-a ");
|
||||
test_from_string("C-A-a", " C-A-a ");
|
||||
test_from_string(L"C-A-a", L" A-C-a ");
|
||||
test_from_string(L"C-A-a", L" C-A-a ");
|
||||
|
||||
test_from_string("C-A-a b C-c", " C-A-a b C-c");
|
||||
test_from_string(L"C-A-a b C-c", L" C-A-a b C-c");
|
||||
|
||||
test_from_string("C-A-LEFT RIGHT C-UP",
|
||||
" C-A-LEFT RIGHT C-UP");
|
||||
test_from_string(L"C-A-LEFT RIGHT C-UP",
|
||||
L" C-A-LEFT RIGHT C-UP");
|
||||
|
||||
REQUIRE_THROWS_AS(Shortcut { "C-A-DUCK" }, invalid_shortcut_error);
|
||||
REQUIRE_THROWS_AS(Shortcut { L"C-A-DUCK" }, invalid_shortcut_error);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue