<commit_before>#include "pbview.h"

#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <ncurses.h>
#include <iostream>
#include <sstream>

#include "config.h"
#include "configcontainer.h"
#include "dllist.h"
#include "download.h"
#include "fmtstrformatter.h"
#include "help.h"
#include "listformatter.h"
#include "logger.h"
#include "pbcontroller.h"
#include "strprintf.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

PbView::PbView(PbController* c)
	: update_view(true)
	, ctrl(c)
	, dllist_form(dllist_str)
	, help_form(help_str)
	, keys(0)
	, colorman(ctrl->get_colormanager())
	, downloads_list("dls", dllist_form,
		  ctrl->get_cfgcont()->get_configvalue_as_int("scrolloff"))
	, help_textview("helptext", help_form)
{
	if (getenv("ESCDELAY") == nullptr) {
		set_escdelay(25);
	}
}

PbView::~PbView()
{
	Stfl::reset();
}

void PbView::run(bool auto_download, bool wrap_scroll)
{
	bool quit = false;

	// Make sure curses is initialized
	dllist_form.run(-3);
	// Hide cursor using curses
	curs_set(0);

	set_dllist_keymap_hint();

	do {
		if (update_view) {
			const double total_kbps = ctrl->get_total_kbps();
			const auto speed = get_speed_human_readable(total_kbps);

			char parbuf[128] = "";
			if (ctrl->get_maxdownloads() > 1) {
				snprintf(parbuf,
					sizeof(parbuf),
					_(" - %u parallel downloads"),
					ctrl->get_maxdownloads());
			}

			char buf[1024];
			snprintf(buf,
				sizeof(buf),
				_("Queue (%u downloads in progress, %u total) "
					"- %.2f %s total%s"),
				static_cast<unsigned int>(
					ctrl->downloads_in_progress()),
				static_cast<unsigned int>(
					ctrl->downloads().size()),
				speed.first,
				speed.second.c_str(),
				parbuf);

			dllist_form.set("head", buf);

			LOG(Level::DEBUG,
				"PbView::run: updating view... "
				"downloads().size() "
				"= %" PRIu64,
				static_cast<uint64_t>(ctrl->downloads().size()));

			ListFormatter listfmt;
			const std::string line_format =
				ctrl->get_cfgcont()->get_configvalue("podlist-format");

			dllist_form.run(-3); // compute all widget dimensions
			const unsigned int width = downloads_list.get_width();

			unsigned int i = 0;
			for (const auto& dl : ctrl->downloads()) {
				auto lbuf = format_line(line_format, dl, i, width);
				listfmt.add_line(lbuf);
				i++;
			}

			downloads_list.stfl_replace_lines(listfmt);

			// If there's no status message, we know there's no error to show
			// Thus, it's safe to replace with the download's status
			if (i >= 1 && dllist_form.get("msg").empty()) {
				const auto idx = downloads_list.get_position();
				dllist_form.set("msg", ctrl->downloads()[idx].status_msg());
			}

			update_view = false;
		}

		const char* event = dllist_form.run(500);

		if (auto_download) {
			if (ctrl->get_maxdownloads() >
				ctrl->downloads_in_progress()) {
				ctrl->start_downloads();
			}
		}

		if (!event || strcmp(event, "TIMEOUT") == 0) {
			continue;
		}

		if (strcmp(event, "RESIZE") == 0) {
			handle_resize();
			continue;
		}

		Operation op = keys->get_operation(event, "podboat");

		if (dllist_form.get("msg").length() > 0) {
			dllist_form.set("msg", "");
			update_view = true;
		}

		switch (op) {
		case OP_REDRAW:
			Stfl::reset();
			break;
		case OP_PREV:
		case OP_SK_UP:
			downloads_list.move_up(wrap_scroll);
			update_view = true;
			break;
		case OP_NEXT:
		case OP_SK_DOWN:
			downloads_list.move_down(wrap_scroll);
			update_view = true;
			break;
		case OP_SK_HOME:
			downloads_list.move_to_first();
			update_view = true;
			break;
		case OP_SK_END:
			downloads_list.move_to_last();
			update_view = true;
			break;
		case OP_SK_PGUP:
			downloads_list.move_page_up(wrap_scroll);
			update_view = true;
			break;
		case OP_SK_PGDOWN:
			downloads_list.move_page_down(wrap_scroll);
			update_view = true;
			break;
		case OP_PB_TOGGLE_DLALL:
			auto_download = !auto_download;
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg", _("Error: can't quit: download(s) in progress."));
				update_view = true;
			} else {
				quit = true;
			}
			break;
		case OP_PB_MOREDL:
			ctrl->increase_parallel_downloads();
			break;
		case OP_PB_LESSDL:
			ctrl->decrease_parallel_downloads();
			break;
		case OP_PB_DOWNLOAD: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				auto& item = ctrl->downloads()[idx];
				if (item.status() != DlStatus::DOWNLOADING) {
					ctrl->start_download(item);
				}
			}
		}
		break;
		case OP_PB_PLAY: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::FINISHED ||
					status == DlStatus::PLAYED ||
					status == DlStatus::READY) {
					ctrl->play_file(ctrl->downloads()[idx]
						.filename());
					ctrl->downloads()[idx].set_status(
						DlStatus::PLAYED);
				} else {
					dllist_form.set("msg",
						_("Error: download needs to be "
							"finished before the file "
							"can be played."));
				}
			}
		}
		break;
		case OP_PB_MARK_FINISHED: {
			auto& downloads = ctrl->downloads();
			if (downloads.size() >= 1) {
				const auto idx = downloads_list.get_position();
				DlStatus status =
					downloads[idx].status();
				if (status == DlStatus::PLAYED) {
					downloads[idx].set_status(DlStatus::FINISHED);
					if (idx + 1 < downloads.size()) {
						downloads_list.set_position(idx + 1);
					}
				}
			}
		}
		break;
		case OP_PB_CANCEL: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (ctrl->downloads()[idx].status() ==
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::CANCELLED);
				}
			}
		}
		break;
		case OP_PB_DELETE: {
			auto& downloads = ctrl->downloads();
			if (downloads.size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (downloads[idx].status() !=
					DlStatus::DOWNLOADING) {
					downloads[idx].set_status(DlStatus::DELETED);
					if (idx + 1 < downloads.size()) {
						downloads_list.set_position(idx + 1);
					}
				}
			}
		}
		break;
		case OP_PB_PURGE:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: unable to perform operation: "
						"download(s) in progress."));
			} else {
				ctrl->purge_queue();
			}
			update_view = true;
			break;
		case OP_HELP:
			run_help();
			break;
		default:
			break;
		}

	} while (!quit);
}

void PbView::handle_resize()
{
	std::vector<std::reference_wrapper<newsboat::Stfl::Form>> forms = {dllist_form, help_form};
	for (const auto& form : forms) {
		form.get().run(-3);
	}
	update_view = true;
}

void PbView::apply_colors_to_all_forms()
{
	using namespace std::placeholders;
	colorman.apply_colors(std::bind(&newsboat::Stfl::Form::set, &dllist_form, _1,
			_2));
	colorman.apply_colors(std::bind(&newsboat::Stfl::Form::set, &help_form, _1,
			_2));
}

std::pair<double, std::string> PbView::get_speed_human_readable(double kbps)
{
	if (kbps < 1024) {
		return std::make_pair(kbps, _("KB/s"));
	} else if (kbps < 1024 * 1024) {
		return std::make_pair(kbps / 1024, _("MB/s"));
	} else {
		return std::make_pair(kbps / 1024 / 1024, _("GB/s"));
	}
}

void PbView::run_help()
{
	set_help_keymap_hint();

	help_form.set("head", _("Help"));

	const auto descs = keys->get_keymap_descriptions("podboat");

	ListFormatter listfmt;

	for (const auto& desc : descs) {
		const std::string descline = strprintf::fmt("%-7s %-23s %s",
				desc.key,
				desc.cmd,
				desc.desc);

		listfmt.add_line(descline);
	}

	help_textview.stfl_replace_lines(listfmt.get_lines_count(),
		listfmt.format_list());

	bool quit = false;

	do {
		const char* event = help_form.run(0);
		if (!event) {
			continue;
		}

		if (strcmp(event, "RESIZE") == 0) {
			handle_resize();
			continue;
		}

		Operation op = keys->get_operation(event, "help");

		switch (op) {
		case OP_SK_UP:
			help_textview.scroll_up();
			break;
		case OP_SK_DOWN:
			help_textview.scroll_down();
			break;
		case OP_SK_HOME:
			help_textview.scroll_to_top();
			break;
		case OP_SK_END:
			help_textview.scroll_to_bottom();
			break;
		case OP_SK_PGUP:
			help_textview.scroll_page_up();
			break;
		case OP_SK_PGDOWN:
			help_textview.scroll_page_down();
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			quit = true;
			break;
		default:
			break;
		}
	} while (!quit);
}

std::string PbView::prepare_keymaphint(KeyMapHintEntry* hints)
{
	std::string keymap_hint;
	for (int i = 0; hints[i].op != OP_NIL; ++i) {
		std::string bound_keys = utils::join(keys->get_keys(hints[i].op, "podboat"),
				",");
		if (bound_keys.empty()) {
			bound_keys = "<none>";
		}
		keymap_hint.append(bound_keys);
		keymap_hint.append(":");
		keymap_hint.append(hints[i].text);
		keymap_hint.append(" ");
	}
	return keymap_hint;
}

void PbView::set_help_keymap_hint()
{
	static KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")}, {OP_NIL, nullptr}};
	std::string keymap_hint = prepare_keymaphint(hints);
	help_form.set("help", keymap_hint);
}

