#pragma once #include #include #include #include "app.h" #include "dialog_model.h" #include "line_editor.h" #include "list_model.h" #include "services/ssh_service.h" #include "services/storage_service.h" #include "settings.h" #include "ui/canvas.h" namespace roro { // The SSH App (issue #2, docs/milestones/N1.md): the saved hosts, and a terminal on the one that // is connected. Leaving it with Home doesn't end the session; coming back finds it. class SshApp : public App { public: static constexpr const char* kPublicKeyPath = "/ssh/id_ed25519.pub"; SshApp(SshService& ssh, Settings& settings, StorageService& storage) : ssh_(ssh), settings_(settings), storage_(storage) {} void onEnter() override; bool onKey(const KeyEvent& e) override; bool textEntryActive() const override { return view_ == View::Entry || (view_ == View::Session && !dialog_); } void help(std::vector& out) const override; const char* helpTitle() const override; void update(uint32_t nowMs) override; void draw(Canvas& c) override; // `ssh user@host` from the Shell or a console: "" or why not. std::string connectTo(const std::string& target); private: enum class View { Hosts, Entry, Session, Key }; struct Font { const lgfx::IFont* font; int w, h; }; static const Font kFonts[5]; void grid(int& cols, int& rows) const; void connect(const term::SshTarget& target); void setFont(int index); bool sessionKey(const KeyEvent& e); void drawHosts(Canvas& c); void drawSession(Canvas& c); void drawTerminal(Canvas& c); void drawKey(Canvas& c); void say(const std::string& text); void writePublicKey(); SshService& ssh_; Settings& settings_; StorageService& storage_; View view_ = View::Hosts; ListModel list_{8}; std::vector hosts_; LineEditor entry_{96}, password_{96}; std::unique_ptr dialog_; enum class Ask { None, Trust, NewKey, Forget } ask_ = Ask::None; SshService::State shownState_ = SshService::State::Idle; std::string message_; uint32_t messageMs_ = 0, shownRevision_ = 0, lastDrawMs_ = 0; int font_ = 1, scroll_ = 0; }; } // namespace roro