Skip to content

Commit

Permalink
Rocket: refactor, add unit tests
Browse files Browse the repository at this point in the history
Fixes #36
  • Loading branch information
timower committed May 31, 2024
1 parent fc4303e commit ce5f658
Show file tree
Hide file tree
Showing 23 changed files with 1,014 additions and 679 deletions.
132 changes: 103 additions & 29 deletions apps/rocket/App.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#include "App.h"

#include <unistdpp/pipe.h>

#include <Device.h>

#include <algorithm>
#include <csignal>
#include <fstream>
#include <iostream>

#include <sys/wait.h>
#include <unistd.h>

using namespace rmlib;
Expand All @@ -15,6 +19,7 @@ namespace {
pid_t
runCommand(std::string_view cmd) {
pid_t pid = fork();

if (pid == -1) {
perror("Error launching");
return -1;
Expand All @@ -25,21 +30,28 @@ runCommand(std::string_view cmd) {
return pid;
}

std::cout << "Running: " << cmd << std::endl;
setpgid(0, 0);

std::cout << "Running: " << cmd << std::endl;
execlp("/bin/sh", "/bin/sh", "-c", cmd.data(), nullptr);
perror("Error running process");
return -1;
}

bool
endsWith(std::string_view a, std::string_view end) {
if (a.size() < end.size()) {
return false;
void
stop(std::shared_ptr<AppRunInfo> runInfo) {
const auto pid = runInfo->pid;

if (runInfo->paused) {
kill(-pid, SIGCONT);
}

return a.substr(a.size() - end.size()) == end;
int res = kill(-pid, SIGTERM);
if (res != 0) {
perror("Error killing!");
}
}

} // namespace

std::optional<AppDescription>
Expand Down Expand Up @@ -76,17 +88,22 @@ AppDescription::read(std::string_view path, std::string_view iconDir) {
}

if (!result.icon.empty()) {
auto iconPath = std::string(iconDir) + '/' + result.icon + ".png";
std::cout << "Parsing image from: " << iconPath << std::endl;
result.iconImage = ImageCanvas::load(iconPath.c_str());
if (result.iconImage.has_value()) {
std::cout << result.iconImage->canvas.components() << std::endl;
}
result.iconPath = std::string(iconDir) + '/' + result.icon + ".png";
}

return std::optional(std::move(result));
}

std::optional<ImageCanvas>
AppDescription::getIcon() const {
std::cout << "Parsing image from: " << iconPath << std::endl;
auto iconImage = ImageCanvas::load(iconPath.c_str());
if (iconImage.has_value()) {
std::cout << iconImage->canvas.components() << std::endl;
}
return iconImage;
}

std::vector<AppDescription>
readAppFiles(std::string_view directory) {
const auto iconPath = std::string(directory) + "/icons";
Expand All @@ -95,11 +112,6 @@ readAppFiles(std::string_view directory) {
std::vector<AppDescription> result;

for (const auto& path : paths) {
if (!endsWith(path, ".draft")) {
std::cerr << "skipping non draft file: " << path << std::endl;
continue;
}

auto appDesc = AppDescription::read(path, iconPath);
if (!appDesc.has_value()) {
std::cerr << "error parsing file: " << path << std::endl;
Expand All @@ -112,19 +124,30 @@ readAppFiles(std::string_view directory) {
return result;
}

void
App::updateDescription(AppDescription desc) {
mDescription = std::move(desc);
iconImage = mDescription.getIcon();
}

bool
App::launch() {
if (runInfo.has_value()) {
if (isRunning()) {
assert(false && "Shouldn't be called if the app is already running");
return false;
}

auto pid = runCommand(description.command);
auto pid = runCommand(description().command);
if (pid == -1) {
return false;
}

runInfo = AppRunInfo{ pid };
auto runInfo = std::make_shared<AppRunInfo>();
runInfo->pid = pid;

this->runInfo = runInfo;

AppManager::getInstance().runInfos.emplace_back(std::move(runInfo));

return true;
}
Expand All @@ -133,19 +156,17 @@ void
App::stop() {
assert(isRunning());

if (isPaused()) {
kill(-runInfo->pid, SIGCONT);
}

kill(-runInfo->pid, SIGTERM);
::stop(runInfo.lock());
}

void
App::pause(std::optional<MemoryCanvas> screen) {
assert(isRunning() && !isPaused());

kill(-runInfo->pid, SIGSTOP);
runInfo->paused = true;
auto lockedInfo = runInfo.lock();

kill(-lockedInfo->pid, SIGSTOP);
lockedInfo->paused = true;
savedFb = std::move(screen);
}

Expand All @@ -160,6 +181,59 @@ App::resume(rmlib::fb::FrameBuffer* fb) {
savedFb.reset();
}

kill(-runInfo->pid, SIGCONT);
runInfo->paused = false;
auto lockedInfo = runInfo.lock();
kill(-lockedInfo->pid, SIGCONT);
lockedInfo->paused = false;
}

AppManager&
AppManager::getInstance() {
static AppManager instance;
return instance;
}

bool
AppManager::update() {
bool anyKilled = false;

while (auto res = pipe.readPipe.readAll<pid_t>()) {
auto pid = *res;
anyKilled = true;

runInfos.erase(
std::remove_if(runInfos.begin(),
runInfos.end(),
[pid](auto& info) { return info->pid == pid; }),
runInfos.end());
}

return anyKilled;
}

void
AppManager::onSigChild(int sig) {
auto& inst = AppManager::getInstance();

pid_t childPid = 0;
while ((childPid = waitpid(static_cast<pid_t>(-1), nullptr, WNOHANG)) > 0) {

std::cout << "Killed: " << childPid << "\n";

auto v = inst.pipe.writePipe.writeAll(childPid);

if (!v.has_value()) {
std::cerr << "Error in writing pid: " << to_string(v.error()) << "\n";
}
}
}

AppManager::AppManager() : pipe(unistdpp::fatalOnError(unistdpp::pipe())) {
unistdpp::setNonBlocking(pipe.readPipe);
std::signal(SIGCHLD, onSigChild);
}

AppManager::~AppManager() {
for (auto runInfo : runInfos) {
::stop(runInfo);
}
}
71 changes: 51 additions & 20 deletions apps/rocket/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
#include <Canvas.h>
#include <FrameBuffer.h>

#include <unistdpp/pipe.h>

#include <chrono>
#include <optional>
#include <string>
#include <vector>

struct AppRunInfo {
pid_t pid;
pid_t pid = -1;
bool paused = false;

// Indicates that the app should be removed when it exists
bool shouldRemove = false;
};

struct AppDescription {
Expand All @@ -25,7 +24,8 @@ struct AppDescription {
std::string command;
std::string icon;

std::optional<rmlib::ImageCanvas> iconImage;
std::string iconPath;
std::optional<rmlib::ImageCanvas> getIcon() const;

static std::optional<AppDescription> read(std::string_view path,
std::string_view iconDir);
Expand All @@ -34,23 +34,15 @@ struct AppDescription {
std::vector<AppDescription>
readAppFiles(std::string_view directory);

struct App {
AppDescription description;

std::optional<AppRunInfo> runInfo = std::nullopt;

std::chrono::steady_clock::time_point lastActivated;
class App {
public:
App(AppDescription desc)
: mDescription(std::move(desc)), iconImage(mDescription.getIcon()) {}

std::optional<rmlib::MemoryCanvas> savedFb;

// Used for UI:
rmlib::Rect launchRect;
rmlib::Rect killRect;
void updateDescription(AppDescription desc);

App(AppDescription desc) : description(std::move(desc)) {}

bool isRunning() const { return runInfo.has_value(); }
bool isPaused() const { return isRunning() && runInfo->paused; }
bool isRunning() const { return !runInfo.expired(); }
bool isPaused() const { return isRunning() && runInfo.lock()->paused; }

/// Starts a new instance of the app if it's not already running.
/// \returns True if a new instance was started.
Expand All @@ -60,4 +52,43 @@ struct App {

void pause(std::optional<rmlib::MemoryCanvas> screen = std::nullopt);
void resume(rmlib::fb::FrameBuffer* fb = nullptr);

const AppDescription& description() const { return mDescription; }

const std::optional<rmlib::ImageCanvas>& icon() const { return iconImage; }
const std::optional<rmlib::MemoryCanvas>& savedFB() const { return savedFb; }
void resetSavedFB() { savedFb.reset(); }

void setRemoveOnExit() { shouldRemove = true; }
bool shouldRemoveOnExit() const { return shouldRemove; }

private:
AppDescription mDescription;

std::weak_ptr<AppRunInfo> runInfo;

std::optional<rmlib::ImageCanvas> iconImage;
std::optional<rmlib::MemoryCanvas> savedFb;

// Indicates that the app should be removed when it exists
bool shouldRemove = false;
};

class AppManager {
public:
static AppManager& getInstance();

bool update();
const unistdpp::FD& getWaitFD() const { return pipe.readPipe; }

private:
friend class App;
unistdpp::Pipe pipe;

std::vector<std::shared_ptr<AppRunInfo>> runInfos;

static void onSigChild(int sig);

AppManager();
~AppManager();
};
13 changes: 13 additions & 0 deletions apps/rocket/AppWidgets.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "AppWidgets.h"

using namespace rmlib;

const MemoryCanvas&
getMissingImage() {
static auto image = [] {
auto mem = MemoryCanvas(128, 128, 2);
mem.canvas.set(greyToRGB565(0xaa));
return mem;
}();
return image;
}

0 comments on commit ce5f658

Please sign in to comment.