void PbView::set_dllist_keymap_hint()
{
	static KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")},
		{OP_PB_DOWNLOAD, _("Download")},
		{OP_PB_CANCEL, _("Cancel")},
		{OP_PB_DELETE, _("Delete")},
		{OP_PB_PURGE, _("Purge Finished")},
		{OP_PB_TOGGLE_DLALL, _("Toggle Automatic Download")},
		{OP_PB_PLAY, _("Play")},
		{OP_PB_MARK_FINISHED, _("Mark as Finished")},
		{OP_HELP, _("Help")},
		{OP_NIL, nullptr}
	};

	std::string keymap_hint = prepare_keymaphint(hints);
	dllist_form.set("help", keymap_hint);
}

std::string PbView::format_line(const std::string& podlist_format,
	const Download& dl,
	unsigned int pos,
	unsigned int width)
{
	FmtStrFormatter fmt;

	const double speed_kbps = dl.kbps();
	const auto speed = get_speed_human_readable(speed_kbps);

	fmt.register_fmt('i', strprintf::fmt("%u", pos + 1));
	fmt.register_fmt('d',
		strprintf::fmt("%.1f", dl.current_size() / (1024 * 1024)));
	fmt.register_fmt(
		't', strprintf::fmt("%.1f", dl.total_size() / (1024 * 1024)));
	fmt.register_fmt('p', strprintf::fmt("%.1f", dl.percents_finished()));
	fmt.register_fmt('k', strprintf::fmt("%.2f", speed_kbps));
	fmt.register_fmt('K', strprintf::fmt("%.2f %s", speed.first, speed.second));
	fmt.register_fmt('S', strprintf::fmt("%s", dl.status_text()));
	fmt.register_fmt('u', strprintf::fmt("%s", dl.url()));
	fmt.register_fmt('F', strprintf::fmt("%s", dl.filename()));
	fmt.register_fmt('b', strprintf::fmt("%s", dl.basename()));

	auto formattedLine = fmt.do_format(podlist_format, width);
	formattedLine = utils::quote_for_stfl(formattedLine);
	return formattedLine;
}

} // namespace podboat
<commit_msg>Podboat title: Use strprintf::fmt()<commit_after>#include "pbview.h"

#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <ncurses.h>
#include <iostream>
#include <sstream>

#include "config.h"
#include "configcontainer.h"
#include "dllist.h"
#include "download.h"
#include "fmtstrformatter.h"
#include "help.h"
#include "listformatter.h"
#include "logger.h"
#include "pbcontroller.h"
#include "strprintf.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

PbView::PbView(PbController* c)
	: update_view(true)
	, ctrl(c)
	, dllist_form(dllist_str)
	, help_form(help_str)
	, keys(0)
	, colorman(ctrl->get_colormanager())
	, downloads_list("dls", dllist_form,
		  ctrl->get_cfgcont()->get_configvalue_as_int("scrolloff"))
	, help_textview("helptext", help_form)
{
	if (getenv("ESCDELAY") == nullptr) {
		set_escdelay(25);
	}
}

PbView::~PbView()
{
	Stfl::reset();
}

void PbView::run(bool auto_download, bool wrap_scroll)
{
	bool quit = false;

	// Make sure curses is initialized
	dllist_form.run(-3);
	// Hide cursor using curses
	curs_set(0);

	set_dllist_keymap_hint();

	do {
		if (update_view) {
			const double total_kbps = ctrl->get_total_kbps();
			const auto speed = get_speed_human_readable(total_kbps);

			char parbuf[128] = "";
			if (ctrl->get_maxdownloads() > 1) {
				snprintf(parbuf,
					sizeof(parbuf),
					_(" - %u parallel downloads"),
					ctrl->get_maxdownloads());
			}

			const auto title = strprintf::fmt(
					_("Queue (%u downloads in progress, %u total) - %.2f %s total%s"),
					static_cast<unsigned int>(ctrl->downloads_in_progress()),
					static_cast<unsigned int>(ctrl->downloads().size()),
					speed.first,
					speed.second,
					parbuf);

			dllist_form.set("head", title);

			LOG(Level::DEBUG,
				"PbView::run: updating view... "
				"downloads().size() "
				"= %" PRIu64,
				static_cast<uint64_t>(ctrl->downloads().size()));

			ListFormatter listfmt;
			const std::string line_format =
				ctrl->get_cfgcont()->get_configvalue("podlist-format");

			dllist_form.run(-3); // compute all widget dimensions
			const unsigned int width = downloads_list.get_width();

			unsigned int i = 0;
			for (const auto& dl : ctrl->downloads()) {
				auto lbuf = format_line(line_format, dl, i, width);
				listfmt.add_line(lbuf);
				i++;
			}

			downloads_list.stfl_replace_lines(listfmt);

			// If there's no status message, we know there's no error to show
			// Thus, it's safe to replace with the download's status
			if (i >= 1 && dllist_form.get("msg").empty()) {
				const auto idx = downloads_list.get_position();
				dllist_form.set("msg", ctrl->downloads()[idx].status_msg());
			}

			update_view = false;
		}

		const char* event = dllist_form.run(500);

		if (auto_download) {
			if (ctrl->get_maxdownloads() >
				ctrl->downloads_in_progress()) {
				ctrl->start_downloads();
			}
		}

		if (!event || strcmp(event, "TIMEOUT") == 0) {
			continue;
		}

		if (strcmp(event, "RESIZE") == 0) {
			handle_resize();
			continue;
		}

		Operation op = keys->get_operation(event, "podboat");

		if (dllist_form.get("msg").length() > 0) {
			dllist_form.set("msg", "");
			update_view = true;
		}

		switch (op) {
		case OP_REDRAW:
			Stfl::reset();
			break;
		case OP_PREV:
		case OP_SK_UP:
			downloads_list.move_up(wrap_scroll);
			update_view = true;
			break;
		case OP_NEXT:
		case OP_SK_DOWN:
			downloads_list.move_down(wrap_scroll);
			update_view = true;
			break;
		case OP_SK_HOME:
			downloads_list.move_to_first();
			update_view = true;
			break;
		case OP_SK_END:
			downloads_list.move_to_last();
			update_view = true;
			break;
		case OP_SK_PGUP:
			downloads_list.move_page_up(wrap_scroll);
			update_view = true;
			break;
		case OP_SK_PGDOWN:
			downloads_list.move_page_down(wrap_scroll);
			update_view = true;
			break;
		case OP_PB_TOGGLE_DLALL:
			auto_download = !auto_download;
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg", _("Error: can't quit: download(s) in progress."));
				update_view = true;
			} else {
				quit = true;
			}
			break;
		case OP_PB_MOREDL:
			ctrl->increase_parallel_downloads();
			break;
		case OP_PB_LESSDL:
			ctrl->decrease_parallel_downloads();
			break;
		case OP_PB_DOWNLOAD: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				auto& item = ctrl->downloads()[idx];
				if (item.status() != DlStatus::DOWNLOADING) {
					ctrl->start_download(item);
				}
			}
		}
		break;
		case OP_PB_PLAY: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::FINISHED ||
					status == DlStatus::PLAYED ||
					status == DlStatus::READY) {
					ctrl->play_file(ctrl->downloads()[idx]
						.filename());
					ctrl->downloads()[idx].set_status(
						DlStatus::PLAYED);
				} else {
					dllist_form.set("msg",
						_("Error: download needs to be "
							"finished before the file "
							"can be played."));
				}
			}
		}
		break;
		case OP_PB_MARK_FINISHED: {
			auto& downloads = ctrl->downloads();
			if (downloads.size() >= 1) {
				const auto idx = downloads_list.get_position();
				DlStatus status =
					downloads[idx].status();
				if (status == DlStatus::PLAYED) {
					downloads[idx].set_status(DlStatus::FINISHED);
					if (idx + 1 < downloads.size()) {
						downloads_list.set_position(idx + 1);
					}
				}
			}
		}
		break;
		case OP_PB_CANCEL: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (ctrl->downloads()[idx].status() ==
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::CANCELLED);
				}
			}
		}
		break;
		case OP_PB_DELETE: {
			auto& downloads = ctrl->downloads();
			if (downloads.size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (downloads[idx].status() !=
					DlStatus::DOWNLOADING) {
					downloads[idx].set_status(DlStatus::DELETED);
					if (idx + 1 < downloads.size()) {
						downloads_list.set_position(idx + 1);
					}
				}
			}
		}
		break;
		case OP_PB_PURGE:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: unable to perform operation: "
						"download(s) in progress."));
			} else {
				ctrl->purge_queue();
			}
			update_view = true;
			break;
		case OP_HELP:
			run_help();
			break;
		default:
			break;
		}

	} while (!quit);
}

void PbView::handle_resize()
{
	std::vector<std::reference_wrapper<newsboat::Stfl::Form>> forms = {dllist_form, help_form};
	for (const auto& form : forms) {
		form.get().run(-3);
	}
	update_view = true;
}

void PbView::apply_colors_to_all_forms()
{
	using namespace std::placeholders;
	colorman.apply_colors(std::bind(&newsboat::Stfl::Form::set, &dllist_form, _1,
			_2));
	colorman.apply_colors(std::bind(&newsboat::Stfl::Form::set, &help_form, _1,
			_2));
}

std::pair<double, std::string> PbView::get_speed_human_readable(double kbps)
{
	if (kbps < 1024) {
		return std::make_pair(kbps, _("KB/s"));
	} else if (kbps < 1024 * 1024) {
		return std::make_pair(kbps / 1024, _("MB/s"));
	} else {
		return std::make_pair(kbps / 1024 / 1024, _("GB/s"));
	}
}

