diff --git a/src/lib/nccpp/Color.hpp b/src/lib/nccpp/Color.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a8d44d65e3bee26eca2779464d1d36bc178bcc70
--- /dev/null
+++ b/src/lib/nccpp/Color.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+namespace nccpp {
+
+/**
+ * \brief Class representing a ncurses color pair.
+ */
+struct Color {
+    Color() : Color{-1, -1} {}
+    Color(short f, short b) : foreground{f}, background{b} {}
+
+    short foreground; ///< Foreground color.
+    short background; ///< Background color.
+};
+
+inline bool operator==(nccpp::Color const &lhs, nccpp::Color const &rhs) {
+    return (lhs.foreground == rhs.foreground) && (lhs.background == rhs.background);
+}
+
+inline bool operator!=(nccpp::Color const &lhs, nccpp::Color const &rhs) {
+    return !(lhs == rhs);
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Ncurses.cpp b/src/lib/nccpp/Ncurses.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..59eab3260b039f68e194b993b21106ce84be693c
--- /dev/null
+++ b/src/lib/nccpp/Ncurses.cpp
@@ -0,0 +1,333 @@
+#include "lib/nccpp/Ncurses.hpp"
+
+#include <algorithm>
+#include <cassert>
+
+#include "lib/nccpp/errors.hpp"
+
+namespace nccpp {
+
+inline Ncurses::Ncurses()    : Window{initscr()}, registered_colors_{},
+#ifndef NDEBUG
+    windows_ {}, is_exit_ {false},
+#endif
+    colors_initialized_ {false}
+{
+    if (!win_) {
+        throw errors::NcursesInit{};
+    }
+}
+
+inline Ncurses::~Ncurses() {
+    endwin();
+    win_ = nullptr;
+#ifdef NO_LEAKS
+    _nc_freeall();
+#endif
+}
+
+#ifndef NDEBUG
+inline void Ncurses::register_window_(Window &new_win, Window::Key /*dummy*/) {
+    windows_.push_back(&new_win);
+}
+
+inline void Ncurses::unregister_window_(Window &win, Window::Key /*dummy*/) {
+    auto it = std::find(std::begin(windows_), std::end(windows_), &win);
+
+    assert(it != std::end(windows_));
+
+    windows_.erase(it);
+}
+#endif
+
+inline void Ncurses::exit_ncurses_mode() {
+    assert(!is_exit_ && "Ncurses mode is already off");
+
+#ifndef NDEBUG
+
+    for (auto elem : windows_) {
+        elem->invalidate_for_exit_(Window::Key{});
+    }
+
+    invalidate_for_exit_(Key{});
+    is_exit_ = true;
+
+#endif
+
+    endwin();
+}
+
+inline void Ncurses::resume_ncurses_mode() {
+    assert(is_exit_ && "Ncurses mode is already on");
+
+#ifndef NDEBUG
+
+    for (auto elem : windows_) {
+        elem->validate_for_resume_(Window::Key{});
+    }
+
+    validate_for_resume_(Key{});
+    is_exit_ = false;
+
+#endif
+
+    doupdate();
+}
+
+// Input options
+
+inline int Ncurses::cbreak(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return on ? ::cbreak() : nocbreak();
+}
+
+inline int Ncurses::echo(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return on ? ::echo() : noecho();
+}
+
+inline int Ncurses::halfdelay(int delay) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::halfdelay(delay);
+}
+
+inline int Ncurses::intrflush(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::intrflush(win_, on);
+}
+
+inline int Ncurses::meta(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::meta(win_, on);
+}
+
+inline int Ncurses::raw(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return on ? ::raw() : noraw();
+}
+
+inline void Ncurses::qiflush(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    on ? ::qiflush() : noqiflush();
+}
+
+inline int Ncurses::typeahead(int fd) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::typeahead(fd);
+}
+
+// Output options
+
+inline int Ncurses::clearok(bool on, bool use_cs) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return::clearok(use_cs ? curscr : win_, on);
+}
+
+inline int Ncurses::idlok(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::idlok(win_, on);
+}
+
+inline void Ncurses::idcok(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    ::idcok(win_, on);
+}
+
+inline void Ncurses::immedok(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    ::immedok(win_, on);
+}
+
+inline int Ncurses::leaveok(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::leaveok(win_, on);
+}
+
+inline int Ncurses::scrollok(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::scrollok(win_, on);
+}
+
+inline int Ncurses::nl(bool on) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return on ? ::nl() : nonl();
+}
+
+// Input functions
+
+inline int Ncurses::ungetch(int ch) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::ungetch(ch);
+}
+
+inline int Ncurses::has_key(int ch) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::has_key(ch);
+}
+
+// Misc
+
+inline int Ncurses::doupdate() {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::doupdate();
+}
+
+inline int Ncurses::line_count() {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return LINES;
+}
+
+inline int Ncurses::column_count() {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return COLS;
+}
+
+// Mouse
+
+inline bool Ncurses::has_mouse() {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::has_mouse();
+}
+
+inline int Ncurses::getmouse(MEVENT &event) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::getmouse(&event);
+}
+
+inline int Ncurses::ungetmouse(MEVENT &event) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::ungetmouse(&event);
+}
+
+inline mmask_t Ncurses::mousemask(mmask_t newmask, mmask_t* oldmask) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::mousemask(newmask, oldmask);
+}
+
+inline int Ncurses::mouseinterval(int erval) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return ::mouseinterval(erval);
+}
+
+// Window
+
+inline WINDOW* Ncurses::newwin_(int nlines, int ncols, int begin_y, int begin_x, Window::Key /*dummy*/) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return newwin(nlines, ncols, begin_y, begin_x);
+}
+
+// Color
+
+inline void Ncurses::start_color() {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    if (colors_initialized_) {
+        return;
+    }
+
+    if (::start_color() == ERR) {
+        throw errors::ColorInit{};
+    }
+
+    colors_initialized_ = true;
+}
+
+inline int Ncurses::use_default_colors() {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    start_color();
+
+    return ::use_default_colors();
+}
+
+inline short Ncurses::color_to_pair_number(Color const &color) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    auto it = std::find_if(
+                  std::begin(registered_colors_),
+                  std::end(registered_colors_),
+    [color](Color const & elem) {
+        return color == elem;
+    });
+
+    if (it != std::end(registered_colors_)) {
+        return static_cast<short>(it - std::begin(registered_colors_) + 1);
+    }
+
+    start_color();
+
+    // Ensure push_back will not throw
+    registered_colors_.reserve(registered_colors_.size() + 1);
+    auto res = init_pair(static_cast<short>(registered_colors_.size() + 1), color.foreground, color.background);
+
+    if (res == ERR) {
+        throw errors::TooMuchColors{color};
+    }
+
+    registered_colors_.push_back(color);
+
+    return static_cast<short>(registered_colors_.size());
+}
+
+inline attr_t Ncurses::color_to_attr(Color const &color) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return static_cast<attr_t>(COLOR_PAIR(color_to_pair_number(color)));
+}
+
+inline Color Ncurses::pair_number_to_color(short pair_n) {
+    assert(!is_exit_ && "Ncurses mode is off");
+    assert(static_cast<std::size_t>(pair_n) <= registered_colors_.size() && "No such color");
+
+    return registered_colors_[static_cast<std::size_t>(pair_n - 1)];
+}
+
+inline Color Ncurses::attr_to_color(attr_t a) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    return pair_number_to_color(static_cast<short>(PAIR_NUMBER(static_cast<int>(a))));
+}
+
+inline int Ncurses::init_color(short color, short r, short g, short b) {
+    assert(!is_exit_ && "Ncurses mode is off");
+
+    start_color();
+
+    return ::init_color(color, r, g, b);
+}
+
+inline void Ncurses::assign(WINDOW*) {
+    assert(false && "Can't call nccpp::Ncurses::assign");
+}
+
+inline void Ncurses::destroy() {
+    assert(false && "Can't call nccpp::Ncurses::destroy");
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Ncurses.hpp b/src/lib/nccpp/Ncurses.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e31f0f9a4cd138311b31e77b36936f0795527ea4
--- /dev/null
+++ b/src/lib/nccpp/Ncurses.hpp
@@ -0,0 +1,119 @@
+#pragma once
+
+#include <vector>
+
+#include "lib/ncurses/ncurses.hpp"
+
+#include "lib/nccpp/Color.hpp"
+
+namespace nccpp {
+    class Ncurses;
+}
+
+#include "lib/nccpp/Window.hpp"
+
+namespace nccpp{
+
+class Ncurses : public Window{
+    friend Ncurses &ncurses();
+public:
+    Ncurses(const Ncurses &) = delete;
+    Ncurses(Ncurses &&) = delete;
+    Ncurses &operator=(const Ncurses &) = delete;
+    Ncurses &operator=(Ncurses &&) = delete;
+    ~Ncurses();
+
+    void exit_ncurses_mode();
+    void resume_ncurses_mode();
+
+    // Input options
+
+    int cbreak(bool);
+    int echo(bool);
+    int halfdelay(int);
+    int intrflush(bool);
+    int meta(bool);
+    int raw(bool);
+    void qiflush(bool);
+    int typeahead(int);
+
+    // Output options
+
+    int clearok(bool, bool = false);
+    int idlok(bool);
+    void idcok(bool);
+    void immedok(bool);
+    int leaveok(bool);
+    int scrollok(bool);
+    int nl(bool);
+
+    // Input functions
+
+    int ungetch(int);
+    int has_key(int);
+
+    // Misc
+
+    int doupdate();
+    int line_count();
+    int column_count();
+
+    // Mouse
+
+    bool has_mouse();
+    int getmouse(MEVENT &);
+    int ungetmouse(MEVENT &);
+    mmask_t mousemask(mmask_t, mmask_t* = nullptr);
+    int mouseinterval(int);
+
+    // Window
+
+    /// \cond NODOC
+    WINDOW* newwin_(int, int, int, int, Window::Key);
+
+#ifndef NDEBUG
+    void register_window_(Window &, Window::Key);
+    void unregister_window_(Window &, Window::Key);
+#endif
+
+    // Color
+
+    void start_color();
+    int use_default_colors();
+
+    short color_to_pair_number(Color const &);
+    attr_t color_to_attr(Color const &);
+    Color pair_number_to_color(short);
+    Color attr_to_color(attr_t);
+
+    int init_color(short, short, short, short);
+
+private:
+    Ncurses();
+
+    std::vector<Color> registered_colors_;
+
+#ifndef NDEBUG
+    std::vector<Window*> windows_;
+    bool is_exit_;
+#endif
+
+    bool colors_initialized_;
+
+    void assign(WINDOW*) override;
+    void destroy() override;
+};
+
+/**
+ * Access the Ncurses singleton.
+ *
+ * \exception errors::NcursesInit Thrown when ncurses can't be initialized.
+ * \return A reference to the singleton.
+ */
+inline Ncurses &ncurses()
+{
+    static Ncurses nc{};
+    return nc;
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Subwindow.cpp b/src/lib/nccpp/Subwindow.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..36c23796827de6cc3e3a0346c85ea9e95bee5ac9
--- /dev/null
+++ b/src/lib/nccpp/Subwindow.cpp
@@ -0,0 +1,53 @@
+#include "lib/nccpp/Subwindow.hpp"
+
+#include <cassert>
+
+#include "lib/nccpp/Window.hpp"
+
+namespace nccpp {
+
+inline Window &Subwindow::get_parent() {
+    assert(win_ && "Invalid subwindow");
+
+    return parent_;
+}
+
+inline int Subwindow::mvderwin(int y, int x) {
+    assert(win_ && "Invalid subwindow");
+
+    return ::mvderwin(win_, y, x);
+}
+
+inline void Subwindow::syncup() {
+    assert(win_ && "Invalid subwindow");
+
+    wsyncup(win_);
+}
+
+inline int Subwindow::syncok(bool on) {
+    assert(win_ && "Invalid subwindow");
+
+    return ::syncok(win_, on);
+}
+
+inline void Subwindow::cursyncup() {
+    assert(win_ && "Invalid subwindow");
+
+    wcursyncup(win_);
+}
+
+inline void Subwindow::syncdown() {
+    assert(win_ && "Invalid subwindow");
+
+    wsyncdown(win_);
+}
+
+inline void Subwindow::assign(WINDOW*) {
+    assert(false && "Can't call nccpp::Subwindow::assign");
+}
+
+inline void Subwindow::destroy() {
+    assert(false && "Can't call nccpp::Subwindow::destroy");
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Subwindow.hpp b/src/lib/nccpp/Subwindow.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0d0a59a56b4e60ecf43fcb67f0c760dbd8d4e408
--- /dev/null
+++ b/src/lib/nccpp/Subwindow.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "lib/nccpp/Ncurses.hpp"
+#include "lib/nccpp/Window.hpp"
+
+namespace nccpp {
+
+class Subwindow : public Window {
+public:
+    Subwindow(Window &parent, WINDOW* subwin, Window::Key /*dummy*/)
+        : Window{subwin}, parent_{parent}
+    {}
+
+    Subwindow(const Subwindow &) = delete;
+    Subwindow(Subwindow &&) = default;
+    Subwindow &operator=(const Subwindow &) = delete;
+    Subwindow &operator=(Subwindow &&) = default;
+
+    ~Subwindow() = default;
+
+    Window &get_parent();
+
+    int mvderwin(int, int);
+
+    void syncup();
+    int syncok(bool);
+    void cursyncup();
+    void syncdown();
+
+private:
+    Window &parent_;
+
+    void assign(WINDOW*) override;
+    void destroy() override;
+};
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Window.cpp b/src/lib/nccpp/Window.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5b39a306248053cac53d2d4363b0ff8704421f8
--- /dev/null
+++ b/src/lib/nccpp/Window.cpp
@@ -0,0 +1,269 @@
+#include "lib/nccpp/Window.hpp"
+
+#include <cassert>
+
+#include "lib/nccpp/errors.hpp"
+#include "lib/nccpp/Ncurses.hpp"
+#include "lib/nccpp/Subwindow.hpp"
+
+namespace nccpp{
+
+inline Window::Window(WINDOW* win) :
+    win_{win},
+#ifndef NDEBUG
+    win_save_ {nullptr},
+#endif
+    subwindows_ {}
+{
+#ifndef NDEBUG
+
+    if (win_ != stdscr) {
+        ncurses().register_window_(*this, Key{});
+    }
+
+#endif
+}
+
+inline Window::Window(int nlines, int ncols, int begin_y, int begin_x) :
+    win_{ncurses().newwin_(nlines, ncols, begin_y, begin_x, Key{})},
+#ifndef NDEBUG
+    win_save_ {nullptr},
+#endif
+    subwindows_ {}
+{
+    if (!win_) {
+        throw errors::WindowInit{};
+    }
+
+#ifndef NDEBUG
+
+    try {
+        ncurses().register_window_(*this, Key{});
+    }
+    catch (...) {
+        delwin(win_);
+        throw;
+    }
+
+#endif
+}
+
+inline Window::Window(Window const &cp) :
+    win_{nullptr},
+#ifndef NDEBUG
+    win_save_ {nullptr},
+#endif
+    subwindows_ {}
+{
+    assert(!cp.win_save_ && "Can't duplicate windows while ncurses mode is off");
+    subwindows_.reserve(cp.subwindows_.size());
+
+    if (cp.win_ && !(win_ = dupwin(cp.win_))) {
+        throw errors::WindowInit{};
+    }
+
+#ifndef NDEBUG
+
+    try {
+        ncurses().register_window_(*this, Key{});
+    }
+    catch (...) {
+        delwin(win_);
+        throw;
+    }
+
+#endif
+}
+
+inline Window &Window::operator=(Window const &cp){
+    if (this != &cp) {
+        Window tmp{cp};
+        *this = std::move(tmp);
+    }
+
+    return *this;
+}
+
+inline Window::Window(Window &&mv)
+#ifdef NDEBUG
+noexcept
+#endif
+    :
+    win_ {mv.win_},
+#ifndef NDEBUG
+    win_save_ {mv.win_save_},
+#endif
+    subwindows_ {std::move(mv.subwindows_)}
+{
+    mv.win_ = nullptr;
+#ifndef NDEBUG
+    mv.win_save_ = nullptr;
+    ncurses().register_window_(*this, Key{});
+#endif
+}
+
+inline Window &Window::operator=(Window &&mv) noexcept {
+    if (this != &mv) {
+        destroy();
+        win_ = mv.win_;
+        mv.win_ = nullptr;
+
+#ifndef NDEBUG
+        win_save_ = mv.win_save_;
+        mv.win_save_ = nullptr;
+#endif
+
+        subwindows_ = std::move(mv.subwindows_);
+    }
+
+    return *this;
+}
+
+inline Window::~Window() {
+#ifndef NDEBUG
+
+    if (this != &ncurses()) {
+        ncurses().unregister_window_(*this, Key{});
+    }
+
+#endif
+
+    destroy();
+}
+
+inline void Window::assign(WINDOW* new_win) {
+    assert(!win_save_ && "Can't modify window while ncurses mode is off");
+
+    if (win_) {
+        destroy();
+    }
+
+    win_ = new_win;
+}
+
+inline void Window::destroy() {
+    if (win_) {
+        subwindows_.clear();
+        delwin(win_);
+        win_ = nullptr;
+    }
+
+#ifndef NDEBUG
+    else if (win_save_) {
+        subwindows_.clear();
+        delwin(win_save_);
+        win_save_ = nullptr;
+    }
+
+#endif
+}
+
+inline WINDOW* Window::get_handle() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return win_;
+}
+
+inline WINDOW const* Window::get_handle() const {
+    assert(win_ && "Window doesn't manage any object");
+    return win_;
+}
+
+inline std::size_t Window::add_subwindow(int lines, int cols, int beg_y, int beg_x) {
+    assert(win_ && "Window doesn't manage any object");
+
+#ifndef NDEBUG
+    int posx = 0, posy = 0, maxx = 0, maxy = 0;
+    get_begyx(posy, posx);
+    get_maxyx(maxy, maxx);
+    maxy += posy;
+    maxx += posx;
+#endif
+
+    assert(posy <= beg_y && posx <= beg_x && maxy >= beg_y + lines && maxx >= beg_x + cols &&
+           "Invalid subwindow coordinates");
+
+    auto new_subw = subwin(win_, lines, cols, beg_y, beg_x);
+
+    if (!new_subw) {
+        throw errors::WindowInit{};
+    }
+
+    try {
+        subwindows_.emplace_back(*this, new_subw, Window::Key{});
+    }
+    catch (...) {
+        delwin(new_subw);
+        throw;
+    }
+
+    return subwindows_.size() - 1;
+}
+
+inline Subwindow &Window::get_subwindow(std::size_t index) {
+    assert(win_ && "Window doesn't manage any object");
+    assert(index < subwindows_.size() && subwindows_[index].win_ && "Invalid subwindow");
+
+    return subwindows_[index];
+}
+
+inline void Window::delete_subwindow(std::size_t index) {
+    assert(win_ && "Window doesn't manage any object");
+    assert(index < subwindows_.size() && subwindows_[index].win_ && "Invalid subwindow");
+
+    subwindows_[index].~Subwindow();
+    new (&subwindows_[index]) Subwindow{*this, nullptr, Key{}};
+}
+
+#ifndef NDEBUG
+inline void Window::invalidate_for_exit_(Window::Key /*dummy*/) {
+    for (auto &elem : subwindows_) {
+        elem.invalidate_for_exit_(Key{});
+    }
+
+    win_save_ = win_;
+    win_ = nullptr;
+}
+
+inline void Window::validate_for_resume_(Window::Key /*dummy*/) {
+    for (auto &elem : subwindows_) {
+        elem.validate_for_resume_(Key{});
+    }
+
+    win_ = win_save_;
+    win_save_ = nullptr;
+}
+#endif
+
+inline int overlay(Window const &src, Window &dst) {
+    return ::overlay(src.get_handle(), dst.get_handle());
+}
+
+inline int overwrite(Window const &src, Window &dst) {
+    return ::overwrite(src.get_handle(), dst.get_handle());
+}
+
+inline int copywin(
+    Window const &src,
+    Window &dst,
+    int sminrow,
+    int smincol,
+    int dminrow,
+    int dmincol,
+    int dmaxrow,
+    int dmaxcol,
+    bool overlay
+) {
+    return ::copywin(
+               src.get_handle(),
+               dst.get_handle(),
+               sminrow,
+               smincol,
+               dminrow,
+               dmincol,
+               dmaxrow,
+               dmaxcol,
+               overlay);
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Window.hpp b/src/lib/nccpp/Window.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9a98f06978d3bd1153ebcf993f208cc81cd861af
--- /dev/null
+++ b/src/lib/nccpp/Window.hpp
@@ -0,0 +1,201 @@
+#pragma once
+
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include "lib/ncurses/ncurses.hpp"
+
+#include "lib/nccpp/Color.hpp"
+
+namespace nccpp {
+
+/** \brief Alias used for input functions. */
+using String = std::basic_string<chtype>;
+
+class Ncurses;
+class Subwindow;
+
+class Window {
+public:
+    explicit Window(WINDOW*);
+    Window(int, int, int, int);
+
+    Window(Window const &);
+    Window &operator=(Window const &);
+
+    Window(Window &&)
+#ifdef NDEBUG
+    noexcept
+#endif
+    ;
+
+    Window &operator=(Window &&) noexcept;
+
+    Window(const Ncurses &) = delete;
+    Window(Ncurses &&) = delete;
+    Window &operator=(const Ncurses &) = delete;
+    Window &operator=(Ncurses &&) = delete;
+
+    Window(const Subwindow &) = delete;
+    Window(Subwindow &&) = delete;
+    Window &operator=(const Subwindow &) = delete;
+    Window &operator=(Subwindow &&) = delete;
+
+    ~Window();
+
+    virtual void assign(WINDOW*);
+    virtual void destroy();
+    WINDOW* get_handle();
+    WINDOW const* get_handle() const;
+
+    std::size_t add_subwindow(int, int, int, int);
+    Subwindow &get_subwindow(std::size_t);
+    void delete_subwindow(std::size_t);
+
+    // Input options
+
+    int keypad(bool);
+    int nodelay(bool);
+    int notimeout(bool);
+    void timeout(int);
+
+    // Output options
+
+    int clearok(bool);
+    int setscrreg(int, int);
+
+    // Input functions
+
+    int getch();
+    int mvgetch(int, int);
+
+    int scanw(char const*, ...);
+    int mvscanw(int, int, char const*, ...);
+
+    int getstr(std::string &);
+    int getnstr(std::string &, std::size_t);
+    int mvgetstr(int, int, std::string &);
+    int mvgetnstr(int, int, std::string &, std::size_t);
+
+    chtype inch();
+    chtype mvinch(int, int);
+
+    int instr(std::string &);
+    int innstr(std::string &, std::size_t);
+    int mvinstr(int, int, std::string &);
+    int mvinnstr(int, int, std::string &, std::size_t);
+
+    int inchstr(String &);
+    int inchnstr(String &, std::size_t);
+    int mvinchstr(int, int, String &);
+    int mvinchnstr(int, int, String &, std::size_t);
+
+    // Output functions
+
+    int addch(chtype const);
+    int mvaddch(int, int, chtype const);
+    int echochar(chtype const);
+
+    int printw(char const*, ...);
+    int mvprintw(int, int, char const*, ...);
+
+    int addstr(std::string const &);
+    int addnstr(std::string const &, std::size_t);
+    int mvaddstr(int, int, std::string const &);
+    int mvaddnstr(int, int, std::string const &, std::size_t);
+
+    int addchstr(String const &);
+    int addchnstr(String const &, std::size_t);
+    int mvaddchstr(int, int, String const &);
+    int mvaddchnstr(int, int, String const &, std::size_t);
+
+    int insch(chtype);
+    int mvinsch(int y, int x, chtype);
+
+    int insstr(std::string const &);
+    int insnstr(std::string const &, std::size_t);
+    int mvinsstr(int, int, std::string const &);
+    int mvinsnstr(int, int, std::string const &, std::size_t);
+
+    // Deletion functions
+
+    int delch();
+    int mvdelch(int, int);
+
+    int insdelln(int);
+
+    // Border
+
+    int border(chtype, chtype, chtype, chtype, chtype, chtype, chtype, chtype);
+    int box(chtype, chtype);
+
+    int hline(chtype, int);
+    int vline(chtype, int);
+    int mvhline(int, int, chtype, int);
+    int mvvline(int, int, chtype, int);
+
+    // Background
+
+    void bkgdset(int);
+    int bkgd(int);
+    chtype getbkgd();
+
+    // Attributes
+
+    int attroff(int);
+    int attron(int);
+    int attrset(int);
+
+    int attr_get(attr_t &);
+    int color_get(Color &);
+
+    int attr_color_get(attr_t &, Color &);
+
+    int chgat(int, attr_t, Color);
+    int mvchgat(int, int, int, attr_t, Color);
+
+    // Misc
+
+    int move(int, int);
+    int mvwin(int, int);
+
+    int erase();
+    int clear();
+    int clrtobot();
+    int clrtoeol();
+
+    int refresh();
+    int outrefresh();
+    int redraw();
+    int redrawln(int, int);
+
+    int scroll(int = 1);
+
+    void get_yx(int &, int &);
+    void get_begyx(int &, int &);
+    void get_maxyx(int &, int &);
+
+    int touchln(int, int, bool);
+
+    bool enclose(int, int);
+    bool coord_trafo(int &, int &, bool);
+
+protected:
+    struct Key {};
+
+    WINDOW* win_;
+
+#ifndef NDEBUG
+    WINDOW* win_save_;
+
+public:
+    void invalidate_for_exit_(Key);
+    void validate_for_resume_(Key);
+#endif
+
+private:
+    std::vector<Subwindow> subwindows_;
+};
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Window_appearance.cpp b/src/lib/nccpp/Window_appearance.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..80ac2fea457b598fbba3fcb17c2869ddfd7b7f71
--- /dev/null
+++ b/src/lib/nccpp/Window_appearance.cpp
@@ -0,0 +1,61 @@
+#include "lib/nccpp/Window.hpp"
+
+#include <cassert>
+
+namespace nccpp {
+
+inline int Window::border(chtype ls, chtype rs, chtype ts, chtype bs,                          chtype tl, chtype tr, chtype bl, chtype br) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wborder(win_, ls, rs, ts, bs, tl, tr, bl, br);
+}
+
+inline int Window::box(chtype vch, chtype hch) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return ::box(win_, vch, hch);
+}
+
+inline int Window::hline(chtype ch, int n) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return whline(win_, ch, n);
+}
+
+inline int Window::vline(chtype ch, int n) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wvline(win_, ch, n);
+}
+
+inline int Window::mvhline(int y, int x, chtype ch, int n) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return (this->move)(y, x) == ERR ? ERR : (this->hline)(ch, n);
+}
+
+inline int Window::mvvline(int y, int x, chtype ch, int n) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return (this->move)(y, x) == ERR ? ERR : (this->vline)(ch, n);
+}
+
+inline void Window::bkgdset(int ch) {
+    assert(win_ && "Window doesn't manage any object");
+
+    wbkgdset(win_, static_cast<chtype>(ch));
+}
+
+inline int Window::bkgd(int ch) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wbkgd(win_, static_cast<chtype>(ch));
+}
+
+inline chtype Window::getbkgd() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return (::getbkgd)(win_);
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Window_attributes.cpp b/src/lib/nccpp/Window_attributes.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a845d0341c7420e849c532ef0e5260cf3bbae95a
--- /dev/null
+++ b/src/lib/nccpp/Window_attributes.cpp
@@ -0,0 +1,74 @@
+#include "lib/nccpp/Window.hpp"
+
+#include <cassert>
+
+#include "lib/nccpp/Color.hpp"
+#include "lib/nccpp/Ncurses.hpp"
+
+namespace nccpp {
+
+inline int Window::attroff(int a) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wattroff(win_, a);
+}
+
+inline int Window::attron(int a) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wattron(win_, a);
+}
+
+inline int Window::attrset(int a) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wattrset(win_, a);
+}
+
+inline int Window::attr_get(attr_t &a) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wattr_get(win_, &a, nullptr, nullptr);
+}
+
+inline int Window::color_get(Color &c) {
+    assert(win_ && "Window doesn't manage any object");
+
+    short pair_n{0};
+
+    if (wattr_get(win_, nullptr, &pair_n, nullptr) == ERR) {
+        return ERR;
+    }
+
+    c = nccpp::ncurses().pair_number_to_color(pair_n);
+
+    return OK;
+}
+
+inline int Window::attr_color_get(attr_t &a, Color &c) {
+    assert(win_ && "Window doesn't manage any object");
+
+    short pair_n{0};
+
+    if (wattr_get(win_, &a, &pair_n, nullptr) == ERR) {
+        return ERR;
+    }
+
+    c = nccpp::ncurses().pair_number_to_color(pair_n);
+
+    return OK;
+}
+
+inline int Window::chgat(int n, attr_t a, Color c) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return ::wchgat(win_, n, a, nccpp::ncurses().color_to_pair_number(c), nullptr);
+}
+
+inline int Window::mvchgat(int y, int x, int n, attr_t a, Color c) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return (this->move)(y, x) == ERR ? ERR : (this->chgat)(n, a, c);
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Window_input.cpp b/src/lib/nccpp/Window_input.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..26b15ae67ce9af5c01705f9a9f21ac925ffc4f6c
--- /dev/null
+++ b/src/lib/nccpp/Window_input.cpp
@@ -0,0 +1,130 @@
+#include "lib/nccpp/Window.hpp"
+
+#include <cassert>
+#include <string>
+
+namespace nccpp {
+
+// getch
+
+inline int Window::getch() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wgetch(win_);
+}
+
+inline int Window::mvgetch(int y, int x) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return mvwgetch(win_, y, x);
+}
+
+// scanw
+
+inline int Window::scanw(char const* fmt, ...) {
+    assert(win_ && "Window doesn't manage any object");
+
+    va_list args;
+    va_start(args, fmt);
+    auto ret = vw_scanw(win_, const_cast<char*>(fmt), args);
+    va_end(args);
+
+    return ret;
+}
+
+inline int Window::mvscanw(int y, int x, char const* fmt, ...) {
+    assert(win_ && "Window doesn't manage any object");
+
+    if ((this->move)(y, x) == ERR) {
+        return ERR;
+    }
+
+    va_list args;
+    va_start(args, fmt);
+    auto ret = vw_scanw(win_, const_cast<char*>(fmt), args);
+    va_end(args);
+
+    return ret;
+}
+
+// getstr
+
+inline int Window::getstr(std::string &str) {
+    return (this->getnstr)(str, str.size());
+}
+
+inline int Window::getnstr(std::string &str, std::size_t n) {
+    assert(win_ && "Window doesn't manage any object");
+
+    str.resize(n);
+
+    return wgetnstr(win_, &str[0], static_cast<int>(n));
+}
+
+inline int Window::mvgetstr(int y, int x, std::string &str) {
+    return (this->mvgetnstr)(y, x, str, str.size());
+}
+
+inline int Window::mvgetnstr(int y, int x, std::string &str, std::size_t n) {
+    return (this->move)(y, x) == ERR ? ERR : (this->getnstr)(str, n);
+}
+
+// inch
+
+inline chtype Window::inch() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return winch(win_);
+}
+
+inline chtype Window::mvinch(int y, int x) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return mvwinch(win_, y, x);
+}
+
+// instr
+
+inline int Window::instr(std::string &str) {
+    return (this->innstr)(str, str.size());
+}
+
+inline int Window::innstr(std::string &str, std::size_t n) {
+    assert(win_ && "Window doesn't manage any object");
+
+    str.resize(n);
+
+    return winnstr(win_, &str[0], static_cast<int>(n));
+}
+
+inline int Window::mvinstr(int y, int x, std::string &str) {
+    return (this->mvinnstr)(y, x, str, str.size());
+}
+
+inline int Window::mvinnstr(int y, int x, std::string &str, std::size_t n) {
+    return (this->move)(y, x) == ERR ? ERR : (this->innstr)(str, n);
+}
+
+// inchstr
+
+inline int Window::inchstr(String &str) {
+    return (this->inchnstr)(str, str.size());
+}
+
+inline int Window::inchnstr(String &str, std::size_t n) {
+    assert(win_ && "Window doesn't manage any object");
+
+    str.resize(n);
+
+    return winchnstr(win_, &str[0], static_cast<int>(n));
+}
+
+inline int Window::mvinchstr(int y, int x, String &str) {
+    return (this->mvinchnstr)(y, x, str, str.size());
+}
+
+inline int Window::mvinchnstr(int y, int x, String &str, std::size_t n) {
+    return (this->move)(y, x) == ERR ? ERR : (this->inchnstr)(str, n);
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Window_misc.cpp b/src/lib/nccpp/Window_misc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7f017a924d25e094d12bbaf60e85460b5a72d110
--- /dev/null
+++ b/src/lib/nccpp/Window_misc.cpp
@@ -0,0 +1,111 @@
+#include "lib/nccpp/Window.hpp"
+
+#include <cassert>
+
+#include "lib/ncurses/ncurses.hpp"
+
+namespace nccpp {
+
+inline int Window::move(int y, int x) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wmove(win_, y, x);
+}
+
+inline int Window::mvwin(int y, int x) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return ::mvwin(win_, y, x);
+}
+
+inline int Window::erase() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return werase(win_);
+}
+
+inline int Window::clear() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wclear(win_);
+}
+
+inline int Window::clrtobot() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wclrtobot(win_);
+}
+
+inline int Window::clrtoeol() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wclrtoeol(win_);
+}
+
+inline int Window::refresh() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wrefresh(win_);
+}
+
+inline int Window::outrefresh() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wnoutrefresh(win_);
+}
+
+inline int Window::redraw() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return redrawwin(win_);
+}
+
+inline int Window::redrawln(int beg, int num) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wredrawln(win_, beg, num);
+}
+
+inline int Window::scroll(int n) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wscrl(win_, n);
+}
+
+inline void Window::get_yx(int &y, int &x) {
+    assert(win_ && "Window doesn't manage any object");
+
+    getyx(win_, y, x);
+}
+
+inline void Window::get_begyx(int &y, int &x) {
+    assert(win_ && "Window doesn't manage any object");
+
+    getbegyx(win_, y, x);
+}
+
+inline void Window::get_maxyx(int &y, int &x) {
+    assert(win_ && "Window doesn't manage any object");
+
+    getmaxyx(win_, y, x);
+}
+
+inline int Window::touchln(int start, int count, bool changed) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wtouchln(win_, start, count, changed);
+}
+
+inline bool Window::enclose(int y, int x) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wenclose(win_, y, x);
+}
+
+inline bool Window::coord_trafo(int &y, int &x, bool to_screen) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wmouse_trafo(win_, &y, &x, to_screen);
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Window_options.cpp b/src/lib/nccpp/Window_options.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a284df5200e0f88d39dd2aacaf4118c714b8a972
--- /dev/null
+++ b/src/lib/nccpp/Window_options.cpp
@@ -0,0 +1,43 @@
+#include "lib/nccpp/Window.hpp"
+
+#include <cassert>
+
+namespace nccpp {
+
+inline int Window::keypad(bool on) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return ::keypad(win_, on);
+}
+
+inline int Window::nodelay(bool on) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return ::nodelay(win_, on);
+}
+
+inline int Window::notimeout(bool on) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return ::notimeout(win_, on);
+}
+
+inline void Window::timeout(int delay) {
+    assert(win_ && "Window doesn't manage any object");
+
+    wtimeout(win_, delay);
+}
+
+inline int Window::clearok(bool on) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return ::clearok(win_, on);
+}
+
+inline int Window::setscrreg(int top, int bot) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wsetscrreg(win_, top, bot);
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/Window_output.cpp b/src/lib/nccpp/Window_output.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3cd5cfb1ac62442dc906a41bbb39ea3001be39c3
--- /dev/null
+++ b/src/lib/nccpp/Window_output.cpp
@@ -0,0 +1,154 @@
+#include "lib/nccpp/Window.hpp"
+
+#include <cassert>
+
+namespace nccpp {
+
+// addch
+
+inline int Window::addch(chtype const ch) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return waddch(win_, ch);
+}
+
+inline int Window::mvaddch(int y, int x, chtype const ch) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return mvwaddch(win_, y, x, ch);
+}
+
+inline int Window::echochar(chtype const ch) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wechochar(win_, ch);
+}
+
+// printw
+
+inline int Window::printw(char const* fmt, ...) {
+    assert(win_ && "Window doesn't manage any object");
+
+    va_list args;
+    va_start(args, fmt);
+    auto ret = vw_printw(win_, fmt, args);
+    va_end(args);
+
+    return ret;
+}
+
+inline int Window::mvprintw(int y, int x, char const* fmt, ...) {
+    assert(win_ && "Window doesn't manage any object");
+
+    if ((this->move)(y, x) == ERR) {
+        return ERR;
+    }
+
+    va_list args;
+    va_start(args, fmt);
+    auto ret = vw_printw(win_, fmt, args);
+    va_end(args);
+
+    return ret;
+}
+
+// addstr
+
+inline int Window::addstr(std::string const &str) {
+    return (this->addnstr)(str, str.size());
+}
+
+inline int Window::addnstr(std::string const &str, std::size_t n) {
+    assert(win_ && "Window doesn't manage any object");
+    assert(n <= str.size());
+
+    return waddnstr(win_, str.c_str(), static_cast<int>(n));
+}
+
+inline int Window::mvaddstr(int y, int x, std::string const &str) {
+    return (this->mvaddnstr)(y, x, str, str.size());
+}
+
+inline int Window::mvaddnstr(int y, int x, std::string const &str, std::size_t n) {
+    return (this->move)(y, x) == ERR ? ERR : (this->addnstr)(str, n);
+}
+
+// addchstr
+
+inline int Window::addchstr(String const &chstr) {
+    return (this->addchnstr)(chstr, chstr.size());
+}
+
+inline int Window::addchnstr(String const &chstr, std::size_t n) {
+    assert(win_ && "Window doesn't manage any object");
+    assert(n <= chstr.size());
+
+    return waddchnstr(win_, chstr.c_str(), static_cast<int>(n));
+}
+
+inline int Window::mvaddchstr(int y, int x, String const &chstr) {
+    return (this->mvaddchnstr)(y, x, chstr.c_str(), chstr.size());
+}
+
+inline int Window::mvaddchnstr(int y, int x, String const &chstr, std::size_t n) {
+    return (this->move)(y, x) ? ERR : (this->addchnstr)(chstr, n);
+}
+
+// insch
+
+inline int Window::insch(chtype ch) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return winsch(win_, ch);
+}
+
+inline int Window::mvinsch(int y, int x, chtype ch) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return mvwinsch(win_, y, x, ch);
+}
+
+// insstr
+
+inline int Window::insstr(std::string const &str) {
+    return (this->insnstr)(str, str.size());
+}
+
+inline int Window::insnstr(std::string const &str, std::size_t n) {
+    assert(win_ && "Window doesn't manage any object");
+    assert(n <= str.size());
+
+    return winsnstr(win_, str.c_str(), static_cast<int>(n));
+}
+
+inline int Window::mvinsstr(int y, int x, std::string const &str) {
+    return (this->mvinsnstr)(y, x, str, str.size());
+}
+
+inline int Window::mvinsnstr(int y, int x, std::string const &str, std::size_t n) {
+    return (this->move)(y, x) == ERR ? ERR : (this->insnstr)(str, n);
+}
+
+// delch
+
+inline int Window::delch() {
+    assert(win_ && "Window doesn't manage any object");
+
+    return wdelch(win_);
+}
+
+inline int Window::mvdelch(int y, int x) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return mvwdelch(win_, y, x);
+}
+
+// deleteln
+
+inline int Window::insdelln(int n) {
+    assert(win_ && "Window doesn't manage any object");
+
+    return winsdelln(win_, n);
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/constants.hpp b/src/lib/nccpp/constants.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..be16a346f8b91a197e1c83c27c90503119fb3d50
--- /dev/null
+++ b/src/lib/nccpp/constants.hpp
@@ -0,0 +1,261 @@
+#pragma once
+
+#include <cassert>
+
+#include "lib/nccpp/Ncurses.hpp"
+
+namespace nccpp {
+
+namespace attributes {
+
+int constexpr normal{A_NORMAL};
+int constexpr standout{A_STANDOUT};
+int constexpr underline{A_UNDERLINE};
+int constexpr reverse{A_REVERSE};
+int constexpr blink{A_BLINK};
+int constexpr dim{A_DIM};
+int constexpr bold{A_BOLD};
+int constexpr protect{A_PROTECT};
+int constexpr invis{A_INVIS};
+int constexpr altcharset{A_ALTCHARSET};
+int constexpr chartext{A_CHARTEXT};
+
+} // namespace attributes
+
+namespace internal {
+
+struct DefaultColor {
+    operator short() const {
+        static bool init_done{false};
+
+        if (!init_done) {
+            ncurses().use_default_colors();
+            init_done = true;
+        }
+
+        return -1;
+    }
+};
+
+} // namespace internal
+
+/**
+ * \brief Colors constants
+ */
+namespace colors {
+
+static internal::DefaultColor const def{};
+short constexpr black{COLOR_BLACK};
+short constexpr red{COLOR_RED};
+short constexpr green{COLOR_GREEN};
+short constexpr yellow{COLOR_YELLOW};
+short constexpr blue{COLOR_BLUE};
+short constexpr magenta{COLOR_MAGENTA};
+short constexpr cyan{COLOR_CYAN};
+short constexpr white{COLOR_WHITE};
+
+} // namespace colors
+
+namespace internal {
+
+struct FunctionKeys {
+    int operator()(int n) const {
+        assert(n < 64 && "Function key doesn't exists");
+        return KEY_F0 + n;
+    }
+};
+
+}
+
+/**
+ * \brief Keys constants
+ */
+namespace keys {
+
+int constexpr break_k{KEY_BREAK};
+int constexpr sreset{KEY_SRESET};
+int constexpr reset{KEY_RESET};
+int constexpr down{KEY_DOWN};
+int constexpr up{KEY_UP};
+int constexpr left{KEY_LEFT};
+int constexpr right{KEY_RIGHT};
+int constexpr home{KEY_HOME};
+int constexpr backspace{KEY_BACKSPACE};
+static internal::FunctionKeys const f{};
+int constexpr dl{KEY_DL};
+int constexpr il{KEY_IL};
+int constexpr dc{KEY_DC};
+int constexpr ic{KEY_IC};
+int constexpr eic{KEY_EIC};
+int constexpr clear{KEY_CLEAR};
+int constexpr eos{KEY_EOS};
+int constexpr eol{KEY_EOL};
+int constexpr sf{KEY_SF};
+int constexpr sr{KEY_SR};
+int constexpr npage{KEY_NPAGE};
+int constexpr ppage{KEY_PPAGE};
+int constexpr stab{KEY_STAB};
+int constexpr ctab{KEY_CTAB};
+int constexpr catab{KEY_CATAB};
+int constexpr enter{KEY_ENTER};
+int constexpr print{KEY_PRINT};
+int constexpr ll{KEY_LL};
+int constexpr a1{KEY_A1};
+int constexpr a3{KEY_A3};
+int constexpr b2{KEY_B2};
+int constexpr c1{KEY_C1};
+int constexpr c3{KEY_C3};
+int constexpr btab{KEY_BTAB};
+int constexpr beg{KEY_BEG};
+int constexpr cancel{KEY_CANCEL};
+int constexpr close{KEY_CLOSE};
+int constexpr command{KEY_COMMAND};
+int constexpr copy{KEY_COPY};
+int constexpr create{KEY_CREATE};
+int constexpr end{KEY_END};
+int constexpr exit{KEY_EXIT};
+int constexpr find{KEY_FIND};
+int constexpr help{KEY_HELP};
+int constexpr mark{KEY_MARK};
+int constexpr message{KEY_MESSAGE};
+int constexpr move{KEY_MOVE};
+int constexpr next{KEY_NEXT};
+int constexpr open{KEY_OPEN};
+int constexpr options{KEY_OPTIONS};
+int constexpr previous{KEY_PREVIOUS};
+int constexpr redo{KEY_REDO};
+int constexpr reference{KEY_REFERENCE};
+int constexpr refresh{KEY_REFRESH};
+int constexpr replace{KEY_REPLACE};
+int constexpr restart{KEY_RESTART};
+int constexpr resume{KEY_RESUME};
+int constexpr save{KEY_SAVE};
+int constexpr sbeg{KEY_SBEG};
+int constexpr scancel{KEY_SCANCEL};
+int constexpr scommand{KEY_SCOMMAND};
+int constexpr scopy{KEY_SCOPY};
+int constexpr screate{KEY_SCREATE};
+int constexpr sdc{KEY_SDC};
+int constexpr sdl{KEY_SDL};
+int constexpr select{KEY_SELECT};
+int constexpr send{KEY_SEND};
+int constexpr seol{KEY_SEOL};
+int constexpr sexit{KEY_SEXIT};
+int constexpr sfind{KEY_SFIND};
+int constexpr shelp{KEY_SHELP};
+int constexpr shome{KEY_SHOME};
+int constexpr sic{KEY_SIC};
+int constexpr sleft{KEY_SLEFT};
+int constexpr smessage{KEY_SMESSAGE};
+int constexpr smove{KEY_SMOVE};
+int constexpr snext{KEY_SNEXT};
+int constexpr soptions{KEY_SOPTIONS};
+int constexpr sprevious{KEY_SPREVIOUS};
+int constexpr sprint{KEY_SPRINT};
+int constexpr sredo{KEY_SREDO};
+int constexpr sreplace{KEY_SREPLACE};
+int constexpr sright{KEY_SRIGHT};
+int constexpr srsume{KEY_SRSUME};
+int constexpr ssave{KEY_SSAVE};
+int constexpr ssuspend{KEY_SSUSPEND};
+int constexpr sundo{KEY_SUNDO};
+int constexpr suspend{KEY_SUSPEND};
+int constexpr undo{KEY_UNDO};
+int constexpr mouse{KEY_MOUSE};
+int constexpr resize{KEY_RESIZE};
+int constexpr event{KEY_EVENT};
+
+} // namespace keys
+
+namespace internal {
+
+struct ButtonRelease {
+    mmask_t operator()(mmask_t event, unsigned char button) const    {
+        return BUTTON_RELEASE(event, button);
+    }
+};
+
+struct ButtonPress {
+    mmask_t operator()(mmask_t event, unsigned char button) const    {
+        return BUTTON_PRESS(event, button);
+    }
+};
+
+struct ButtonClick {
+    mmask_t operator()(mmask_t event, unsigned char button) const    {
+        return BUTTON_CLICK(event, button);
+    }
+};
+
+struct ButtonDoubleClick {
+    mmask_t operator()(mmask_t event, unsigned char button) const    {
+        return BUTTON_DOUBLE_CLICK(event, button);
+    }
+};
+
+struct ButtonTripleClick {
+    mmask_t operator()(mmask_t event, unsigned char button) const    {
+        return BUTTON_TRIPLE_CLICK(event, button);
+    }
+};
+
+struct ButtonReserved {
+    mmask_t operator()(mmask_t event, unsigned char button) const    {
+        return BUTTON_RESERVED_EVENT(event, button);
+    }
+};
+
+}
+
+namespace mouse {
+
+int constexpr button1_pressed{BUTTON1_PRESSED};
+int constexpr button1_released{BUTTON1_RELEASED};
+int constexpr button1_clicked{BUTTON1_CLICKED};
+int constexpr button1_double_clicked{BUTTON1_DOUBLE_CLICKED};
+int constexpr button1_triple_clicked{BUTTON1_TRIPLE_CLICKED};
+int constexpr button2_pressed{BUTTON2_PRESSED};
+int constexpr button2_released{BUTTON2_RELEASED};
+int constexpr button2_clicked{BUTTON2_CLICKED};
+int constexpr button2_double_clicked{BUTTON2_DOUBLE_CLICKED};
+int constexpr button2_triple_clicked{BUTTON2_TRIPLE_CLICKED};
+int constexpr button3_pressed{BUTTON3_PRESSED};
+int constexpr button3_released{BUTTON3_RELEASED};
+int constexpr button3_clicked{BUTTON3_CLICKED};
+int constexpr button3_double_clicked{BUTTON3_DOUBLE_CLICKED};
+int constexpr button3_triple_clicked{BUTTON3_TRIPLE_CLICKED};
+int constexpr button4_pressed{BUTTON4_PRESSED};
+int constexpr button4_released{BUTTON4_RELEASED};
+int constexpr button4_clicked{BUTTON4_CLICKED};
+int constexpr button4_double_clicked{BUTTON4_DOUBLE_CLICKED};
+int constexpr button4_triple_clicked{BUTTON4_TRIPLE_CLICKED};
+
+#if NCURSES_MOUSE_VERSION > 1
+int constexpr button5_pressed {BUTTON5_PRESSED};
+int constexpr button5_released {BUTTON5_RELEASED};
+int constexpr button5_clicked {BUTTON5_CLICKED};
+int constexpr button5_double_clicked {BUTTON5_DOUBLE_CLICKED};
+int constexpr button5_triple_clicked {BUTTON5_TRIPLE_CLICKED};
+#else
+int constexpr button1_reserved_event {BUTTON1_RESERVED_EVENT};
+int constexpr button2_reserved_event {BUTTON2_RESERVED_EVENT};
+int constexpr button3_reserved_event {BUTTON3_RESERVED_EVENT};
+int constexpr button4_reserved_event {BUTTON4_RESERVED_EVENT};
+#endif
+
+int constexpr button_ctrl {BUTTON_CTRL};
+int constexpr button_shift{BUTTON_SHIFT};
+int constexpr button_alt{BUTTON_ALT};
+int constexpr report_mouse_position{REPORT_MOUSE_POSITION};
+int constexpr all{ALL_MOUSE_EVENTS};
+
+static internal::ButtonRelease const release{};
+static internal::ButtonPress const press{};
+static internal::ButtonClick const click{};
+static internal::ButtonDoubleClick const double_click{};
+static internal::ButtonTripleClick const triple_click{};
+static internal::ButtonReserved const reserved_event{};
+
+}
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/errors.hpp b/src/lib/nccpp/errors.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1178429fca14c804032420901004065b759c66b6
--- /dev/null
+++ b/src/lib/nccpp/errors.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#include <exception>
+
+#include "lib/nccpp/Color.hpp"
+
+namespace nccpp {
+
+namespace errors {
+
+class Base : public std::exception {
+public:
+    Base() noexcept = default;
+
+    Base(const Base &) noexcept = default;
+    Base &operator=(const Base &) noexcept = default;
+
+    virtual ~Base() = default;
+
+    char const* what() const noexcept override {
+        return "nccpp::errors::Base";
+    }
+};
+
+class NcursesInit : public Base {
+public:
+    NcursesInit() noexcept = default;
+    NcursesInit(const NcursesInit &) noexcept = default;
+    NcursesInit &operator=(const NcursesInit &) noexcept = default;
+
+    virtual ~NcursesInit() = default;
+
+    char const* what() const noexcept override {
+        return "nccpp::errors::NcursesInit : Can't initialize ncuses, initscr() failed";
+    }
+};
+
+class WindowInit : public Base {
+public:
+    WindowInit() noexcept = default;
+    WindowInit(const WindowInit &) noexcept = default;
+    WindowInit &operator=(const WindowInit &) noexcept = default;
+
+    virtual ~WindowInit() = default;
+
+    char const* what() const noexcept override    {
+        return "nccpp::errors::WindowInit : Can't create new window, newwin() failed";
+    }
+};
+
+class ColorInit : public Base {
+public:
+    ColorInit() noexcept = default;
+    ColorInit(const ColorInit &) noexcept = default;
+    ColorInit &operator=(const ColorInit &) noexcept = default;
+
+    virtual ~ColorInit() = default;
+
+    char const* what() const noexcept override    {
+        return "nccpp::errors::ColorInit : Can't initialize colors, start_color() failed";
+    }
+};
+
+class TooMuchColors : public Base {
+public:
+    TooMuchColors(const Color &c) noexcept : color {c} {}
+
+    TooMuchColors(const TooMuchColors &) noexcept = default;
+    TooMuchColors &operator=(const TooMuchColors &) noexcept = default;
+
+    virtual ~TooMuchColors() = default;
+
+    char const* what() const noexcept override    {
+        return "nccpp::errors::TooMuchColors : Can't initialize new colors, init_pair failed";
+    }
+
+    const Color color; ///< The color that caused the error.
+};
+
+} // namespace errors
+
+} // namespace nccpp
diff --git a/src/lib/nccpp/ncursescpp.hpp b/src/lib/nccpp/ncursescpp.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bdd999c52e445a2fadf7f4e7ade8871fd0bd0008
--- /dev/null
+++ b/src/lib/nccpp/ncursescpp.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "lib/nccpp/Ncurses.hpp"
+#include "lib/nccpp/Window.hpp"
+#include "lib/nccpp/Subwindow.hpp"
+#include "lib/nccpp/Color.hpp"
+#include "lib/nccpp/constants.hpp"
+#include "lib/nccpp/errors.hpp"