void PbView::run_help()
{
	set_help_keymap_hint();

	help_form.set("head", _("Help"));

	const auto descs = keys->get_keymap_descriptions("podboat");

	ListFormatter listfmt;

	for (const auto& desc : descs) {
		const std::string descline = strprintf::fmt("%-7s %-23s %s",
				desc.key,
				desc.cmd,
				desc.desc);

		listfmt.add_line(descline);
	}

	help_textview.stfl_replace_lines(listfmt.get_lines_count(),
		listfmt.format_list());

	bool quit = false;

	do {
		const char* event = help_form.run(0);
		if (!event) {
			continue;
		}

		if (strcmp(event, "RESIZE") == 0) {
			handle_resize();
			continue;
		}

		Operation op = keys->get_operation(event, "help");

		switch (op) {
		case OP_SK_UP:
			help_textview.scroll_up();
			break;
		case OP_SK_DOWN:
			help_textview.scroll_down();
			break;
		case OP_SK_HOME:
			help_textview.scroll_to_top();
			break;
		case OP_SK_END:
			help_textview.scroll_to_bottom();
			break;
		case OP_SK_PGUP:
			help_textview.scroll_page_up();
			break;
		case OP_SK_PGDOWN:
			help_textview.scroll_page_down();
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			quit = true;
			break;
		default:
			break;
		}
	} while (!quit);
}

std::string PbView::prepare_keymaphint(KeyMapHintEntry* hints)
{
	std::string keymap_hint;
	for (int i = 0; hints[i].op != OP_NIL; ++i) {
		std::string bound_keys = utils::join(keys->get_keys(hints[i].op, "podboat"),
				",");
		if (bound_keys.empty()) {
			bound_keys = "<none>";
		}
		keymap_hint.append(bound_keys);
		keymap_hint.append(":");
		keymap_hint.append(hints[i].text);
		keymap_hint.append(" ");
	}
	return keymap_hint;
}

void PbView::set_help_keymap_hint()
{
	static KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")}, {OP_NIL, nullptr}};
	std::string keymap_hint = prepare_keymaphint(hints);
	help_form.set("help", keymap_hint);
}

void PbView::set_dllist_keymap_hint()
{
	static KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")},
		{OP_PB_DOWNLOAD, _("Download")},
		{OP_PB_CANCEL, _("Cancel")},
		{OP_PB_DELETE, _("Delete")},
		{OP_PB_PURGE, _("Purge Finished")},
		{OP_PB_TOGGLE_DLALL, _("Toggle Automatic Download")},
		{OP_PB_PLAY, _("Play")},
		{OP_PB_MARK_FINISHED, _("Mark as Finished")},
		{OP_HELP, _("Help")},
		{OP_NIL, nullptr}
	};

	std::string keymap_hint = prepare_keymaphint(hints);
	dllist_form.set("help", keymap_hint);
}

std::string PbView::format_line(const std::string& podlist_format,
	const Download& dl,
	unsigned int pos,
	unsigned int width)
{
	FmtStrFormatter fmt;

	const double speed_kbps = dl.kbps();
	const auto speed = get_speed_human_readable(speed_kbps);

	fmt.register_fmt('i', strprintf::fmt("%u", pos + 1));
	fmt.register_fmt('d',
		strprintf::fmt("%.1f", dl.current_size() / (1024 * 1024)));
	fmt.register_fmt(
		't', strprintf::fmt("%.1f", dl.total_size() / (1024 * 1024)));
	fmt.register_fmt('p', strprintf::fmt("%.1f", dl.percents_finished()));
	fmt.register_fmt('k', strprintf::fmt("%.2f", speed_kbps));
	fmt.register_fmt('K', strprintf::fmt("%.2f %s", speed.first, speed.second));
	fmt.register_fmt('S', strprintf::fmt("%s", dl.status_text()));
	fmt.register_fmt('u', strprintf::fmt("%s", dl.url()));
	fmt.register_fmt('F', strprintf::fmt("%s", dl.filename()));
	fmt.register_fmt('b', strprintf::fmt("%s", dl.basename()));

	auto formattedLine = fmt.do_format(podlist_format, width);
	formattedLine = utils::quote_for_stfl(formattedLine);
	return formattedLine;
}

} // namespace podboat
<|endoftext|>


<commit_before>#include "MeterWnd.h"
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")

#include "Animation.h"

#define TIMER_HIDE 100
#define TIMER_ANIMIN 101
#define TIMER_ANIMOUT 102

MeterWnd::MeterWnd(LPCWSTR className, LPCWSTR title, HINSTANCE hInstance,
    Animation *hideAnimation, Animation *showAnimation, int visibleDuration) :
_hInstance(hInstance),
_className(className),
_title(title),
_hideAnimation(hideAnimation),
_showAnimation(showAnimation),
_visibleDuration(visibleDuration),
_visible(false) {
    if (_hInstance == NULL) {
        _hInstance = (HINSTANCE) GetModuleHandle(NULL);
    }

    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = 0;
    wcex.lpfnWndProc = &MeterWnd::StaticWndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = _hInstance;
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = _className;
    wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wcex)) {
        /* throw exception */
    }

    _location.x = 0;
    _location.y = 0;
    _size.cx = 1;
    _size.cy = 1;
    _transparency = 255;

    _hWnd = CreateWindowEx(
        WS_EX_TOOLWINDOW |
        WS_EX_LAYERED |
        WS_EX_NOACTIVATE |
        WS_EX_TOPMOST |
        WS_EX_TRANSPARENT,
        _className, _title,
        WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,
        _size.cx, _size.cy,
        NULL, NULL, _hInstance, this);

    if (_hWnd == NULL) {
        /* throw exception */
    }
}

MeterWnd::~MeterWnd() {
    DestroyWindow(_hWnd);
}

void MeterWnd::Update()
{
    CLOG(L"Updating meter window");
    using namespace Gdiplus;

    bool dirty = (_composite == NULL);

    for (Meter *meter : _meters) {
        if (meter->Dirty() == true) {
            dirty = true;
            break;
        }
    }

    if (dirty) {
        CLOG(L"Contents have changed; redrawing");

        if (_composite) {
            delete _composite;
        }

        Rect bgRect(0, 0, _background->GetWidth(), _background->GetHeight());
        _composite = _background->Clone(bgRect, PixelFormat32bppARGB);
        Graphics graphics(_composite);

        for (Meter *meter : _meters) {
            CLOG(L"Drawing meter:\n%s", meter->ToString().c_str());
            meter->Draw(_composite, &graphics);
        }
    }

    UpdateLayeredWnd();
}

void MeterWnd::UpdateLayeredWnd() {
    BLENDFUNCTION bFunc;
    bFunc.AlphaFormat = AC_SRC_ALPHA;
    bFunc.BlendFlags = 0;
    bFunc.BlendOp = AC_SRC_OVER;
    bFunc.SourceConstantAlpha = _transparency;

    HDC screenDc = GetDC(GetDesktopWindow());
    HDC sourceDc = CreateCompatibleDC(screenDc);

    HBITMAP hBmp;
    _composite->GetHBITMAP(Gdiplus::Color(0, 0, 0, 0), &hBmp);
    HGDIOBJ hReplaced = SelectObject(sourceDc, hBmp);

    POINT pt = { 0, 0 };
    SIZE size = { _composite->GetWidth(), _composite->GetHeight() };

    UPDATELAYEREDWINDOWINFO lwInfo;
    lwInfo.cbSize = sizeof(UPDATELAYEREDWINDOWINFO);
    lwInfo.crKey = 0;
    lwInfo.dwFlags = ULW_ALPHA;
    lwInfo.hdcDst = screenDc;
    lwInfo.hdcSrc = sourceDc;
    lwInfo.pblend = &bFunc;
    lwInfo.pptDst = &_location;
    lwInfo.pptSrc = &pt;
    lwInfo.prcDirty = _dirtyRect;
    lwInfo.psize = &size;

    UpdateLayeredWindowIndirect(_hWnd, &lwInfo);

    SelectObject(sourceDc, hReplaced);
    DeleteDC(sourceDc);
    DeleteObject(hBmp);
    ReleaseDC(GetDesktopWindow(), screenDc);
}

void MeterWnd::UpdateTransparency() {
    BLENDFUNCTION bFunc;
    bFunc.AlphaFormat = AC_SRC_ALPHA;
    bFunc.BlendFlags = 0;
    bFunc.BlendOp = AC_SRC_OVER;
    bFunc.SourceConstantAlpha = _transparency;

    UPDATELAYEREDWINDOWINFO lwInfo;
    lwInfo.cbSize = sizeof(UPDATELAYEREDWINDOWINFO);
    lwInfo.crKey = 0;
    lwInfo.dwFlags = ULW_ALPHA;
    lwInfo.hdcDst = NULL;
    lwInfo.hdcSrc = NULL;
    lwInfo.pblend = &bFunc;
    lwInfo.pptDst = NULL;
    lwInfo.pptSrc = NULL;
    lwInfo.prcDirty = NULL;
    lwInfo.psize = NULL;

    UpdateLayeredWindowIndirect(_hWnd, &lwInfo);
}

void MeterWnd::ApplyGlass(Gdiplus::Bitmap *glassMask) {
    using namespace Gdiplus;
    Color searchColor(0, 0, 0);
    ARGB searchArgb = 0xFF000000;

    unsigned int height = glassMask->GetHeight();
    unsigned int width = glassMask->GetWidth();

    Region reg;
    reg.MakeEmpty();
    bool match = false;
    Rect rec(0, 0, 0, 1); //scan one row of pixels at a time

    for (unsigned int y = 0; y < height; ++y) {
        for (unsigned int x = 0; x < width; ++x) {
            Color pixelColor;
            glassMask->GetPixel(x, y, &pixelColor);
            ARGB pixelArgb = pixelColor.GetValue();

            if ((searchArgb & pixelArgb) == searchArgb && (x + 1 != width)) {
                if (match) {
                    continue;
                }

                match = true;
                rec.X = x;
                rec.Y = y;
            } else if (match) {
                match = false;
                rec.Width = x - rec.X;
                reg.Union(rec);
            }
        }
    }

    DWM_BLURBEHIND blurBehind = { 0 };
    blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
    blurBehind.fEnable = true;

    Graphics g(glassMask);
    HRGN hReg = reg.GetHRGN(&g);
    blurBehind.hRgnBlur = reg.GetHRGN(&g);

    DwmEnableBlurBehindWindow(_hWnd, &blurBehind);
}

//
// UpdateLocation()
//
// Uses MoveWindow() to set the window's X,Y coordinates its size.
//
void MeterWnd::UpdateLocation() {
    MoveWindow(_hWnd, _location.x, _location.y, Width(), Height(), FALSE);
}

void MeterWnd::AddMeter(Meter *meter)
{
    _meters.push_back(meter);
}

void MeterWnd::MeterLevels(float value)
{
    for (Meter *meter : _meters) {
        meter->Value(value);
    }
}

void MeterWnd::HideAnimation(Animation *anim) {
    _hideAnimation = anim;
}

void MeterWnd::VisibleDuration(int duration) {
    _visibleDuration = duration;
}

void MeterWnd::BackgroundImage(Gdiplus::Bitmap *background) {
    _background = background;
    _size.cx = background->GetWidth();
    _size.cy = background->GetHeight();
}

void MeterWnd::Show(bool animate) {
    //TODO: animation support
    if (_visible == false) {
        UpdateLocation();
        ShowWindow(_hWnd, SW_SHOW);
        _visible = true;
    }

    if (_visibleDuration > 0) {
        SetTimer(_hWnd, TIMER_HIDE, _visibleDuration, NULL);
        KillTimer(_hWnd, TIMER_ANIMOUT);

        if (_hideAnimation) {
            _hideAnimation->Reset(this);
        }
    }
}

void MeterWnd::Hide(bool animate) {
    if (_visible == false) {
        return;
    }

    if (animate && _hideAnimation) {
        SetTimer(_hWnd, TIMER_ANIMOUT, 15, NULL);
    } else {
        ShowWindow(_hWnd, SW_HIDE);
        _visible = false;
    }
}

void MeterWnd::AnimateOut() {
    bool animOver = _hideAnimation->Animate(this);
    if (animOver) {
        CLOG(L"Finished hide animation.");
        KillTimer(_hWnd, TIMER_ANIMOUT);
        ShowWindow(_hWnd, SW_HIDE);
        _visible = false;
    }
}

int MeterWnd::X() const {
    return _location.x;
}

void MeterWnd::X(int x) {
    _location.x = x;
}

int MeterWnd::Y() const {
    return _location.y;
}

void MeterWnd::Y(int y) {
    _location.y = y;
}

int MeterWnd::Width() const {
    return _size.cx;
}

int MeterWnd::Height() const {
    return _size.cy;
}

void MeterWnd::Move(int x, int y) {
    _location.x = x;
    _location.y = y;
    UpdateLocation();
}

byte MeterWnd::Transparency() const {
    return _transparency;
}

void MeterWnd::Transparency(byte transparency) {
    _transparency = transparency;
    UpdateTransparency();
}

LRESULT CALLBACK
MeterWnd::StaticWndProc(
        HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    MeterWnd* wnd;

    if (message == WM_CREATE) {
        wnd = (MeterWnd*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
        SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) wnd);
    } else {
        wnd = (MeterWnd*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
        if (!wnd) {
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }

    return wnd->WndProc(message, wParam, lParam);
}

LRESULT MeterWnd::WndProc(UINT message, WPARAM wParam, LPARAM lParam) {
    if (message == WM_TIMER) {
        switch (wParam) {
        case TIMER_HIDE:
            CLOG(L"Display duration has elapsed. Hiding window.");
            Hide();
            KillTimer(_hWnd, TIMER_HIDE);
            break;

        case TIMER_ANIMIN:
            break;

        case TIMER_ANIMOUT:
            AnimateOut();
            break;
        }
    }

    return DefWindowProc(_hWnd, message, wParam, lParam);
}

<commit_msg>Handle DWM composition changes<commit_after>#include "MeterWnd.h"
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")

#include "Animation.h"

#define TIMER_HIDE 100
#define TIMER_ANIMIN 101
#define TIMER_ANIMOUT 102

MeterWnd::MeterWnd(LPCWSTR className, LPCWSTR title, HINSTANCE hInstance,
    Animation *hideAnimation, Animation *showAnimation, int visibleDuration) :
_hInstance(hInstance),
_className(className),
_title(title),
_hideAnimation(hideAnimation),
_showAnimation(showAnimation),
_visibleDuration(visibleDuration),
_visible(false) {
    if (_hInstance == NULL) {
        _hInstance = (HINSTANCE) GetModuleHandle(NULL);
    }

    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = 0;
    wcex.lpfnWndProc = &MeterWnd::StaticWndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = _hInstance;
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = _className;
    wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wcex)) {
        /* throw exception */
    }

    _location.x = 0;
    _location.y = 0;
    _size.cx = 1;
    _size.cy = 1;
    _transparency = 255;

    _hWnd = CreateWindowEx(
        WS_EX_TOOLWINDOW |
        WS_EX_LAYERED |
        WS_EX_NOACTIVATE |
        WS_EX_TOPMOST |
        WS_EX_TRANSPARENT,
        _className, _title,
        WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,
        _size.cx, _size.cy,
        NULL, NULL, _hInstance, this);

    if (_hWnd == NULL) {
        /* throw exception */
    }
}

MeterWnd::~MeterWnd() {
    DestroyWindow(_hWnd);
}

void MeterWnd::Update()
{
    CLOG(L"Updating meter window");
    using namespace Gdiplus;

    bool dirty = (_composite == NULL);

    for (Meter *meter : _meters) {
        if (meter->Dirty() == true) {
            dirty = true;
            break;
        }
    }

    if (dirty) {
        CLOG(L"Contents have changed; redrawing");

        if (_composite) {
            delete _composite;
        }

        Rect bgRect(0, 0, _background->GetWidth(), _background->GetHeight());
        _composite = _background->Clone(bgRect, PixelFormat32bppARGB);
        Graphics graphics(_composite);

        for (Meter *meter : _meters) {
            CLOG(L"Drawing meter:\n%s", meter->ToString().c_str());
            meter->Draw(_composite, &graphics);
        }
    }

    UpdateLayeredWnd();
}

void MeterWnd::UpdateLayeredWnd() {
    BLENDFUNCTION bFunc;
    bFunc.AlphaFormat = AC_SRC_ALPHA;
    bFunc.BlendFlags = 0;
    bFunc.BlendOp = AC_SRC_OVER;
    bFunc.SourceConstantAlpha = _transparency;

    HDC screenDc = GetDC(GetDesktopWindow());
    HDC sourceDc = CreateCompatibleDC(screenDc);

    HBITMAP hBmp;
    _composite->GetHBITMAP(Gdiplus::Color(0, 0, 0, 0), &hBmp);
    HGDIOBJ hReplaced = SelectObject(sourceDc, hBmp);

    POINT pt = { 0, 0 };
    SIZE size = { _composite->GetWidth(), _composite->GetHeight() };

    UPDATELAYEREDWINDOWINFO lwInfo;
    lwInfo.cbSize = sizeof(UPDATELAYEREDWINDOWINFO);
    lwInfo.crKey = 0;
    lwInfo.dwFlags = ULW_ALPHA;
    lwInfo.hdcDst = screenDc;
    lwInfo.hdcSrc = sourceDc;
    lwInfo.pblend = &bFunc;
    lwInfo.pptDst = &_location;
    lwInfo.pptSrc = &pt;
    lwInfo.prcDirty = _dirtyRect;
    lwInfo.psize = &size;

    UpdateLayeredWindowIndirect(_hWnd, &lwInfo);

    SelectObject(sourceDc, hReplaced);
    DeleteDC(sourceDc);
    DeleteObject(hBmp);
    ReleaseDC(GetDesktopWindow(), screenDc);
}

void MeterWnd::UpdateTransparency() {
    BLENDFUNCTION bFunc;
    bFunc.AlphaFormat = AC_SRC_ALPHA;
    bFunc.BlendFlags = 0;
    bFunc.BlendOp = AC_SRC_OVER;
    bFunc.SourceConstantAlpha = _transparency;

    UPDATELAYEREDWINDOWINFO lwInfo;
    lwInfo.cbSize = sizeof(UPDATELAYEREDWINDOWINFO);
    lwInfo.crKey = 0;
    lwInfo.dwFlags = ULW_ALPHA;
    lwInfo.hdcDst = NULL;
    lwInfo.hdcSrc = NULL;
    lwInfo.pblend = &bFunc;
    lwInfo.pptDst = NULL;
    lwInfo.pptSrc = NULL;
    lwInfo.prcDirty = NULL;
    lwInfo.psize = NULL;

    UpdateLayeredWindowIndirect(_hWnd, &lwInfo);
}

void MeterWnd::ApplyGlass(Gdiplus::Bitmap *glassMask) {
    using namespace Gdiplus;
    Color searchColor(0, 0, 0);
    ARGB searchArgb = 0xFF000000;

    unsigned int height = glassMask->GetHeight();
    unsigned int width = glassMask->GetWidth();

    Region reg;
    reg.MakeEmpty();
    bool match = false;
    Rect rec(0, 0, 0, 1); //scan one row of pixels at a time

    for (unsigned int y = 0; y < height; ++y) {
        for (unsigned int x = 0; x < width; ++x) {
            Color pixelColor;
            glassMask->GetPixel(x, y, &pixelColor);
            ARGB pixelArgb = pixelColor.GetValue();

            if ((searchArgb & pixelArgb) == searchArgb && (x + 1 != width)) {
                if (match) {
                    continue;
                }

                match = true;
                rec.X = x;
                rec.Y = y;
            } else if (match) {
                match = false;
                rec.Width = x - rec.X;
                reg.Union(rec);
            }
        }
    }

    DWM_BLURBEHIND blurBehind = { 0 };
    blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
    blurBehind.fEnable = true;

    Graphics g(glassMask);
    HRGN hReg = reg.GetHRGN(&g);
    blurBehind.hRgnBlur = reg.GetHRGN(&g);

    DwmEnableBlurBehindWindow(_hWnd, &blurBehind);
}

//
// UpdateLocation()
//
// Uses MoveWindow() to set the window's X,Y coordinates its size.
//
void MeterWnd::UpdateLocation() {
    MoveWindow(_hWnd, _location.x, _location.y, Width(), Height(), FALSE);
}

void MeterWnd::AddMeter(Meter *meter)
{
    _meters.push_back(meter);
}

void MeterWnd::MeterLevels(float value)
{
    for (Meter *meter : _meters) {
        meter->Value(value);
    }
}

void MeterWnd::HideAnimation(Animation *anim) {
    _hideAnimation = anim;
}

void MeterWnd::VisibleDuration(int duration) {
    _visibleDuration = duration;
}

void MeterWnd::BackgroundImage(Gdiplus::Bitmap *background) {
    _background = background;
    _size.cx = background->GetWidth();
    _size.cy = background->GetHeight();
}

void MeterWnd::Show(bool animate) {
    //TODO: animation support
    if (_visible == false) {
        UpdateLocation();
        ShowWindow(_hWnd, SW_SHOW);
        _visible = true;
    }

    if (_visibleDuration > 0) {
        SetTimer(_hWnd, TIMER_HIDE, _visibleDuration, NULL);
        KillTimer(_hWnd, TIMER_ANIMOUT);

        if (_hideAnimation) {
            _hideAnimation->Reset(this);
        }
    }
}

void MeterWnd::Hide(bool animate) {
    if (_visible == false) {
        return;
    }

    if (animate && _hideAnimation) {
        SetTimer(_hWnd, TIMER_ANIMOUT, 15, NULL);
    } else {
        ShowWindow(_hWnd, SW_HIDE);
        _visible = false;
    }
}

void MeterWnd::AnimateOut() {
    bool animOver = _hideAnimation->Animate(this);
    if (animOver) {
        CLOG(L"Finished hide animation.");
        KillTimer(_hWnd, TIMER_ANIMOUT);
        ShowWindow(_hWnd, SW_HIDE);
        _visible = false;
    }
}

int MeterWnd::X() const {
    return _location.x;
}

void MeterWnd::X(int x) {
    _location.x = x;
}

int MeterWnd::Y() const {
    return _location.y;
}

void MeterWnd::Y(int y) {
    _location.y = y;
}

int MeterWnd::Width() const {
    return _size.cx;
}

int MeterWnd::Height() const {
    return _size.cy;
}

void MeterWnd::Move(int x, int y) {
    _location.x = x;
    _location.y = y;
    UpdateLocation();
}

byte MeterWnd::Transparency() const {
    return _transparency;
}

void MeterWnd::Transparency(byte transparency) {
    _transparency = transparency;
    UpdateTransparency();
}

LRESULT CALLBACK
MeterWnd::StaticWndProc(
        HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    MeterWnd* wnd;

    if (message == WM_CREATE) {
        wnd = (MeterWnd*) ((LPCREATESTRUCT) lParam)->lpCreateParams;
        SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) wnd);
    } else {
        wnd = (MeterWnd*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
        if (!wnd) {
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }

    return wnd->WndProc(message, wParam, lParam);
}

LRESULT MeterWnd::WndProc(UINT message, WPARAM wParam, LPARAM lParam) {
    if (message == WM_TIMER) {
        switch (wParam) {
        case TIMER_HIDE:
            CLOG(L"Display duration has elapsed. Hiding window.");
            Hide();
            KillTimer(_hWnd, TIMER_HIDE);
            break;

        case TIMER_ANIMIN:
            break;

        case TIMER_ANIMOUT:
            AnimateOut();
            break;
        }
    } else if (message == WM_DWMCOMPOSITIONCHANGED) {
        ApplyGlass();
    }

    return DefWindowProc(_hWnd, message, wParam, lParam);
}

<|endoftext|>


<commit_before>#include "process.h"
#include <cstdlib>
#include <thread>
#include <signal.h>

#include <iostream> //TODO: remove
using namespace std; //TODO: remove

pid_t Process::open(const std::string &command, const std::string &path) {
  if(use_stdin)
    stdin_fd=std::unique_ptr<file_descriptor_type>(new int);
  if(read_stdout)
    stdout_fd=std::unique_ptr<file_descriptor_type>(new int);
  if(read_stderr)
    stderr_fd=std::unique_ptr<file_descriptor_type>(new int);

  int stdin_p[2], stdout_p[2], stderr_p[2];

  if(stdin_fd && pipe(stdin_p)!=0) {
    close(stdin_p[0]);
    close(stdin_p[1]);
    return -1;
  }
  if(stdout_fd && pipe(stdout_p)!=0) {
    if(stdin_fd) close(stdin_p[0]);
    if(stdin_fd) close(stdin_p[1]);
    close(stdout_p[0]);
    close(stdout_p[1]);
    return -1;
  }
  if(stderr_fd && pipe(stderr_p)!=0) {
    if(stdin_fd) close(stdin_p[0]);
    if(stdin_fd) close(stdin_p[1]);
    if(stdout_fd) close(stdout_p[0]);
    if(stdout_fd) close(stdout_p[1]);
    close(stderr_p[0]);
    close(stderr_p[1]);
    return -1;
  }

  pid_t pid = fork();

  if (pid < 0) {
    if(stdin_fd) close(stdin_p[0]);
    if(stdin_fd) close(stdin_p[1]);
    if(stdout_fd) close(stdout_p[0]);
    if(stdout_fd) close(stdout_p[1]);
    if(stderr_fd) close(stderr_p[0]);
    if(stderr_fd) close(stderr_p[1]);
    return pid;
  }
  else if (pid == 0) {
    if(stdin_fd) close(stdin_p[1]);
    if(stdout_fd) close(stdout_p[0]);
    if(stderr_fd) close(stderr_p[0]);
    if(stdin_fd) dup2(stdin_p[0], 0);
    if(stdout_fd) dup2(stdout_p[1], 1);
    if(stderr_fd) dup2(stderr_p[1], 2);

    setpgid(0, 0);
    //TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe
    //TODO: One solution is: echo "command;exit"|script -q /dev/null

    if(!path.empty())
      execl("/bin/sh", "sh", "-c", ("cd \""+path+"\" && "+command).c_str(), NULL);
    else
      execl("/bin/sh", "sh", "-c", command.c_str(), NULL);

    perror("execl");
    exit(EXIT_FAILURE);
  }

  if(stdin_fd) close(stdin_p[0]);
  if(stdout_fd) close(stdout_p[1]);
  if(stderr_fd) close(stderr_p[1]);

  if(stdin_fd) *stdin_fd = stdin_p[1];
  if(stdout_fd) *stdout_fd = stdout_p[0];
  if(stderr_fd) *stderr_fd = stderr_p[0];

  return pid;
}

void Process::async_read() {
  if(stdout_fd) {
    stdout_thread=std::thread([this](){
      char buffer[buffer_size];
      ssize_t n;
      while ((n=read(*stdout_fd, buffer, buffer_size)) > 0)
        read_stdout(buffer, static_cast<size_t>(n));
    });
  }
  if(stderr_fd) {
    stderr_thread=std::thread([this](){
      char buffer[buffer_size];
      ssize_t n;
      while ((n=read(*stderr_fd, buffer, buffer_size)) > 0)
        read_stderr(buffer, static_cast<size_t>(n));
    });
  }
}

int Process::get_exit_code() {
  int exit_code;
  waitpid(id, &exit_code, 0);

  if(stdout_thread.joinable())
    stdout_thread.join();
  if(stderr_thread.joinable())
    stderr_thread.join();

  stdin_mutex.lock();
  if(stdin_fd) {
    close(*stdin_fd);
    stdin_fd.reset();
  }
  stdin_mutex.unlock();
  if(stdout_fd) {
    close(*stdout_fd);
    stdout_fd.reset();
  }
  if(stderr_fd) {
    close(*stderr_fd);
    stderr_fd.reset();
  }

  return exit_code;
}

bool Process::write(const char *bytes, size_t n) {
  stdin_mutex.lock();
  if(stdin_fd) {
    if(::write(*stdin_fd, bytes, n)>=0) {
      stdin_mutex.unlock();
      return true;
    }
    else {
      stdin_mutex.unlock();
      return false;
    }
  }
  stdin_mutex.unlock();
  return false;
}

void Process::kill(process_id_type id, bool force) {
  if(force)
    ::kill(-id, SIGTERM);
  else
    ::kill(-id, SIGINT);
}
<commit_msg>Added missing include in process_unix.cc<commit_after>#include "process.h"
#include <cstdlib>
#include <thread>
#include <unistd.h>
#include <signal.h>

#include <iostream> //TODO: remove
using namespace std; //TODO: remove

pid_t Process::open(const std::string &command, const std::string &path) {
  if(use_stdin)
    stdin_fd=std::unique_ptr<file_descriptor_type>(new int);
  if(read_stdout)
    stdout_fd=std::unique_ptr<file_descriptor_type>(new int);
  if(read_stderr)
    stderr_fd=std::unique_ptr<file_descriptor_type>(new int);

  int stdin_p[2], stdout_p[2], stderr_p[2];

  if(stdin_fd && pipe(stdin_p)!=0) {
    close(stdin_p[0]);
    close(stdin_p[1]);
    return -1;
  }
  if(stdout_fd && pipe(stdout_p)!=0) {
    if(stdin_fd) close(stdin_p[0]);
    if(stdin_fd) close(stdin_p[1]);
    close(stdout_p[0]);
    close(stdout_p[1]);
    return -1;
  }
  if(stderr_fd && pipe(stderr_p)!=0) {
    if(stdin_fd) close(stdin_p[0]);
    if(stdin_fd) close(stdin_p[1]);
    if(stdout_fd) close(stdout_p[0]);
    if(stdout_fd) close(stdout_p[1]);
    close(stderr_p[0]);
    close(stderr_p[1]);
    return -1;
  }

  pid_t pid = fork();

  if (pid < 0) {
    if(stdin_fd) close(stdin_p[0]);
    if(stdin_fd) close(stdin_p[1]);
    if(stdout_fd) close(stdout_p[0]);
    if(stdout_fd) close(stdout_p[1]);
    if(stderr_fd) close(stderr_p[0]);
    if(stderr_fd) close(stderr_p[1]);
    return pid;
  }
  else if (pid == 0) {
    if(stdin_fd) close(stdin_p[1]);
    if(stdout_fd) close(stdout_p[0]);
    if(stderr_fd) close(stderr_p[0]);
    if(stdin_fd) dup2(stdin_p[0], 0);
    if(stdout_fd) dup2(stdout_p[1], 1);
    if(stderr_fd) dup2(stderr_p[1], 2);

    setpgid(0, 0);
    //TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe
    //TODO: One solution is: echo "command;exit"|script -q /dev/null

    if(!path.empty())
      execl("/bin/sh", "sh", "-c", ("cd \""+path+"\" && "+command).c_str(), NULL);
    else
      execl("/bin/sh", "sh", "-c", command.c_str(), NULL);

    perror("execl");
    exit(EXIT_FAILURE);
  }

  if(stdin_fd) close(stdin_p[0]);
  if(stdout_fd) close(stdout_p[1]);
  if(stderr_fd) close(stderr_p[1]);

  if(stdin_fd) *stdin_fd = stdin_p[1];
  if(stdout_fd) *stdout_fd = stdout_p[0];
  if(stderr_fd) *stderr_fd = stderr_p[0];

  return pid;
}

void Process::async_read() {
  if(stdout_fd) {
    stdout_thread=std::thread([this](){
      char buffer[buffer_size];
      ssize_t n;
      while ((n=read(*stdout_fd, buffer, buffer_size)) > 0)
        read_stdout(buffer, static_cast<size_t>(n));
    });
  }
  if(stderr_fd) {
    stderr_thread=std::thread([this](){
      char buffer[buffer_size];
      ssize_t n;
      while ((n=read(*stderr_fd, buffer, buffer_size)) > 0)
        read_stderr(buffer, static_cast<size_t>(n));
    });
  }
}

int Process::get_exit_code() {
  int exit_code;
  waitpid(id, &exit_code, 0);

  if(stdout_thread.joinable())
    stdout_thread.join();
  if(stderr_thread.joinable())
    stderr_thread.join();

  stdin_mutex.lock();
  if(stdin_fd) {
    close(*stdin_fd);
    stdin_fd.reset();
  }
  stdin_mutex.unlock();
  if(stdout_fd) {
    close(*stdout_fd);
    stdout_fd.reset();
  }
  if(stderr_fd) {
    close(*stderr_fd);
    stderr_fd.reset();
  }

  return exit_code;
}

bool Process::write(const char *bytes, size_t n) {
  stdin_mutex.lock();
  if(stdin_fd) {
    if(::write(*stdin_fd, bytes, n)>=0) {
      stdin_mutex.unlock();
      return true;
    }
    else {
      stdin_mutex.unlock();
      return false;
    }
  }
  stdin_mutex.unlock();
  return false;
}

void Process::kill(process_id_type id, bool force) {
  if(force)
    ::kill(-id, SIGTERM);
  else
    ::kill(-id, SIGINT);
}
<|endoftext|>


<commit_before>#include <prjxray/xilinx/xc7series/part.h>

#include <iomanip>
#include <sstream>

namespace prjxray {
namespace xilinx {
namespace xc7series {

absl::optional<Part> Part::FromFile(const std::string& path) {
	try {
		YAML::Node yaml = YAML::LoadFile(path);
		return yaml.as<Part>();
	} catch (YAML::Exception e) {
		return {};
	}
}

bool Part::IsValidFrameAddress(FrameAddress address) const {
	if (address.is_bottom_half_rows()) {
		return bottom_region_.IsValidFrameAddress(address);
	} else {
		return top_region_.IsValidFrameAddress(address);
	}
}

absl::optional<FrameAddress> Part::GetNextFrameAddress(
    FrameAddress address) const {
	// Ask the current global clock region first.
	absl::optional<FrameAddress> next_address =
	    (address.is_bottom_half_rows()
	         ? bottom_region_.GetNextFrameAddress(address)
	         : top_region_.GetNextFrameAddress(address));
	if (next_address)
		return next_address;

	// If the current address is in the top region, the bottom region is
	// next numerically.
	if (!address.is_bottom_half_rows()) {
		next_address =
		    FrameAddress(address.block_type(), true, 0, 0, 0);
		if (bottom_region_.IsValidFrameAddress(*next_address))
			return next_address;
	}

	// Block types are next numerically.
	if (address.block_type() < BlockType::BLOCK_RAM) {
		next_address =
		    FrameAddress(BlockType::BLOCK_RAM, false, 0, 0, 0);
		if (IsValidFrameAddress(*next_address))
			return next_address;
	}

	if (address.block_type() < BlockType::CFG_CLB) {
		next_address = FrameAddress(BlockType::CFG_CLB, false, 0, 0, 0);
		if (IsValidFrameAddress(*next_address))
			return next_address;
	}

	return {};
}

}  // namespace xc7series
}  // namespace xilinx
}  // namespace prjxray

namespace xc7series = prjxray::xilinx::xc7series;

namespace YAML {

Node convert<xc7series::Part>::encode(const xc7series::Part& rhs) {
	Node node;
	node.SetTag("xilinx/xc7series/part");

	std::ostringstream idcode_str;
	idcode_str << "0x" << std::hex << rhs.idcode_;
	node["idcode"] = idcode_str.str();
	node["global_clock_regions"]["top"] = rhs.top_region_;
	node["global_clock_regions"]["bottom"] = rhs.bottom_region_;
	return node;
}

bool convert<xc7series::Part>::decode(const Node& node, xc7series::Part& lhs) {
	if (!node.Tag().empty() && node.Tag() != "xilinx/xc7series/part")
		return false;

	if (!node["global_clock_regions"] && !node["configuration_ranges"]) {
		return false;
	}

	lhs.idcode_ = node["idcode"].as<uint32_t>();

	if (node["global_clock_regions"]) {
		lhs.top_region_ = node["global_clock_regions"]["top"]
		                      .as<xc7series::GlobalClockRegion>();
		lhs.bottom_region_ = node["global_clock_regions"]["bottom"]
		                         .as<xc7series::GlobalClockRegion>();
	} else if (node["configuration_ranges"]) {
		std::vector<xc7series::FrameAddress> addresses;
		for (auto range : node["configuration_ranges"]) {
			auto begin =
			    range["begin"].as<xc7series::FrameAddress>();
			auto end = range["end"].as<xc7series::FrameAddress>();
			for (uint32_t cur = begin; cur < end; ++cur) {
				addresses.push_back(cur);
			}
		}

		lhs = xc7series::Part(lhs.idcode_, addresses);
	}

	return true;
};

}  // namespace YAML
<commit_msg>lib: xilinx: catch exceptions by reference<commit_after>#include <prjxray/xilinx/xc7series/part.h>

#include <iomanip>
#include <sstream>

namespace prjxray {
namespace xilinx {
namespace xc7series {

absl::optional<Part> Part::FromFile(const std::string& path) {
	try {
		YAML::Node yaml = YAML::LoadFile(path);
		return yaml.as<Part>();
	} catch (YAML::Exception& e) {
		return {};
	}
}

bool Part::IsValidFrameAddress(FrameAddress address) const {
	if (address.is_bottom_half_rows()) {
		return bottom_region_.IsValidFrameAddress(address);
	} else {
		return top_region_.IsValidFrameAddress(address);
	}
}

absl::optional<FrameAddress> Part::GetNextFrameAddress(
    FrameAddress address) const {
	// Ask the current global clock region first.
	absl::optional<FrameAddress> next_address =
	    (address.is_bottom_half_rows()
	         ? bottom_region_.GetNextFrameAddress(address)
	         : top_region_.GetNextFrameAddress(address));
	if (next_address)
		return next_address;

	// If the current address is in the top region, the bottom region is
	// next numerically.
	if (!address.is_bottom_half_rows()) {
		next_address =
		    FrameAddress(address.block_type(), true, 0, 0, 0);
		if (bottom_region_.IsValidFrameAddress(*next_address))
			return next_address;
	}

	// Block types are next numerically.
	if (address.block_type() < BlockType::BLOCK_RAM) {
		next_address =
		    FrameAddress(BlockType::BLOCK_RAM, false, 0, 0, 0);
		if (IsValidFrameAddress(*next_address))
			return next_address;
	}

	if (address.block_type() < BlockType::CFG_CLB) {
		next_address = FrameAddress(BlockType::CFG_CLB, false, 0, 0, 0);
		if (IsValidFrameAddress(*next_address))
			return next_address;
	}

	return {};
}

}  // namespace xc7series
}  // namespace xilinx
}  // namespace prjxray

namespace xc7series = prjxray::xilinx::xc7series;

namespace YAML {

Node convert<xc7series::Part>::encode(const xc7series::Part& rhs) {
	Node node;
	node.SetTag("xilinx/xc7series/part");

	std::ostringstream idcode_str;
	idcode_str << "0x" << std::hex << rhs.idcode_;
	node["idcode"] = idcode_str.str();
	node["global_clock_regions"]["top"] = rhs.top_region_;
	node["global_clock_regions"]["bottom"] = rhs.bottom_region_;
	return node;
}

bool convert<xc7series::Part>::decode(const Node& node, xc7series::Part& lhs) {
	if (!node.Tag().empty() && node.Tag() != "xilinx/xc7series/part")
		return false;

	if (!node["global_clock_regions"] && !node["configuration_ranges"]) {
		return false;
	}

	lhs.idcode_ = node["idcode"].as<uint32_t>();

	if (node["global_clock_regions"]) {
		lhs.top_region_ = node["global_clock_regions"]["top"]
		                      .as<xc7series::GlobalClockRegion>();
		lhs.bottom_region_ = node["global_clock_regions"]["bottom"]
		                         .as<xc7series::GlobalClockRegion>();
	} else if (node["configuration_ranges"]) {
		std::vector<xc7series::FrameAddress> addresses;
		for (auto range : node["configuration_ranges"]) {
			auto begin =
			    range["begin"].as<xc7series::FrameAddress>();
			auto end = range["end"].as<xc7series::FrameAddress>();
			for (uint32_t cur = begin; cur < end; ++cur) {
				addresses.push_back(cur);
			}
		}

		lhs = xc7series::Part(lhs.idcode_, addresses);
	}

	return true;
};

}  // namespace YAML
<|endoftext|>


<commit_before>#include "freeway.h"
#include <cstdio>
#include <cstdlib>

#ifdef _LP64
#define __PRIS_PREFIX "z"
#else
#define __PRIS_PREFIX
#endif

#define PRIuS __PRIS_PREFIX "u"

int main(int argc, char** argv) {
  unsigned seed = time(NULL);
  int simulation_steps = 10;
  size_t num_segments = 5;
  size_t toll_pass_limit = 10;
  int segment_ready_percent = 30;
  for (int i = 1; i < argc; ++i) {
    if (strcmp("-seed", argv[i]) == 0) {
      ++i;
      sscanf(argv[i], "%u", &seed);
    } else if (strcmp("-N", argv[i]) == 0) {
      ++i;
      simulation_steps = atoi(argv[i]);
    } else if (strcmp("-NSegs", argv[i]) == 0) {
      ++i;
      sscanf(argv[i], "%" PRIuS, &num_segments);
    } else if (strcmp("-K", argv[i]) == 0) {
      ++i;
      sscanf(argv[i], "%" PRIuS, &toll_pass_limit);
    } else if (strcmp("-Percent", argv[i]) == 0) {
      ++i;
      segment_ready_percent = atoi(argv[i]);
    }
  }
  printf("Seed: %u\n"
         "N: %d\n"
         "NSegs: %" PRIuS "\n"
         "K: %" PRIuS "\n"
         "Percent: %d\n",
         seed, simulation_steps, num_segments, toll_pass_limit,
         segment_ready_percent);

  size_t* segment_capacities = new size_t[num_segments];
  for (size_t i = 0; i < num_segments; ++i) {
    scanf("%" PRIuS, segment_capacities + i);
  }

  oop_project::Freeway freeway(num_segments, segment_ready_percent,
                               segment_capacities, toll_pass_limit);
  printf("Cars: %" PRIuS "\n", freeway.num_cars());
  for (int i = 0; i < simulation_steps; ++i) {
    printf("\nOperation: %d\n", i + 1);
    freeway.Operate();
  }

  delete[] segment_capacities;
  return 0;
}
<commit_msg>Main: Added missing deps<commit_after>#include "freeway.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>

#ifdef _LP64
#define __PRIS_PREFIX "z"
#else
#define __PRIS_PREFIX
#endif

#define PRIuS __PRIS_PREFIX "u"

int main(int argc, char** argv) {
  unsigned seed = time(NULL);
  int simulation_steps = 10;
  size_t num_segments = 5;
  size_t toll_pass_limit = 10;
  int segment_ready_percent = 30;
  for (int i = 1; i < argc; ++i) {
    if (strcmp("-seed", argv[i]) == 0) {
      ++i;
      sscanf(argv[i], "%u", &seed);
    } else if (strcmp("-N", argv[i]) == 0) {
      ++i;
      simulation_steps = atoi(argv[i]);
    } else if (strcmp("-NSegs", argv[i]) == 0) {
      ++i;
      sscanf(argv[i], "%" PRIuS, &num_segments);
    } else if (strcmp("-K", argv[i]) == 0) {
      ++i;
      sscanf(argv[i], "%" PRIuS, &toll_pass_limit);
    } else if (strcmp("-Percent", argv[i]) == 0) {
      ++i;
      segment_ready_percent = atoi(argv[i]);
    }
  }
  printf("Seed: %u\n"
         "N: %d\n"
         "NSegs: %" PRIuS "\n"
         "K: %" PRIuS "\n"
         "Percent: %d\n",
         seed, simulation_steps, num_segments, toll_pass_limit,
         segment_ready_percent);

  size_t* segment_capacities = new size_t[num_segments];
  for (size_t i = 0; i < num_segments; ++i) {
    scanf("%" PRIuS, segment_capacities + i);
  }

  oop_project::Freeway freeway(num_segments, segment_ready_percent,
                               segment_capacities, toll_pass_limit);
  printf("Cars: %" PRIuS "\n", freeway.num_cars());
  for (int i = 0; i < simulation_steps; ++i) {
    printf("\nOperation: %d\n", i + 1);
    freeway.Operate();
  }

  delete[] segment_capacities;
  return 0;
}
<|endoftext|>


<commit_before>#include <boost/python.hpp>
#include <boost/python/exception_translator.hpp>
#include "cimage.h"
#include "cproc.h"
#include "gil.h"
#include "except.h"
#include "bitmap.h"

using namespace boost::python;

class CimageWrapper
    : public Cimage
    , public wrapper<Cimage>
{
public:
    CimageWrapper(size_t width, size_t height)
        : Cimage(width, height)
    {

    }

public:
    void info()
    {
        ScopedPythonGILLock gil_lock;
        override f = this->get_override("info");
        if (f)
            f();
        else
            Cimage::info();
    }
};

void translate_FileError(FileError const & e)
{
    PyErr_SetString(PyExc_OSError, "FileError");
}

BOOST_PYTHON_MODULE(pymycpp)
{
    PyEval_InitThreads();

    register_exception_translator<FileError>(&translate_FileError);

    class_<CimageWrapper, boost::noncopyable>(
            "Cimage", init<size_t, size_t>())
        .def("width", &CimageWrapper::width)
        .def("height", &CimageWrapper::height)
        .def("how_many_bytes", &CimageWrapper::how_many_bytes)
        .staticmethod("how_many_bytes")
        .def("info", &CimageWrapper::info)
        ;

    class_<Cproc, boost::noncopyable>(
            "Cproc", init<>())
        .def("run", &Cproc::run)
        .def("stop", &Cproc::stop)
        ;

    class_<Bitmap, boost::noncopyable>(
            "Bitmap", init<std::string>())
        .def("get_width", &Bitmap::get_width)
        .def("get_height", &Bitmap::get_height)
        .def("save", &Bitmap::save)
        ;
}
<commit_msg>Closes #17: Merge branch '17-expose-bitmap-data-as-ndarray'<commit_after>#include <boost/python.hpp>
#include <boost/python/exception_translator.hpp>
#include <boost/python/numpy.hpp>
#include "cimage.h"
#include "cproc.h"
#include "gil.h"
#include "except.h"
#include "bitmap.h"

using namespace boost::python;

class BitmapWrapper : public Bitmap
                      , public wrapper<Bitmap>
{
    public:
        BitmapWrapper(std::string filepath)
            : Bitmap(filepath)
        {
            // nop
        }

        numpy::ndarray data_as_ndarray() const
        {
            tuple shape, strides;
            numpy::dtype data_type = numpy::dtype::get_builtin<uint8_t>();
            shape = make_tuple(_height,
                               _width,
                               3);
            strides = make_tuple(_width * 3 * sizeof(uint8_t),
                                 3 * sizeof(uint8_t),
                                 sizeof(uint8_t));
            return numpy::from_data(_data,
                                    data_type,
                                    shape,
                                    strides,
                                    object());
        }
};

class CimageWrapper
    : public Cimage
    , public wrapper<Cimage>
{
public:
    CimageWrapper(size_t width, size_t height)
        : Cimage(width, height)
    {

    }

public:
    void info()
    {
        ScopedPythonGILLock gil_lock;
        override f = this->get_override("info");
        if (f)
            f();
        else
            Cimage::info();
    }
};

void translate_FileError(FileError const & e)
{
    PyErr_SetString(PyExc_OSError, "FileError");
}

BOOST_PYTHON_MODULE(pymycpp)
{
    PyEval_InitThreads();

    register_exception_translator<FileError>(&translate_FileError);

    class_<CimageWrapper, boost::noncopyable>(
            "Cimage", init<size_t, size_t>())
        .def("width", &CimageWrapper::width)
        .def("height", &CimageWrapper::height)
        .def("how_many_bytes", &CimageWrapper::how_many_bytes)
        .staticmethod("how_many_bytes")
        .def("info", &CimageWrapper::info)
        ;

    class_<Cproc, boost::noncopyable>(
            "Cproc", init<>())
        .def("run", &Cproc::run)
        .def("stop", &Cproc::stop)
        ;

    class_<BitmapWrapper, boost::noncopyable>(
            "Bitmap", init<std::string>())
        .def("get_width", &BitmapWrapper::get_width)
        .def("get_height", &BitmapWrapper::get_height)
        .def("save", &BitmapWrapper::save)
        .def("data", &BitmapWrapper::data_as_ndarray)
        ;
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_DURATION_HPP
#define CAF_DURATION_HPP

#include <string>
#include <chrono>
#include <cstdint>
#include <stdexcept>

#include "caf/error.hpp"

namespace caf {

/// SI time units to specify timeouts.
/// @relates duration
enum class time_unit : uint32_t {
  invalid,
  minutes,
  seconds,
  milliseconds,
  microseconds,
  nanoseconds
};

/// Relates time_unit
std::string to_string(time_unit x);

// Calculates the index of a time_unit from the denominator of a ratio.
constexpr intmax_t denom_to_unit_index(intmax_t x, intmax_t offset = 2) {
  return x < 1000 ? (x == 1 ? offset : 0)
                  : denom_to_unit_index(x / 1000, offset + 1);
}

constexpr time_unit denom_to_time_unit(intmax_t x) {
  return static_cast<time_unit>(denom_to_unit_index(x));
}

/// Converts the ratio Num/Denom to a `time_unit` if the ratio describes
/// seconds, milliseconds, microseconds, or minutes. Minutes are mapped
/// to `time_unit::seconds`, any unrecognized ratio to `time_unit::invalid`.
/// @relates duration
template <intmax_t Num, intmax_t Denom>
struct ratio_to_time_unit_helper;

template <intmax_t Denom>
struct ratio_to_time_unit_helper<1, Denom> {
  static constexpr time_unit value = denom_to_time_unit(Denom);
};

template <>
struct ratio_to_time_unit_helper<60, 1> {
  static constexpr time_unit value = time_unit::minutes;
};

/// Converts an STL time period to a `time_unit`.
/// @relates duration
template <class Period>
constexpr time_unit get_time_unit_from_period() {
  return ratio_to_time_unit_helper<Period::num, Period::den>::value;
}

/// Represents an infinite amount of timeout for specifying "invalid" timeouts.
struct infinite_t {
  constexpr infinite_t() {
    // nop
  }
};

static constexpr infinite_t infinite = infinite_t{};

/// Time duration consisting of a `time_unit` and a 64 bit unsigned integer.
class duration {
public:
  constexpr duration() : unit(time_unit::invalid), count(0) {
    // nop
  }

  constexpr duration(time_unit u, uint32_t v) : unit(u), count(v) {
    // nop
  }

  constexpr duration(infinite_t) : unit(time_unit::invalid), count(0) {
    // nop
  }

  /// Creates a new instance from an STL duration.
  /// @throws std::invalid_argument Thrown if `d.count() is negative.
  template <class Rep, class Period,
            class E =
              typename std::enable_if<
                std::is_integral<Rep>::value
                && get_time_unit_from_period<Period>() != time_unit::invalid
              >::type>
  explicit duration(const std::chrono::duration<Rep, Period>& d)
      : unit(get_time_unit_from_period<Period>()),
        count(d.count() < 0 ? 0u : static_cast<uint64_t>(d.count())) {
    // nop
  }

  /// Returns `unit != time_unit::invalid`.
  constexpr bool valid() const {
    return unit != time_unit::invalid;
  }

  /// Returns `count == 0`.
  constexpr bool is_zero() const {
    return count == 0;
  }

  time_unit unit;

  uint64_t count;
};

/// @relates duration
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, duration& x) {
  return f(x.unit, x.count);
}

/// @relates duration
std::string to_string(const duration& x);

/// @relates duration
bool operator==(const duration& lhs, const duration& rhs);

/// @relates duration
inline bool operator!=(const duration& lhs, const duration& rhs) {
  return !(lhs == rhs);
}

/// @relates duration
template <class Clock, class Duration>
std::chrono::time_point<Clock, Duration>&
operator+=(std::chrono::time_point<Clock, Duration>& lhs, const duration& rhs) {
  switch (rhs.unit) {
    case time_unit::invalid:
      break;
    case time_unit::minutes:
      lhs += std::chrono::minutes(rhs.count);
      break;
    case time_unit::seconds:
      lhs += std::chrono::seconds(rhs.count);
      break;
    case time_unit::milliseconds:
      lhs += std::chrono::milliseconds(rhs.count);
      break;
    case time_unit::microseconds:
      lhs += std::chrono::microseconds(rhs.count);
      break;
    case time_unit::nanoseconds:
      lhs += std::chrono::nanoseconds(rhs.count);
      break;
  }
  return lhs;
}

} // namespace caf

#endif // CAF_DURATION_HPP
<commit_msg>Add literals for caf::duration<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_DURATION_HPP
#define CAF_DURATION_HPP

#include <string>
#include <chrono>
#include <cstdint>
#include <stdexcept>

#include "caf/error.hpp"

namespace caf {

/// SI time units to specify timeouts.
/// @relates duration
enum class time_unit : uint32_t {
  invalid,
  minutes,
  seconds,
  milliseconds,
  microseconds,
  nanoseconds
};

/// Relates time_unit
std::string to_string(time_unit x);

// Calculates the index of a time_unit from the denominator of a ratio.
constexpr intmax_t denom_to_unit_index(intmax_t x, intmax_t offset = 2) {
  return x < 1000 ? (x == 1 ? offset : 0)
                  : denom_to_unit_index(x / 1000, offset + 1);
}

constexpr time_unit denom_to_time_unit(intmax_t x) {
  return static_cast<time_unit>(denom_to_unit_index(x));
}

/// Converts the ratio Num/Denom to a `time_unit` if the ratio describes
/// seconds, milliseconds, microseconds, or minutes. Minutes are mapped
/// to `time_unit::seconds`, any unrecognized ratio to `time_unit::invalid`.
/// @relates duration
template <intmax_t Num, intmax_t Denom>
struct ratio_to_time_unit_helper;

template <intmax_t Denom>
struct ratio_to_time_unit_helper<1, Denom> {
  static constexpr time_unit value = denom_to_time_unit(Denom);
};

template <>
struct ratio_to_time_unit_helper<60, 1> {
  static constexpr time_unit value = time_unit::minutes;
};

/// Converts an STL time period to a `time_unit`.
/// @relates duration
template <class Period>
constexpr time_unit get_time_unit_from_period() {
  return ratio_to_time_unit_helper<Period::num, Period::den>::value;
}

/// Represents an infinite amount of timeout for specifying "invalid" timeouts.
struct infinite_t {
  constexpr infinite_t() {
    // nop
  }
};

static constexpr infinite_t infinite = infinite_t{};

/// Time duration consisting of a `time_unit` and a 64 bit unsigned integer.
class duration {
public:
  constexpr duration() : unit(time_unit::invalid), count(0) {
    // nop
  }

  constexpr duration(time_unit u, uint32_t v) : unit(u), count(v) {
    // nop
  }

  constexpr duration(infinite_t) : unit(time_unit::invalid), count(0) {
    // nop
  }

  /// Creates a new instance from an STL duration.
  /// @throws std::invalid_argument Thrown if `d.count() is negative.
  template <class Rep, class Period,
            class E =
              typename std::enable_if<
                std::is_integral<Rep>::value
                && get_time_unit_from_period<Period>() != time_unit::invalid
              >::type>
  explicit duration(const std::chrono::duration<Rep, Period>& d)
      : unit(get_time_unit_from_period<Period>()),
        count(d.count() < 0 ? 0u : static_cast<uint64_t>(d.count())) {
    // nop
  }

  /// Returns `unit != time_unit::invalid`.
  constexpr bool valid() const {
    return unit != time_unit::invalid;
  }

  /// Returns `count == 0`.
  constexpr bool is_zero() const {
    return count == 0;
  }

  time_unit unit;

  uint64_t count;
};

/// @relates duration
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, duration& x) {
  return f(x.unit, x.count);
}

/// @relates duration
std::string to_string(const duration& x);

/// @relates duration
bool operator==(const duration& lhs, const duration& rhs);

/// @relates duration
inline bool operator!=(const duration& lhs, const duration& rhs) {
  return !(lhs == rhs);
}

/// @relates duration
template <class Clock, class Duration>
std::chrono::time_point<Clock, Duration>&
operator+=(std::chrono::time_point<Clock, Duration>& lhs, const duration& rhs) {
  switch (rhs.unit) {
    case time_unit::invalid:
      break;
    case time_unit::minutes:
      lhs += std::chrono::minutes(rhs.count);
      break;
    case time_unit::seconds:
      lhs += std::chrono::seconds(rhs.count);
      break;
    case time_unit::milliseconds:
      lhs += std::chrono::milliseconds(rhs.count);
      break;
    case time_unit::microseconds:
      lhs += std::chrono::microseconds(rhs.count);
      break;
    case time_unit::nanoseconds:
      lhs += std::chrono::nanoseconds(rhs.count);
      break;
  }
  return lhs;
}

namespace literals {

constexpr duration operator""_min(unsigned long long x) {
  return {time_unit::minutes, static_cast<uint32_t>(x)};
}

constexpr duration operator""_s(unsigned long long x) {
  return {time_unit::seconds, static_cast<uint32_t>(x)};
}
constexpr duration operator""_ms(unsigned long long x) {
  return {time_unit::milliseconds, static_cast<uint32_t>(x)};
}

constexpr duration operator""_us(unsigned long long x) {
  return {time_unit::microseconds, static_cast<uint32_t>(x)};
}

constexpr duration operator""_ns(unsigned long long x) {
  return {time_unit::nanoseconds, static_cast<uint32_t>(x)};
}

} // namespace literals

} // namespace caf

#endif // CAF_DURATION_HPP
<|endoftext|>