<commit_before>/*

MIT License

Copyright (c) 2016 Mark Allender

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

#include <string>
#include <fstream>
#include "imgui/imgui.h"
#include "imgui/imgui_impl_sdl.h"
#include "interface.h"
#include "6502/video.h"
#include "6502/disk.h"
#include "utils/path_utils.h"
#include "apple2emu.h"
#include "nfd.h"

static bool Show_main_menu = false;
static bool Show_debug_menu = false;
static bool Menu_open_at_start = false;
static bool Imgui_initialized = false;

static const int32_t Line_length = 256;
static const char *Settings_filename = "settings.txt";

// settings to be stored
static int32_t Video_color_type = static_cast<int>(video_display_types::MONO_WHITE);

static const uint32_t Cycles_array_size = 64;
static uint32_t Cycles_per_frame[Cycles_array_size];
static uint32_t Current_cycles_array_entry = 0;

// inserts disk image into the given disk drive
static void ui_insert_disk(const char *disk_filename, int slot)
{
	FILE *fp = fopen(disk_filename, "rb");
	if (fp == nullptr) {
		return;
	}
	fclose(fp);

	disk_insert(disk_filename, slot);
}

static void ui_load_settings()
{
	std::ifstream infile(Settings_filename);
	std::string line;

	while(std::getline(infile, line)) {
		int pos = line.find('=');
		if (pos > 0) {
			std::string setting = line.substr(0, pos - 1);
			std::string value = line.substr(pos + 1);
			while (value[0] == ' ') {
				value.erase(0, 1);
			}

			if (setting == "auto_start") {
				int i_val = strtol(value.c_str(), nullptr, 10);
				Auto_start = i_val ? true : false;
			}
			else if (setting == "emulator_type") {
				int i_val = strtol(value.c_str(), nullptr, 10);
				Emulator_type = static_cast<emulator_type>(i_val);
			}
			else if (setting == "open_at_start") {
				int i_val = strtol(value.c_str(), nullptr, 10);
				Menu_open_at_start = i_val ? true : false;
				Show_main_menu = Menu_open_at_start;
			}
			else if (setting == "disk1") {
				ui_insert_disk(value.c_str(), 1);
			}
			else if (setting == "disk2") {
				ui_insert_disk(value.c_str(), 2);
			}
			else if (setting == "video") {
				Video_color_type = (uint8_t)strtol(value.c_str(), nullptr, 10);
				video_set_mono_type(static_cast<video_display_types>(Video_color_type));
			}
			else if (setting == "speed") {
				Speed_multiplier = (int)strtol(value.c_str(), nullptr, 10);
			}
		}
	}
}

static void ui_save_settings()
{
	FILE *fp = fopen(Settings_filename, "wt");
	if (fp == nullptr) {
		printf("Unable to open settings file for writing\n");
		return;
	}
	fprintf(fp, "auto_start = %d\n", Auto_start == true ? 1 : 0);
	fprintf(fp, "emulator_type = %d\n", static_cast<uint8_t>(Emulator_type));
	fprintf(fp, "open_at_start = %d\n", Menu_open_at_start == true ? 1 : 0);
	fprintf(fp, "disk1 = %s\n", disk_get_mounted_filename(1));
	fprintf(fp, "disk2 = %s\n", disk_get_mounted_filename(2));
	fprintf(fp, "video = %d\n", Video_color_type);
	fprintf(fp, "speed = %d\n", Speed_multiplier);

	fclose(fp);
}

static void ui_show_general_options()
{
	ImGui::Text("General Options:");
	ImGui::Checkbox("Auto Start", &Auto_start);
	ImGui::SameLine(200);
	if (Emulator_state == emulator_state::SPLASH_SCREEN) {
		if (ImGui::Button("Start")) {
			Emulator_state = emulator_state::EMULATOR_STARTED;
		}
	} else {
		if (ImGui::Button("Reboot")) {
			reset_machine();
			Show_main_menu = true;
		}
	}
	ImGui::Separator();

	static int type = static_cast<uint8_t>(Emulator_type);
	int old_type = type;
	ImGui::ListBox("Emulation Type", &type, Emulator_names, static_cast<uint8_t>(emulator_type::NUM_EMULATOR_TYPES));

	// if the emulator type changed, then reset the machine (if we haven't
	// started yet.  Otherwise tell user that we need to reset machine
	// for this change to take effect
	if (old_type != type && Emulator_state == emulator_state::SPLASH_SCREEN) {
		Emulator_type = static_cast<emulator_type>(type);
		reset_machine();
	} else {
	}
	ImGui::Checkbox("Open Menu on startup", &Menu_open_at_start);
}

static void ui_get_disk_image(uint8_t slot_num)
{
	nfdchar_t *outPath = NULL;
	nfdresult_t result = NFD_OpenDialog("dsk,do", nullptr, &outPath);

	if (result == NFD_OKAY) {
		disk_insert(outPath, slot_num);
		free(outPath);
	}
}

static void ui_show_disk_menu()
{
	ImGui::Text("Disk Drive Options:");
	ImGui::Spacing();
	ImGui::Spacing();
	ImGui::Text("Slot 6, Disk 1:");
	std::string filename;
	path_utils_get_filename(disk_get_mounted_filename(1), filename);
	if (filename.empty()) {
		filename = "<none>";
	}
	ImGui::SameLine();
	if (ImGui::Button(filename.c_str())) {
		ui_get_disk_image(1);
	}
	ImGui::SameLine();
	if (ImGui::Button("Eject")) {
		disk_eject(1);
	}
	ImGui::Spacing();

	ImGui::Text("Slot 6, Disk 2:");
	path_utils_get_filename(disk_get_mounted_filename(2), filename);
	if (filename.empty()) {
		filename = "<none>";
	}
	ImGui::SameLine();
	if (ImGui::Button(filename.c_str())) {
		ui_get_disk_image(2);
	}
	ImGui::SameLine();
	if (ImGui::Button("Eject")) {
		disk_eject(2);
	}
}

static void ui_show_video_output_menu()
{
	ImGui::Text("Video Output Options:");
	ImGui::RadioButton("White", &Video_color_type, static_cast<uint8_t>(video_display_types::MONO_WHITE));
	ImGui::SameLine();
	ImGui::RadioButton("Amber", &Video_color_type, static_cast<uint8_t>(video_display_types::MONO_AMBER));
	ImGui::SameLine();
	ImGui::RadioButton("Green", &Video_color_type, static_cast<uint8_t>(video_display_types::MONO_GREEN));
	ImGui::SameLine();
	ImGui::RadioButton("Color", &Video_color_type, static_cast<uint8_t>(video_display_types::COLOR));
	video_set_mono_type(static_cast<video_display_types>(Video_color_type));
}

static void ui_show_speed_menu()
{
	if (ImGui::SliderInt("Emulator Speed", (int *)&Speed_multiplier, 1, 10) == true) {
	}
}

static void ui_show_main_menu()
{
	ImGui::Begin("Options");
	ui_show_general_options();
	ImGui::Separator();
	ui_show_video_output_menu();
	ImGui::Separator();
	ui_show_disk_menu();
	ImGui::Separator();
	ui_show_speed_menu();
	ImGui::End();
}

static void ui_show_debug_menu()
{
	static bool animate = true;
	static float cycles[Cycles_array_size];

	ImGui::Begin("Debug");
	ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
	ImGui::Separator();
	ImGui::Checkbox("Animate", &animate);
	if (animate == true) {
		auto index = 0;
		auto i = Current_cycles_array_entry;
		while (true) {
			cycles[index++] = static_cast<float>(Cycles_per_frame[i]);
			i = (i + 1) % Cycles_array_size;
			if (i == Current_cycles_array_entry) {
				break;
			}
		}
	}
	ImGui::PlotLines("Cycles/Frame", cycles, Cycles_array_size, 0, nullptr, 17020.0f, 17050.0f, ImVec2(0, 50));
	ImGui::End();
}

void ui_init()
{
	static bool settings_loaded = false;

	// only load settings when we first start
	// the emulator
	if (settings_loaded == false) {
		ui_load_settings();
		settings_loaded = true;
	}
	for (auto i = 0; i < Cycles_array_size; i++) {
		Cycles_per_frame[i] = 0;
	}
}

void ui_shutdown()
{
	ImGui_ImplSdl_Shutdown();
	Imgui_initialized = false;
	ui_save_settings();
}

void ui_do_frame(SDL_Window *window)
{
	// initlialize imgui if it has not been initialized yet
	if (Imgui_initialized == false) {
		ImGui_ImplSdl_Init(window);
		Imgui_initialized = true;
	}

	ImGui_ImplSdl_NewFrame(window);
	if (Show_main_menu) {
		ui_show_main_menu();
	}
	if (Show_debug_menu) {
		ui_show_debug_menu();
	}
	//ImGui::ShowTestWindow();
	ImGui::Render();
}

void ui_toggle_main_menu()
{
	Show_main_menu = !Show_main_menu;
}

void ui_toggle_debug_menu()
{
	Show_debug_menu = !Show_debug_menu;
}

// since rendering is decoupled from cycles on the machine, extra
// function to store off cycles for debug display
void ui_update_cycle_count()
{
	Cycles_per_frame[Current_cycles_array_entry] = Total_cycles_this_frame;
	Current_cycles_array_entry = (Current_cycles_array_entry + 1) % Cycles_array_size;
}

<commit_msg>small ui improvements<commit_after>/*

MIT License

Copyright (c) 2016 Mark Allender

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

#include <string>
#include <fstream>
#include "imgui/imgui.h"
#include "imgui/imgui_impl_sdl.h"
#include "interface.h"
#include "6502/video.h"
#include "6502/disk.h"
#include "utils/path_utils.h"
#include "apple2emu.h"
#include "nfd.h"

static bool Show_main_menu = false;
static bool Show_debug_menu = false;
static bool Menu_open_at_start = false;
static bool Imgui_initialized = false;

static const int32_t Line_length = 256;
static const char *Settings_filename = "settings.txt";

// settings to be stored
static int32_t Video_color_type = static_cast<int>(video_display_types::MONO_WHITE);

static const uint32_t Cycles_array_size = 64;
static uint32_t Cycles_per_frame[Cycles_array_size];
static uint32_t Current_cycles_array_entry = 0;

// inserts disk image into the given disk drive
static void ui_insert_disk(const char *disk_filename, int slot)
{
	FILE *fp = fopen(disk_filename, "rb");
	if (fp == nullptr) {
		return;
	}
	fclose(fp);

	disk_insert(disk_filename, slot);
}

static void ui_load_settings()
{
	std::ifstream infile(Settings_filename);
	std::string line;

	while(std::getline(infile, line)) {
		int pos = line.find('=');
		if (pos > 0) {
			std::string setting = line.substr(0, pos - 1);
			std::string value = line.substr(pos + 1);
			while (value[0] == ' ') {
				value.erase(0, 1);
			}

			if (setting == "auto_start") {
				int i_val = strtol(value.c_str(), nullptr, 10);
				Auto_start = i_val ? true : false;
			}
			else if (setting == "emulator_type") {
				int i_val = strtol(value.c_str(), nullptr, 10);
				Emulator_type = static_cast<emulator_type>(i_val);
			}
			else if (setting == "open_at_start") {
				int i_val = strtol(value.c_str(), nullptr, 10);
				Menu_open_at_start = i_val ? true : false;
				Show_main_menu = Menu_open_at_start;
			}
			else if (setting == "disk1") {
				ui_insert_disk(value.c_str(), 1);
			}
			else if (setting == "disk2") {
				ui_insert_disk(value.c_str(), 2);
			}
			else if (setting == "video") {
				Video_color_type = (uint8_t)strtol(value.c_str(), nullptr, 10);
				video_set_mono_type(static_cast<video_display_types>(Video_color_type));
			}
			else if (setting == "speed") {
				Speed_multiplier = (int)strtol(value.c_str(), nullptr, 10);
			}
		}
	}
}

static void ui_save_settings()
{
	FILE *fp = fopen(Settings_filename, "wt");
	if (fp == nullptr) {
		printf("Unable to open settings file for writing\n");
		return;
	}
	fprintf(fp, "auto_start = %d\n", Auto_start == true ? 1 : 0);
	fprintf(fp, "emulator_type = %d\n", static_cast<uint8_t>(Emulator_type));
	fprintf(fp, "open_at_start = %d\n", Menu_open_at_start == true ? 1 : 0);
	fprintf(fp, "disk1 = %s\n", disk_get_mounted_filename(1));
	fprintf(fp, "disk2 = %s\n", disk_get_mounted_filename(2));
	fprintf(fp, "video = %d\n", Video_color_type);
	fprintf(fp, "speed = %d\n", Speed_multiplier);

	fclose(fp);
}

static void ui_show_general_options()
{
	ImGui::Text("General Options:");
	ImGui::Checkbox("Auto Start", &Auto_start);
	ImGui::SameLine(200);
	if (Emulator_state == emulator_state::SPLASH_SCREEN) {
		if (ImGui::Button("Start")) {
			Emulator_state = emulator_state::EMULATOR_STARTED;
		}
	} else {
		if (ImGui::Button("Reboot")) {
			reset_machine();
			Show_main_menu = true;
		}
	}
	ImGui::Checkbox("Open Menu on startup", &Menu_open_at_start);
	ImGui::Separator();

	static int type = static_cast<uint8_t>(Emulator_type);
	int old_type = type;
	ImGui::ListBox("Emulation Type", &type, Emulator_names, static_cast<uint8_t>(emulator_type::NUM_EMULATOR_TYPES));

	// if the emulator type changed, then reset the machine (if we haven't
	// started yet.  Otherwise tell user that we need to reset machine
	// for this change to take effect
	if (old_type != type && Emulator_state == emulator_state::SPLASH_SCREEN) {
		Emulator_type = static_cast<emulator_type>(type);
		reset_machine();
	} else {
	}
}

static void ui_get_disk_image(uint8_t slot_num)
{
	nfdchar_t *outPath = NULL;
	nfdresult_t result = NFD_OpenDialog("dsk,do", nullptr, &outPath);

	if (result == NFD_OKAY) {
		disk_insert(outPath, slot_num);
		free(outPath);
	}
}

static void ui_show_disk_menu()
{
	ImGui::Text("Disk Drive Options:");
	ImGui::Spacing();
	ImGui::Spacing();
	ImGui::Text("Slot 6, Disk 1:");
	std::string filename;
	path_utils_get_filename(disk_get_mounted_filename(1), filename);
	if (filename.empty()) {
		filename = "<none>";
	}
	ImGui::SameLine();
	if (ImGui::Button(filename.c_str())) {
		ui_get_disk_image(1);
	}
	ImGui::SameLine();
	if (ImGui::Button("Eject")) {
		disk_eject(1);
	}
	ImGui::Spacing();

	ImGui::Text("Slot 6, Disk 2:");
	path_utils_get_filename(disk_get_mounted_filename(2), filename);
	if (filename.empty()) {
		filename = "<none>";
	}
	ImGui::SameLine();
	if (ImGui::Button(filename.c_str())) {
		ui_get_disk_image(2);
	}
	ImGui::SameLine();
	if (ImGui::Button("Eject")) {
		disk_eject(2);
	}
}

static void ui_show_video_output_menu()
{
	ImGui::Text("Video Output Options:");
	ImGui::RadioButton("White", &Video_color_type, static_cast<uint8_t>(video_display_types::MONO_WHITE));
	ImGui::SameLine();
	ImGui::RadioButton("Amber", &Video_color_type, static_cast<uint8_t>(video_display_types::MONO_AMBER));
	ImGui::SameLine();
	ImGui::RadioButton("Green", &Video_color_type, static_cast<uint8_t>(video_display_types::MONO_GREEN));
	ImGui::SameLine();
	ImGui::RadioButton("Color", &Video_color_type, static_cast<uint8_t>(video_display_types::COLOR));
	video_set_mono_type(static_cast<video_display_types>(Video_color_type));
}

static void ui_show_speed_menu()
{
	if (ImGui::SliderInt("Emulator Speed", (int *)&Speed_multiplier, 1, 100) == true) {
	}
}

static void ui_show_main_menu()
{
	ImGui::Begin("Options", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_ShowBorders);
	ui_show_general_options();
	ImGui::Separator();
	ui_show_video_output_menu();
	ImGui::Separator();
	ui_show_disk_menu();
	ImGui::Separator();
	ui_show_speed_menu();
	ImGui::End();
}

static void ui_show_debug_menu()
{
	static bool animate = true;
	static float cycles[Cycles_array_size];

	ImGui::Begin("Debug");
	ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
	ImGui::Separator();
	ImGui::Checkbox("Animate", &animate);
	if (animate == true) {
		auto index = 0;
		auto i = Current_cycles_array_entry;
		while (true) {
			cycles[index++] = static_cast<float>(Cycles_per_frame[i]);
			i = (i + 1) % Cycles_array_size;
			if (i == Current_cycles_array_entry) {
				break;
			}
		}
	}
	ImGui::PlotLines("Cycles/Frame", cycles, Cycles_array_size, 0, nullptr, 17020.0f, 17050.0f, ImVec2(0, 50));
	ImGui::End();
}

void ui_init()
{
	static bool settings_loaded = false;

	// only load settings when we first start
	// the emulator
	if (settings_loaded == false) {
		ui_load_settings();
		settings_loaded = true;
	}
	for (auto i = 0; i < Cycles_array_size; i++) {
		Cycles_per_frame[i] = 0;
	}
}

void ui_shutdown()
{
	ImGui_ImplSdl_Shutdown();
	Imgui_initialized = false;
	ui_save_settings();
}

void ui_do_frame(SDL_Window *window)
{
	// initlialize imgui if it has not been initialized yet
	if (Imgui_initialized == false) {
		ImGui_ImplSdl_Init(window);

		// maybe use apple 2 font here
		//ImGuiIO& io = ImGui::GetIO();
		//io.Fonts->AddFontFromFileTTF("printchar21.ttf", 8.0f);
		Imgui_initialized = true;
	}

	ImGui_ImplSdl_NewFrame(window);
	if (Show_main_menu) {
		ui_show_main_menu();
	}
	if (Show_debug_menu) {
		ui_show_debug_menu();
	}
	//ImGui::ShowTestWindow();

	if (Show_main_menu || Show_debug_menu) {
		ImGui::Render();
	}
}

void ui_toggle_main_menu()
{
	Show_main_menu = !Show_main_menu;
}

void ui_toggle_debug_menu()
{
	Show_debug_menu = !Show_debug_menu;
}

// since rendering is decoupled from cycles on the machine, extra
// function to store off cycles for debug display
void ui_update_cycle_count()
{
	Cycles_per_frame[Current_cycles_array_entry] = Total_cycles_this_frame;
	Current_cycles_array_entry = (Current_cycles_array_entry + 1) % Cycles_array_size;
}

<|endoftext|>


<commit_before>/* Copyright (c) 2017 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "hdr.hpp"
#include "math.hpp"
#include "application.hpp"

namespace Granite
{
struct FrameEvent : EventHandler
{
	FrameEvent()
	{
		EVENT_MANAGER_REGISTER(FrameEvent, on_frame_time, FrameTickEvent);
	}

	bool on_frame_time(const FrameTickEvent &tick)
	{
		frame_time = float(tick.get_frame_time());
		return true;
	}

	float frame_time = 0.0f;
};

static FrameEvent timer;

static void luminance_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd)
{
	auto &input = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	auto &output = pass.get_graph().get_physical_buffer_resource(pass.get_storage_outputs()[0]->get_physical_index());

	cmd.set_storage_buffer(0, 0, output);
	cmd.set_texture(0, 1, input, Vulkan::StockSampler::LinearClamp);

	unsigned half_width = input.get_image().get_create_info().width / 2;
	unsigned half_height = input.get_image().get_create_info().height / 2;

	auto *program = cmd.get_device().get_shader_manager().register_compute("builtin://shaders/post/luminance.comp");
	unsigned variant = program->register_variant({});
	cmd.set_program(*program->get_program(variant));

	struct Registers
	{
		uvec2 size;
		float lerp;
		float minimum;
		float maximum;
	} push;
	push.size = uvec2(half_width, half_height);
	push.lerp = 1.0f - pow(0.5f, timer.frame_time);
	push.minimum = -2.0f;
	push.maximum = 1.0f;
	cmd.push_constants(&push, 0, sizeof(push));
	cmd.dispatch(1, 1, 1);
}

static void bloom_threshold_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd)
{
	auto &input = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	auto &ubo = pass.get_graph().get_physical_buffer_resource(pass.get_uniform_inputs()[0]->get_physical_index());
	cmd.set_texture(0, 0, input, Vulkan::StockSampler::LinearClamp);
	cmd.set_uniform_buffer(0, 1, ubo);
	Vulkan::CommandBufferUtil::draw_quad(cmd, "builtin://shaders/quad.vert", "builtin://shaders/post/bloom_threshold.frag");
}

static void bloom_downsample_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd, bool feedback)
{
	auto &input = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	cmd.set_texture(0, 0, input, Vulkan::StockSampler::LinearClamp);

	if (feedback)
	{
		auto *feedback_texture = pass.get_graph().get_physical_history_texture_resource(
			pass.get_history_inputs()[0]->get_physical_index());

		if (feedback_texture)
		{
			struct Push
			{
				vec2 inv_size;
				float lerp;
			} push;
			push.inv_size = vec2(1.0f / input.get_image().get_create_info().width,
			                     1.0f / input.get_image().get_create_info().height);

			float lerp = 1.0f - pow(0.001f, timer.frame_time);
			push.lerp = lerp;
			cmd.push_constants(&push, 0, sizeof(push));

			cmd.set_texture(0, 1, *feedback_texture, Vulkan::StockSampler::NearestClamp);
			Vulkan::CommandBufferUtil::draw_quad(cmd,
			                                     "builtin://shaders/quad.vert",
			                                     "builtin://shaders/post/bloom_downsample.frag",
			                                     {{"FEEDBACK", 1}});
		}
		else
		{
			vec2 inv_size = vec2(1.0f / input.get_image().get_create_info().width,
			                     1.0f / input.get_image().get_create_info().height);
			cmd.push_constants(&inv_size, 0, sizeof(inv_size));
			Vulkan::CommandBufferUtil::draw_quad(cmd,
			                                     "builtin://shaders/quad.vert",
			                                     "builtin://shaders/post/bloom_downsample.frag");
		}
	}
	else
	{
		vec2 inv_size = vec2(1.0f / input.get_image().get_create_info().width,
		                     1.0f / input.get_image().get_create_info().height);
		cmd.push_constants(&inv_size, 0, sizeof(inv_size));
		Vulkan::CommandBufferUtil::draw_quad(cmd,
		                                     "builtin://shaders/quad.vert",
		                                     "builtin://shaders/post/bloom_downsample.frag");
	}
}

static void bloom_upsample_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd)
{
	auto &input = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	vec2 inv_size = vec2(1.0f / input.get_image().get_create_info().width, 1.0f / input.get_image().get_create_info().height);
	cmd.push_constants(&inv_size, 0, sizeof(inv_size));
	cmd.set_texture(0, 0, input, Vulkan::StockSampler::LinearClamp);
	Vulkan::CommandBufferUtil::draw_quad(cmd, "builtin://shaders/quad.vert", "builtin://shaders/post/bloom_upsample.frag");
}

static void tonemap_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd)
{
	auto &hdr = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	auto &bloom = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[1]->get_physical_index());
	auto &ubo = pass.get_graph().get_physical_buffer_resource(pass.get_uniform_inputs()[0]->get_physical_index());
	cmd.set_texture(0, 0, hdr, Vulkan::StockSampler::LinearClamp);
	cmd.set_texture(0, 1, bloom, Vulkan::StockSampler::LinearClamp);
	cmd.set_uniform_buffer(0, 2, ubo);
	Vulkan::CommandBufferUtil::draw_quad(cmd, "builtin://shaders/quad.vert", "builtin://shaders/post/tonemap.frag");
}

void setup_hdr_postprocess(RenderGraph &graph, const std::string &input, const std::string &output)
{
	BufferInfo buffer_info;
	buffer_info.size = 3 * sizeof(float);
	buffer_info.persistent = true;
	buffer_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;

	auto &lum = graph.get_buffer_resource("average-luminance");
	lum.set_buffer_info(buffer_info);

	auto &adapt_pass = graph.add_pass("adapt-luminance", VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
	adapt_pass.add_storage_output("average-luminance-updated", buffer_info, "average-luminance");
	adapt_pass.add_texture_input("bloom-downsample-3");
	adapt_pass.set_build_render_pass([&adapt_pass](Vulkan::CommandBuffer &cmd) {
		luminance_build_render_pass(adapt_pass, cmd);
	});

	auto &threshold = graph.add_pass("bloom-threshold", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	AttachmentInfo threshold_info;
	threshold_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	threshold_info.size_x = 0.5f;
	threshold_info.size_y = 0.5f;
	threshold_info.size_class = SizeClass::InputRelative;
	threshold_info.size_relative_name = input;
	threshold.add_color_output("threshold", threshold_info);
	threshold.add_texture_input(input);
	threshold.add_uniform_input("average-luminance");
	threshold.set_build_render_pass([&threshold](Vulkan::CommandBuffer &cmd) {
		bloom_threshold_build_render_pass(threshold, cmd);
	});

	AttachmentInfo blur_info;
	blur_info.size_x = 0.25f;
	blur_info.size_y = 0.25f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	blur_info.size_class = SizeClass::InputRelative;
	blur_info.size_relative_name = input;
	auto &blur0 = graph.add_pass("bloom-downsample-0", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur0.add_color_output("bloom-downsample-0", blur_info);
	blur0.add_texture_input("threshold");
	blur0.set_build_render_pass([&blur0](Vulkan::CommandBuffer &cmd) {
		bloom_downsample_build_render_pass(blur0, cmd, false);
	});

	blur_info.size_x = 0.125f;
	blur_info.size_y = 0.125f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur1 = graph.add_pass("bloom-downsample-1", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur1.add_color_output("bloom-downsample-1", blur_info);
	blur1.add_texture_input("bloom-downsample-0");
	blur1.set_build_render_pass([&blur1](Vulkan::CommandBuffer &cmd) {
		bloom_downsample_build_render_pass(blur1, cmd, false);
	});

	blur_info.size_x = 0.0625f;
	blur_info.size_y = 0.0625f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur2 = graph.add_pass("bloom-downsample-2", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur2.add_color_output("bloom-downsample-2", blur_info);
	blur2.add_texture_input("bloom-downsample-1");
	blur2.set_build_render_pass([&blur2](Vulkan::CommandBuffer &cmd) {
		bloom_downsample_build_render_pass(blur2, cmd, false);
	});

	blur_info.size_x = 0.03125f;
	blur_info.size_y = 0.03125f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur3 = graph.add_pass("bloom-downsample-3", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur3.add_color_output("bloom-downsample-3", blur_info);
	blur3.add_texture_input("bloom-downsample-2");
	blur3.add_history_input("bloom-downsample-3");
	blur3.set_build_render_pass([&blur3](Vulkan::CommandBuffer &cmd) {
		bloom_downsample_build_render_pass(blur3, cmd, true);
	});

	blur_info.size_x = 0.0625f;
	blur_info.size_y = 0.0625f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur4 = graph.add_pass("bloom-upsample-0", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur4.add_color_output("bloom-upsample-0", blur_info);
	blur4.add_texture_input("bloom-downsample-3");
	blur4.set_build_render_pass([&blur4](Vulkan::CommandBuffer &cmd) {
		bloom_upsample_build_render_pass(blur4, cmd);
	});

	blur_info.size_x = 0.125f;
	blur_info.size_y = 0.125f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur5 = graph.add_pass("bloom-upsample-1", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur5.add_color_output("bloom-upsample-1", blur_info);
	blur5.add_texture_input("bloom-upsample-0");
	blur5.set_build_render_pass([&blur5](Vulkan::CommandBuffer &cmd) {
		bloom_upsample_build_render_pass(blur5, cmd);
	});

	blur_info.size_x = 0.25f;
	blur_info.size_y = 0.25f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur6 = graph.add_pass("bloom-upsample-2", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur6.add_color_output("bloom-upsample-2", blur_info);
	blur6.add_texture_input("bloom-upsample-1");
	blur6.set_build_render_pass([&blur6](Vulkan::CommandBuffer &cmd) {
		bloom_upsample_build_render_pass(blur6, cmd);
	});

	AttachmentInfo tonemap_info;
	tonemap_info.size_class = SizeClass::InputRelative;
	tonemap_info.size_relative_name = input;
	auto &tonemap = graph.add_pass("tonemap", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	tonemap.add_color_output(output, tonemap_info);
	tonemap.add_texture_input(input);
	tonemap.add_texture_input("bloom-upsample-2");
	tonemap.add_uniform_input("average-luminance-updated");
	tonemap.set_build_render_pass([&tonemap](Vulkan::CommandBuffer &cmd) {
		tonemap_build_render_pass(tonemap, cmd);
	});
}
}
<commit_msg>Change luminance range.<commit_after>/* Copyright (c) 2017 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "hdr.hpp"
#include "math.hpp"
#include "application.hpp"

namespace Granite
{
struct FrameEvent : EventHandler
{
	FrameEvent()
	{
		EVENT_MANAGER_REGISTER(FrameEvent, on_frame_time, FrameTickEvent);
	}

	bool on_frame_time(const FrameTickEvent &tick)
	{
		frame_time = float(tick.get_frame_time());
		return true;
	}

	float frame_time = 0.0f;
};

static FrameEvent timer;

static void luminance_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd)
{
	auto &input = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	auto &output = pass.get_graph().get_physical_buffer_resource(pass.get_storage_outputs()[0]->get_physical_index());

	cmd.set_storage_buffer(0, 0, output);
	cmd.set_texture(0, 1, input, Vulkan::StockSampler::LinearClamp);

	unsigned half_width = input.get_image().get_create_info().width / 2;
	unsigned half_height = input.get_image().get_create_info().height / 2;

	auto *program = cmd.get_device().get_shader_manager().register_compute("builtin://shaders/post/luminance.comp");
	unsigned variant = program->register_variant({});
	cmd.set_program(*program->get_program(variant));

	struct Registers
	{
		uvec2 size;
		float lerp;
		float minimum;
		float maximum;
	} push;
	push.size = uvec2(half_width, half_height);
	push.lerp = 1.0f - pow(0.5f, timer.frame_time);
	push.minimum = -5.0f;
	push.maximum = 4.0f;
	cmd.push_constants(&push, 0, sizeof(push));
	cmd.dispatch(1, 1, 1);
}

static void bloom_threshold_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd)
{
	auto &input = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	auto &ubo = pass.get_graph().get_physical_buffer_resource(pass.get_uniform_inputs()[0]->get_physical_index());
	cmd.set_texture(0, 0, input, Vulkan::StockSampler::LinearClamp);
	cmd.set_uniform_buffer(0, 1, ubo);
	Vulkan::CommandBufferUtil::draw_quad(cmd, "builtin://shaders/quad.vert", "builtin://shaders/post/bloom_threshold.frag");
}

static void bloom_downsample_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd, bool feedback)
{
	auto &input = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	cmd.set_texture(0, 0, input, Vulkan::StockSampler::LinearClamp);

	if (feedback)
	{
		auto *feedback_texture = pass.get_graph().get_physical_history_texture_resource(
			pass.get_history_inputs()[0]->get_physical_index());

		if (feedback_texture)
		{
			struct Push
			{
				vec2 inv_size;
				float lerp;
			} push;
			push.inv_size = vec2(1.0f / input.get_image().get_create_info().width,
			                     1.0f / input.get_image().get_create_info().height);

			float lerp = 1.0f - pow(0.001f, timer.frame_time);
			push.lerp = lerp;
			cmd.push_constants(&push, 0, sizeof(push));

			cmd.set_texture(0, 1, *feedback_texture, Vulkan::StockSampler::NearestClamp);
			Vulkan::CommandBufferUtil::draw_quad(cmd,
			                                     "builtin://shaders/quad.vert",
			                                     "builtin://shaders/post/bloom_downsample.frag",
			                                     {{"FEEDBACK", 1}});
		}
		else
		{
			vec2 inv_size = vec2(1.0f / input.get_image().get_create_info().width,
			                     1.0f / input.get_image().get_create_info().height);
			cmd.push_constants(&inv_size, 0, sizeof(inv_size));
			Vulkan::CommandBufferUtil::draw_quad(cmd,
			                                     "builtin://shaders/quad.vert",
			                                     "builtin://shaders/post/bloom_downsample.frag");
		}
	}
	else
	{
		vec2 inv_size = vec2(1.0f / input.get_image().get_create_info().width,
		                     1.0f / input.get_image().get_create_info().height);
		cmd.push_constants(&inv_size, 0, sizeof(inv_size));
		Vulkan::CommandBufferUtil::draw_quad(cmd,
		                                     "builtin://shaders/quad.vert",
		                                     "builtin://shaders/post/bloom_downsample.frag");
	}
}

static void bloom_upsample_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd)
{
	auto &input = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	vec2 inv_size = vec2(1.0f / input.get_image().get_create_info().width, 1.0f / input.get_image().get_create_info().height);
	cmd.push_constants(&inv_size, 0, sizeof(inv_size));
	cmd.set_texture(0, 0, input, Vulkan::StockSampler::LinearClamp);
	Vulkan::CommandBufferUtil::draw_quad(cmd, "builtin://shaders/quad.vert", "builtin://shaders/post/bloom_upsample.frag");
}

static void tonemap_build_render_pass(RenderPass &pass, Vulkan::CommandBuffer &cmd)
{
	auto &hdr = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[0]->get_physical_index());
	auto &bloom = pass.get_graph().get_physical_texture_resource(pass.get_texture_inputs()[1]->get_physical_index());
	auto &ubo = pass.get_graph().get_physical_buffer_resource(pass.get_uniform_inputs()[0]->get_physical_index());
	cmd.set_texture(0, 0, hdr, Vulkan::StockSampler::LinearClamp);
	cmd.set_texture(0, 1, bloom, Vulkan::StockSampler::LinearClamp);
	cmd.set_uniform_buffer(0, 2, ubo);
	Vulkan::CommandBufferUtil::draw_quad(cmd, "builtin://shaders/quad.vert", "builtin://shaders/post/tonemap.frag");
}

void setup_hdr_postprocess(RenderGraph &graph, const std::string &input, const std::string &output)
{
	BufferInfo buffer_info;
	buffer_info.size = 3 * sizeof(float);
	buffer_info.persistent = true;
	buffer_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;

	auto &lum = graph.get_buffer_resource("average-luminance");
	lum.set_buffer_info(buffer_info);

	auto &adapt_pass = graph.add_pass("adapt-luminance", VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
	adapt_pass.add_storage_output("average-luminance-updated", buffer_info, "average-luminance");
	adapt_pass.add_texture_input("bloom-downsample-3");
	adapt_pass.set_build_render_pass([&adapt_pass](Vulkan::CommandBuffer &cmd) {
		luminance_build_render_pass(adapt_pass, cmd);
	});

	auto &threshold = graph.add_pass("bloom-threshold", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	AttachmentInfo threshold_info;
	threshold_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	threshold_info.size_x = 0.5f;
	threshold_info.size_y = 0.5f;
	threshold_info.size_class = SizeClass::InputRelative;
	threshold_info.size_relative_name = input;
	threshold.add_color_output("threshold", threshold_info);
	threshold.add_texture_input(input);
	threshold.add_uniform_input("average-luminance");
	threshold.set_build_render_pass([&threshold](Vulkan::CommandBuffer &cmd) {
		bloom_threshold_build_render_pass(threshold, cmd);
	});

	AttachmentInfo blur_info;
	blur_info.size_x = 0.25f;
	blur_info.size_y = 0.25f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	blur_info.size_class = SizeClass::InputRelative;
	blur_info.size_relative_name = input;
	auto &blur0 = graph.add_pass("bloom-downsample-0", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur0.add_color_output("bloom-downsample-0", blur_info);
	blur0.add_texture_input("threshold");
	blur0.set_build_render_pass([&blur0](Vulkan::CommandBuffer &cmd) {
		bloom_downsample_build_render_pass(blur0, cmd, false);
	});

	blur_info.size_x = 0.125f;
	blur_info.size_y = 0.125f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur1 = graph.add_pass("bloom-downsample-1", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur1.add_color_output("bloom-downsample-1", blur_info);
	blur1.add_texture_input("bloom-downsample-0");
	blur1.set_build_render_pass([&blur1](Vulkan::CommandBuffer &cmd) {
		bloom_downsample_build_render_pass(blur1, cmd, false);
	});

	blur_info.size_x = 0.0625f;
	blur_info.size_y = 0.0625f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur2 = graph.add_pass("bloom-downsample-2", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur2.add_color_output("bloom-downsample-2", blur_info);
	blur2.add_texture_input("bloom-downsample-1");
	blur2.set_build_render_pass([&blur2](Vulkan::CommandBuffer &cmd) {
		bloom_downsample_build_render_pass(blur2, cmd, false);
	});

	blur_info.size_x = 0.03125f;
	blur_info.size_y = 0.03125f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur3 = graph.add_pass("bloom-downsample-3", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur3.add_color_output("bloom-downsample-3", blur_info);
	blur3.add_texture_input("bloom-downsample-2");
	blur3.add_history_input("bloom-downsample-3");
	blur3.set_build_render_pass([&blur3](Vulkan::CommandBuffer &cmd) {
		bloom_downsample_build_render_pass(blur3, cmd, true);
	});

	blur_info.size_x = 0.0625f;
	blur_info.size_y = 0.0625f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur4 = graph.add_pass("bloom-upsample-0", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur4.add_color_output("bloom-upsample-0", blur_info);
	blur4.add_texture_input("bloom-downsample-3");
	blur4.set_build_render_pass([&blur4](Vulkan::CommandBuffer &cmd) {
		bloom_upsample_build_render_pass(blur4, cmd);
	});

	blur_info.size_x = 0.125f;
	blur_info.size_y = 0.125f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur5 = graph.add_pass("bloom-upsample-1", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur5.add_color_output("bloom-upsample-1", blur_info);
	blur5.add_texture_input("bloom-upsample-0");
	blur5.set_build_render_pass([&blur5](Vulkan::CommandBuffer &cmd) {
		bloom_upsample_build_render_pass(blur5, cmd);
	});

	blur_info.size_x = 0.25f;
	blur_info.size_y = 0.25f;
	blur_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	auto &blur6 = graph.add_pass("bloom-upsample-2", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	blur6.add_color_output("bloom-upsample-2", blur_info);
	blur6.add_texture_input("bloom-upsample-1");
	blur6.set_build_render_pass([&blur6](Vulkan::CommandBuffer &cmd) {
		bloom_upsample_build_render_pass(blur6, cmd);
	});

	AttachmentInfo tonemap_info;
	tonemap_info.size_class = SizeClass::InputRelative;
	tonemap_info.size_relative_name = input;
	auto &tonemap = graph.add_pass("tonemap", VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
	tonemap.add_color_output(output, tonemap_info);
	tonemap.add_texture_input(input);
	tonemap.add_texture_input("bloom-upsample-2");
	tonemap.add_uniform_input("average-luminance-updated");
	tonemap.set_build_render_pass([&tonemap](Vulkan::CommandBuffer &cmd) {
		tonemap_build_render_pass(tonemap, cmd);
	});
}
}
<|endoftext|>


<commit_before>/**
 * The Seeks proxy and plugin framework are part of the SEEKS project.
 * Copyright (C) 2010 Emmanuel Benazera, ebenazer@seeks-project.info
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "user_db.h"
#include "seeks_proxy.h"
#include "proxy_configuration.h"
#include "errlog.h"
#include "plugin_manager.h"

#include <iostream>
#include <stdlib.h>

using namespace sp;

int main(int argc, char **argv)
{
   if (argc < 3)
     {
	std::cout << "Usage: <db_file> <seeks base dir>\n";
	exit(0);
     }

   std::string dbfile = argv[1];
   std::string basedir = argv[2];

   seeks_proxy::_configfile = "config";

   seeks_proxy::_configfile = basedir + "/config";

   seeks_proxy::initialize_mutexes();
   errlog::init_log_module();
   errlog::set_debug_level(LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO);

   seeks_proxy::_basedir = basedir.c_str();
   plugin_manager::_plugin_repository = basedir + "/plugins/";
   seeks_proxy::_config = new proxy_configuration(seeks_proxy::_configfile);

   seeks_proxy::_user_db = new user_db(dbfile);
   seeks_proxy::_user_db->open_db_readonly();

   plugin_manager::load_all_plugins();
   plugin_manager::instanciate_plugins();

   seeks_proxy::_user_db->print(std::cout);

   seeks_proxy::_user_db->close_db();
}
<commit_msg>fixed db print config file location<commit_after>/**
 * The Seeks proxy and plugin framework are part of the SEEKS project.
 * Copyright (C) 2010 Emmanuel Benazera, ebenazer@seeks-project.info
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "user_db.h"
#include "seeks_proxy.h"
#include "proxy_configuration.h"
#include "errlog.h"
#include "plugin_manager.h"

#include <iostream>
#include <stdlib.h>

using namespace sp;

int main(int argc, char **argv)
{
   if (argc < 3)
     {
	std::cout << "Usage: <db_file> <seeks base dir>\n";
	exit(0);
     }

   std::string dbfile = argv[1];
   std::string basedir = argv[2];

   seeks_proxy::_configfile = basedir + "/config";

   seeks_proxy::initialize_mutexes();
   errlog::init_log_module();
   errlog::set_debug_level(LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO);

   seeks_proxy::_basedir = basedir.c_str();
   plugin_manager::_plugin_repository = basedir + "/plugins/";
   seeks_proxy::_config = new proxy_configuration(seeks_proxy::_configfile);

   seeks_proxy::_user_db = new user_db(dbfile);
   seeks_proxy::_user_db->open_db_readonly();

   plugin_manager::load_all_plugins();
   plugin_manager::instanciate_plugins();

   seeks_proxy::_user_db->print(std::cout);

   seeks_proxy::_user_db->close_db();
}
<|endoftext|>


<commit_before>/*
    This file is part of libkpimexchange
    Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include <qlayout.h>
#include <qlabel.h>
#include <qcombobox.h>

#include <klocale.h>
#include <kmessagebox.h>
#include <kapplication.h>
#include <kglobal.h>
#include <kconfig.h>
#include <kstandarddirs.h>
#include <ksimpleconfig.h>

#include "exchangeprogress.h"
using namespace KPIM;

ExchangeProgress::ExchangeProgress(QWidget *parent)
  : KProgressDialog(parent, i18n("Exchange Download Progress"), i18n("Exchange Plugin"), "text" )
{
  m_finished = 0;
  m_total = 0;
  setAutoClose( false );
  setLabel( i18n( "Listing appointments" ) );
}

ExchangeProgress::~ExchangeProgress()
{
}

void ExchangeProgress::slotTransferStarted()
{
  m_total++;
  progressBar()->setTotalSteps( m_total );
  updateLabel();
}

void ExchangeProgress::slotTransferFinished()
{
  m_finished++;
  updateLabel();
  if ( m_finished == m_total ) {
    emit complete( this );
  }
}

void ExchangeProgress::updateLabel()
{
  progressBar()->setValue( m_finished );
  QString str = QString( i18n( "Downloading, %1 of %2" ) ).arg( m_finished ).arg( m_total );
  setLabel( str );
}

#include "exchangeprogress.moc"
<commit_msg>QString( i18n ( ) ) ? No way !<commit_after>/*
    This file is part of libkpimexchange
    Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include <qlayout.h>
#include <qlabel.h>
#include <qcombobox.h>

#include <klocale.h>
#include <kmessagebox.h>
#include <kapplication.h>
#include <kglobal.h>
#include <kconfig.h>
#include <kstandarddirs.h>
#include <ksimpleconfig.h>

#include "exchangeprogress.h"
using namespace KPIM;

ExchangeProgress::ExchangeProgress(QWidget *parent)
  : KProgressDialog(parent, i18n("Exchange Download Progress"), i18n("Exchange Plugin"), "text" )
{
  m_finished = 0;
  m_total = 0;
  setAutoClose( false );
  setLabel( i18n( "Listing appointments" ) );
}

ExchangeProgress::~ExchangeProgress()
{
}

void ExchangeProgress::slotTransferStarted()
{
  m_total++;
  progressBar()->setTotalSteps( m_total );
  updateLabel();
}

void ExchangeProgress::slotTransferFinished()
{
  m_finished++;
  updateLabel();
  if ( m_finished == m_total ) {
    emit complete( this );
  }
}

void ExchangeProgress::updateLabel()
{
  progressBar()->setValue( m_finished );
  QString str = i18n( "Downloading, %1 of %2" ).arg( m_finished ).arg( m_total );
  setLabel( str );
}

#include "exchangeprogress.moc"
<|endoftext|>


<commit_before>#include "task_synthetic_shapes.h"
#include "libnanocv/loss.h"
#include "libnanocv/util/math.hpp"
#include "libnanocv/util/random.hpp"

namespace ncv
{
        synthetic_shapes_task_t::synthetic_shapes_task_t(const string_t& configuration)
                :       task_t(configuration),
                        m_rows(math::clamp(text::from_params<size_t>(configuration, "rows", 32), 16, 32)),
                        m_cols(math::clamp(text::from_params<size_t>(configuration, "cols", 32), 16, 32)),
                        m_outputs(math::clamp(text::from_params<size_t>(configuration, "dims", 4), 2, 16)),
                        m_folds(1),
                        m_color(text::from_params<color_mode>(configuration, "color", color_mode::rgba)),
                        m_size(math::clamp(text::from_params<size_t>(configuration, "size", 1024), 256, 16 * 1024))
        {
        }

        namespace
        {
                rgba_t make_transparent_color()
                {
                        return 0;
                }

                rgba_t make_light_color()
                {
                        random_t<rgba_t> rng_red(175, 255);
                        random_t<rgba_t> rng_green(175, 255);
                        random_t<rgba_t> rng_blue(175, 255);

                        return color::make_rgba(rng_red(), rng_green(), rng_blue());
                }

                rgba_t make_dark_color()
                {
                        random_t<rgba_t> rng_red(0, 125);
                        random_t<rgba_t> rng_green(0, 125);
                        random_t<rgba_t> rng_blue(0, 125);

                        return color::make_rgba(rng_red(), rng_green(), rng_blue());
                }

                rect_t make_rect(coord_t rows, coord_t cols)
                {
                        random_t<coord_t> rng(2, std::min(rows / 4, cols / 4));

                        const coord_t dx = rng();
                        const coord_t dy = rng();
                        const coord_t dw = rng();
                        const coord_t dh = rng();

                        return rect_t(dx, dy, cols - dx - dw, rows - dy - dh);
                }

                rect_t make_interior_rect(coord_t x, coord_t y, coord_t w, coord_t h)
                {
                        random_t<coord_t> rng(3, std::min(w / 4, h / 4));

                        const coord_t dx = rng();
                        const coord_t dy = rng();
                        const coord_t dw = rng();
                        const coord_t dh = rng();

                        return rect_t(x + dx, y + dy, w - dx - dw, h - dy - dh);
                }

                rect_t make_interior_rect(const rect_t& rect)
                {
                        return make_interior_rect(rect.left(), rect.top(), rect.width(), rect.height());
                }

                image_t make_filled_rect(coord_t rows, coord_t cols, rgba_t fill_color)
                {
                        const rect_t rect = make_rect(rows, cols);

                        image_t image(rows, cols, color_mode::rgba);

                        image.fill(make_transparent_color());
                        image.fill(rect, fill_color);

                        return image;
                }

                image_t make_hollow_rect(coord_t rows, coord_t cols, rgba_t fill_color)
                {
                        const rect_t rect = make_rect(rows, cols);

                        image_t image(rows, cols, color_mode::rgba);

                        image.fill(make_transparent_color());
                        image.fill(rect, fill_color);
                        image.fill(make_interior_rect(rect), make_transparent_color());

                        return image;
                }

                image_t make_filled_ellipse(coord_t rows, coord_t cols, rgba_t fill_color)
                {
                        const rect_t rect = make_rect(rows, cols);

                        image_t image(rows, cols, color_mode::rgba);

                        image.fill(make_transparent_color());
                        image.fill_ellipse(rect, fill_color);

                        return image;
                }

                image_t make_hollow_ellipse(coord_t rows, coord_t cols, rgba_t fill_color)
                {
                        const rect_t rect = make_rect(rows, cols);

                        image_t image(rows, cols, color_mode::rgba);

                        image.fill(make_transparent_color());
                        image.fill_ellipse(rect, fill_color);
                        image.fill_ellipse(make_interior_rect(rect), make_transparent_color());

                        return image;
                }
        }

        bool synthetic_shapes_task_t::load(const string_t &)
        {
                random_t<size_t> rng_protocol(1, 10);
                random_t<size_t> rng_output(1, osize());

                random_t<scalar_t> rng_gauss(scalar_t(1), math::cast<scalar_t>(icols() + irows()) / scalar_t(8));

                const coord_t rows = static_cast<coord_t>(irows());
                const coord_t cols = static_cast<coord_t>(icols());

                clear_memory(0);

                for (size_t f = 0; f < fsize(); f ++)
                {
                        for (size_t i = 0; i < m_size; i ++)
                        {
                                // random protocol: train vs. test
                                const protocol p = (rng_protocol() < 9) ? protocol::train : protocol::test;

                                // random output class: #dots
                                const size_t o = rng_output();

                                const bool is_dark_background = (rng_protocol() % 2) == 0;
                                const rgba_t back_color = is_dark_background ? make_dark_color() : make_light_color();
                                const rgba_t shape_color = is_dark_background ? make_light_color() : make_dark_color();

                                // generate random image background
                                image_t image(irows(), icols(), color());
                                image.fill(back_color);
                                image.random_noise(color_channel::rgba, -25.0, +25.0, rng_gauss());

                                // generate random shapes
                                image_t shape;

                                switch (o)
                                {
                                case 1:         shape = make_filled_rect(rows, cols, shape_color); break;
                                case 2:         shape = make_hollow_rect(rows, cols, shape_color); break;
                                case 3:         shape = make_filled_ellipse(rows, cols, shape_color); break;
                                case 4:         shape = make_hollow_ellipse(rows, cols, shape_color); break;
                                default:        break;
                                }

                                shape.random_noise(color_channel::rgba, -25.0, +25.0, rng_gauss() * 0.5);
                                image.alpha_blend(shape.rgba());

                                add_image(image);

                                // generate sample
                                sample_t sample(n_images() - 1, sample_region(0, 0));
                                switch (o)
                                {
                                case 1:         sample.m_label = "filled_rectangle"; break;
                                case 2:         sample.m_label = "hollow_rectangle"; break;
                                case 3:         sample.m_label = "filled_ellipse"; break;
                                case 4:         sample.m_label = "hollow_ellipse"; break;
                                default:        sample.m_label = "unkown"; break;
                                }

                                sample.m_target = ncv::class_target(o - 1, osize());
                                sample.m_fold = {f, p};
                                add_sample(sample);
                        }
                }

                return true;
        }
}
<commit_msg>fix some synthetic ellipses<commit_after>#include "task_synthetic_shapes.h"
#include "libnanocv/loss.h"
#include "libnanocv/util/math.hpp"
#include "libnanocv/util/random.hpp"

namespace ncv
{
        synthetic_shapes_task_t::synthetic_shapes_task_t(const string_t& configuration)
                :       task_t(configuration),
                        m_rows(math::clamp(text::from_params<size_t>(configuration, "rows", 32), 16, 32)),
                        m_cols(math::clamp(text::from_params<size_t>(configuration, "cols", 32), 16, 32)),
                        m_outputs(math::clamp(text::from_params<size_t>(configuration, "dims", 4), 2, 16)),
                        m_folds(1),
                        m_color(text::from_params<color_mode>(configuration, "color", color_mode::rgba)),
                        m_size(math::clamp(text::from_params<size_t>(configuration, "size", 1024), 256, 16 * 1024))
        {
        }

        namespace
        {
                rgba_t make_transparent_color()
                {
                        return 0;
                }

                rgba_t make_light_color()
                {
                        random_t<rgba_t> rng_red(175, 255);
                        random_t<rgba_t> rng_green(175, 255);
                        random_t<rgba_t> rng_blue(175, 255);

                        return color::make_rgba(rng_red(), rng_green(), rng_blue());
                }

                rgba_t make_dark_color()
                {
                        random_t<rgba_t> rng_red(0, 125);
                        random_t<rgba_t> rng_green(0, 125);
                        random_t<rgba_t> rng_blue(0, 125);

                        return color::make_rgba(rng_red(), rng_green(), rng_blue());
                }

                rect_t make_rect(coord_t rows, coord_t cols)
                {
                        random_t<coord_t> rng(3, std::min(rows / 4, cols / 4));

                        const coord_t dx = rng();
                        const coord_t dy = rng();
                        const coord_t dw = rng();
                        const coord_t dh = rng();

                        return rect_t(dx, dy, cols - dx - dw, rows - dy - dh);
                }

                rect_t make_interior_rect(coord_t x, coord_t y, coord_t w, coord_t h)
                {
                        random_t<coord_t> rng(4, std::min(w / 4, h / 4));

                        const coord_t dx = rng();
                        const coord_t dy = rng();
                        const coord_t dw = rng();
                        const coord_t dh = rng();

                        return rect_t(x + dx, y + dy, w - dx - dw, h - dy - dh);
                }

                rect_t make_interior_rect(const rect_t& rect)
                {
                        return make_interior_rect(rect.left(), rect.top(), rect.width(), rect.height());
                }

                image_t make_filled_rect(coord_t rows, coord_t cols, rgba_t fill_color)
                {
                        const rect_t rect = make_rect(rows, cols);

                        image_t image(rows, cols, color_mode::rgba);

                        image.fill(make_transparent_color());
                        image.fill(rect, fill_color);

                        return image;
                }

                image_t make_hollow_rect(coord_t rows, coord_t cols, rgba_t fill_color)
                {
                        const rect_t rect = make_rect(rows, cols);

                        image_t image(rows, cols, color_mode::rgba);

                        image.fill(make_transparent_color());
                        image.fill(rect, fill_color);
                        image.fill(make_interior_rect(rect), make_transparent_color());

                        return image;
                }

                image_t make_filled_ellipse(coord_t rows, coord_t cols, rgba_t fill_color)
                {
                        const rect_t rect = make_rect(rows, cols);

                        image_t image(rows, cols, color_mode::rgba);

                        image.fill(make_transparent_color());
                        image.fill_ellipse(rect, fill_color);

                        return image;
                }

                image_t make_hollow_ellipse(coord_t rows, coord_t cols, rgba_t fill_color)
                {
                        const rect_t rect = make_rect(rows, cols);

                        image_t image(rows, cols, color_mode::rgba);

                        image.fill(make_transparent_color());
                        image.fill_ellipse(rect, fill_color);
                        image.fill_ellipse(make_interior_rect(rect), make_transparent_color());

                        return image;
                }
        }

        bool synthetic_shapes_task_t::load(const string_t &)
        {
                random_t<size_t> rng_protocol(1, 10);
                random_t<size_t> rng_output(1, osize());

                random_t<scalar_t> rng_gauss(scalar_t(0.5), math::cast<scalar_t>(icols() + irows()) / scalar_t(8));

                const coord_t rows = static_cast<coord_t>(irows());
                const coord_t cols = static_cast<coord_t>(icols());

                clear_memory(0);

                for (size_t f = 0; f < fsize(); f ++)
                {
                        for (size_t i = 0; i < m_size; i ++)
                        {
                                // random protocol: train vs. test
                                const protocol p = (rng_protocol() < 9) ? protocol::train : protocol::test;

                                // random output class: #dots
                                const size_t o = rng_output();

                                const bool is_dark_background = (rng_protocol() % 2) == 0;
                                const rgba_t back_color = is_dark_background ? make_dark_color() : make_light_color();
                                const rgba_t shape_color = is_dark_background ? make_light_color() : make_dark_color();

                                // generate random image background
                                image_t image(irows(), icols(), color());
                                image.fill(back_color);
                                image.random_noise(color_channel::rgba, -25.0, +25.0, rng_gauss());

                                // generate random shapes
                                image_t shape;

                                switch (o)
                                {
                                case 1:         shape = make_filled_rect(rows, cols, shape_color); break;
                                case 2:         shape = make_hollow_rect(rows, cols, shape_color); break;
                                case 3:         shape = make_filled_ellipse(rows, cols, shape_color); break;
                                case 4:         shape = make_hollow_ellipse(rows, cols, shape_color); break;
                                default:        break;
                                }

                                shape.random_noise(color_channel::rgba, -25.0, +25.0, rng_gauss() * 0.5);
                                image.alpha_blend(shape.rgba());

                                add_image(image);

                                // generate sample
                                sample_t sample(n_images() - 1, sample_region(0, 0));
                                switch (o)
                                {
                                case 1:         sample.m_label = "filled_rectangle"; break;
                                case 2:         sample.m_label = "hollow_rectangle"; break;
                                case 3:         sample.m_label = "filled_ellipse"; break;
                                case 4:         sample.m_label = "hollow_ellipse"; break;
                                default:        sample.m_label = "unkown"; break;
                                }

                                sample.m_target = ncv::class_target(o - 1, osize());
                                sample.m_fold = {f, p};
                                add_sample(sample);
                        }
                }

                return true;
        }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include <algorithm>
#include <atomic>

#include <actionlib/server/simple_action_server.h>
#include <controller_manager/controller_manager.h>
#include <franka/exception.h>
#include <franka/robot.h>
#include <franka_hw/franka_hw.h>
#include <franka_hw/services.h>
#include <franka_msgs/ErrorRecoveryAction.h>
#include <ros/ros.h>
#include <std_srvs/Trigger.h>

using franka_hw::ServiceContainer;

int main(int argc, char** argv) {
  ros::init(argc, argv, "franka_control_node");

  ros::NodeHandle public_node_handle;
  ros::NodeHandle node_handle("~");

  franka_hw::FrankaHW franka_control;
  if (!franka_control.init(public_node_handle, node_handle)) {
    ROS_ERROR("franka_control_node: Failed to initialize FrankaHW class. Shutting down!");
    return 1;
  }

  auto services = std::make_unique<ServiceContainer>();
  std::unique_ptr<actionlib::SimpleActionServer<franka_msgs::ErrorRecoveryAction>>
      recovery_action_server;

  std::atomic_bool has_error(false);

  auto disconnect = [&](std_srvs::Trigger::Request& request,
                        std_srvs::Trigger::Response& response) -> bool {
    if (franka_control.controllerActive()) {
      response.success = false;
      response.message = "Controller is active. Cannont disconnect while a controller is running.";
      return true;
    }
    response.success = true;
    response.message = "";
    services.reset();
    recovery_action_server.reset();
    return franka_control.disconnect();
  };

  auto connect = [&](std_srvs::Trigger::Request& request,
                     std_srvs::Trigger::Response& response) -> bool {
    if (franka_control.connected()) {
      response.success = false;
      response.message = "Already conneceted to robot. Cannot connect twice.";
      return true;
    }
    franka_control.connect();
    std::lock_guard<std::mutex> lock(franka_control.robotMutex());
    auto& robot = franka_control.robot();

    // ServiceContainer services;
    franka_hw::setupServices(robot, franka_control.robotMutex(), node_handle, *services);

    recovery_action_server =
        std::make_unique<actionlib::SimpleActionServer<franka_msgs::ErrorRecoveryAction>>(
            node_handle, "error_recovery",
            [&](const franka_msgs::ErrorRecoveryGoalConstPtr&) {
              try {
                std::lock_guard<std::mutex> lock(franka_control.robotMutex());
                robot.automaticErrorRecovery();
                has_error = false;
                recovery_action_server->setSucceeded();
                ROS_INFO("Recovered from error");
              } catch (const franka::Exception& ex) {
                recovery_action_server->setAborted(franka_msgs::ErrorRecoveryResult(), ex.what());
              }
            },
            false);

    recovery_action_server->start();

    // Initialize robot state before loading any controller
    franka_control.update(robot.readOnce());

    response.success = true;
    response.message = "";
    return true;
  };

  std_srvs::Trigger::Request request;
  std_srvs::Trigger::Response response;
  if (!connect(request, response)) {
    ROS_ERROR("franka_control_node: Initial connect failed. Shutting down.");
    return 1;
  }

  ros::ServiceServer connectServer =
      node_handle.advertiseService<std_srvs::Trigger::Request, std_srvs::Trigger::Response>(
          "connect", connect);
  ros::ServiceServer disconnectServer =
      node_handle.advertiseService<std_srvs::Trigger::Request, std_srvs::Trigger::Response>(
          "disconnect", disconnect);

  controller_manager::ControllerManager control_manager(&franka_control, public_node_handle);

  // Start background threads for message handling
  ros::AsyncSpinner spinner(4);
  spinner.start();

  while (ros::ok()) {
    ros::Time last_time = ros::Time::now();

    // Wait until controller has been activated or error has been recovered
    while (!franka_control.controllerActive() || has_error) {
      if (franka_control.connected()) {
        std::lock_guard<std::mutex> lock(franka_control.robotMutex());
        franka_control.update(franka_control.robot().readOnce());
      }
      ros::Time now = ros::Time::now();
      control_manager.update(now, now - last_time);
      franka_control.checkJointLimits();
      last_time = now;

      if (!ros::ok()) {
        return 0;
      }
    }

    if (franka_control.connected()) {
      try {
        // Run control loop. Will exit if the controller is switched.
        franka_control.control([&](const ros::Time& now, const ros::Duration& period) {
          if (period.toSec() == 0.0) {
            // Reset controllers before starting a motion
            control_manager.update(now, period, true);
            franka_control.checkJointLimits();
            franka_control.reset();
          } else {
            control_manager.update(now, period);
            franka_control.checkJointLimits();
            franka_control.enforceLimits(period);
          }
          return ros::ok();
        });
      } catch (const franka::ControlException& e) {
        ROS_ERROR("%s", e.what());
        has_error = true;
      }
    }
    ros::Duration(0.001).sleep();
  }

  return 0;
}
<commit_msg>fixed freezing issues, debug prints<commit_after>// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include <algorithm>
#include <atomic>
#include <thread>
#include <chrono>

#include <actionlib/server/simple_action_server.h>
#include <controller_manager/controller_manager.h>
#include <franka/exception.h>
#include <franka/robot.h>
#include <franka_hw/franka_hw.h>
#include <franka_hw/services.h>
#include <franka_msgs/ErrorRecoveryAction.h>
#include <ros/ros.h>
#include <std_srvs/Trigger.h>

using franka_hw::ServiceContainer;
using namespace std::chrono_literals;

int main(int argc, char** argv) {
  ros::init(argc, argv, "franka_control_node");

  ros::NodeHandle public_node_handle;
  ros::NodeHandle node_handle("~");

  franka_hw::FrankaHW franka_control;
  if (!franka_control.init(public_node_handle, node_handle)) {
    ROS_ERROR("franka_control_node: Failed to initialize FrankaHW class. Shutting down!");
    return 1;
  }

  auto services = std::make_unique<ServiceContainer>();
  std::unique_ptr<actionlib::SimpleActionServer<franka_msgs::ErrorRecoveryAction>>
      recovery_action_server;

  std::atomic_bool has_error(false);

  auto disconnect = [&](std_srvs::Trigger::Request& request,
                        std_srvs::Trigger::Response& response) -> bool {
   ROS_INFO("franka_control, disconnect, db1");
    if (franka_control.controllerActive()) {
      response.success = false;
      response.message = "Controller is active. Cannont disconnect while a controller is running.";
      return true;
    }
   ROS_INFO("franka_control, disconnect, db2");
    response.success = true;
    response.message = "";
    services.reset();
    ROS_INFO("franka_control, disconnect, db3");
    recovery_action_server.reset();
    ROS_INFO("franka_control, disconnect, db4");
    auto result = franka_control.disconnect();
    ROS_INFO("franka_control, disconnect, db5 finished destroying robot");
    return true;
  };

  auto connect = [&](std_srvs::Trigger::Request& request,
                     std_srvs::Trigger::Response& response) -> bool {
    ROS_INFO("Connect db1");
    if (franka_control.connected()) {
      response.success = false;
      response.message = "Already conneceted to robot. Cannot connect twice.";
      return true;
    }
ROS_INFO("Connect db2");
    franka_control.connect();
ROS_INFO("Connect db3");
    std::lock_guard<std::mutex> lock(franka_control.robotMutex());
ROS_INFO("Connect db4");
    auto& robot = franka_control.robot();
ROS_INFO("Connect db5");

    // ServiceContainer services;
    services = std::make_unique<ServiceContainer>();
    franka_hw::setupServices(robot, franka_control.robotMutex(), node_handle, *services);
ROS_INFO("Connect db6");

    recovery_action_server =
        std::make_unique<actionlib::SimpleActionServer<franka_msgs::ErrorRecoveryAction>>(
            node_handle, "error_recovery",
            [&](const franka_msgs::ErrorRecoveryGoalConstPtr&) {
              try {
                std::lock_guard<std::mutex> lock(franka_control.robotMutex());
                robot.automaticErrorRecovery();
                has_error = false;
                recovery_action_server->setSucceeded();
                ROS_INFO("Recovered from error");
              } catch (const franka::Exception& ex) {
                recovery_action_server->setAborted(franka_msgs::ErrorRecoveryResult(), ex.what());
              }
            },
            false);
ROS_INFO("Connect db7");

    recovery_action_server->start();

ROS_INFO("Connect db8");
    // Initialize robot state before loading any controller
    franka_control.update(robot.readOnce());
ROS_INFO("Connect db9");
    response.success = true;
    response.message = "";
    return true;
  };

  std_srvs::Trigger::Request request;
  std_srvs::Trigger::Response response;
  if (!connect(request, response)) {
    ROS_ERROR("franka_control_node: Initial connect failed. Shutting down.");
    return 1;
  }

  ros::ServiceServer connectServer =
      node_handle.advertiseService<std_srvs::Trigger::Request, std_srvs::Trigger::Response>(
          "connect", connect);
  ros::ServiceServer disconnectServer =
      node_handle.advertiseService<std_srvs::Trigger::Request, std_srvs::Trigger::Response>(
          "disconnect", disconnect);

  controller_manager::ControllerManager control_manager(&franka_control, public_node_handle);

  // Start background threads for message handling
  ros::AsyncSpinner spinner(4);
  spinner.start();

  while (ros::ok()) {
    ros::Time last_time = ros::Time::now();

    // Wait until controller has been activated or error has been recovered
    while (!franka_control.controllerActive() || has_error) {
      if (franka_control.connected()) {
        std::lock_guard<std::mutex> lock(franka_control.robotMutex());
        franka_control.update(franka_control.robot().readOnce());

         ros::Time now = ros::Time::now();
         control_manager.update(now, now - last_time);
          franka_control.checkJointLimits();
         last_time = now;
      } else {
        std::this_thread::sleep_for(10ms);
      }

      if (!ros::ok()) {
        return 0;
      }
    }

    if (franka_control.connected()) {
      try {
        // Run control loop. Will exit if the controller is switched.
        franka_control.control([&](const ros::Time& now, const ros::Duration& period) {
          if (period.toSec() == 0.0) {
            // Reset controllers before starting a motion
            control_manager.update(now, period, true);
            franka_control.checkJointLimits();
            franka_control.reset();
          } else {
            control_manager.update(now, period);
            franka_control.checkJointLimits();
            franka_control.enforceLimits(period);
          }
          return ros::ok();
        });
      } catch (const franka::ControlException& e) {
        ROS_ERROR("%s", e.what());
        has_error = true;
      }
    }
    ROS_INFO_THROTTLE(1, "franka_control, main loop");
  }

  return 0;
}
<|endoftext|>


<commit_before>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh_common.h"

#ifdef LIBMESH_HAVE_PETSC

// C++ includes

// Local Includes
#include "auto_ptr.h"
#include "petsc_preconditioner.h"
#include "petsc_macro.h"
#include "petsc_matrix.h"
#include "petsc_vector.h"
#include "petsc_macro.h"

#include "libmesh_common.h"

namespace libMesh
{

template <typename T>
void
PetscPreconditioner<T>::apply(const NumericVector<T> & x, NumericVector<T> & y)
{
  PetscVector<T> & x_pvec = libmesh_cast_ref<PetscVector<T>&>(const_cast<NumericVector<T>&>(x));
  PetscVector<T> & y_pvec = libmesh_cast_ref<PetscVector<T>&>(const_cast<NumericVector<T>&>(y));

  Vec x_vec = x_pvec.vec();
  Vec y_vec = y_pvec.vec();

  PCApply(_pc,x_vec,y_vec);
}

template <typename T>
void
PetscPreconditioner<T>::init ()
{
  if(!this->_matrix)
  {
    libMesh::err << "ERROR: No matrix set for PetscPreconditioner, but init() called" << std::endl;
    libmesh_error();
  }

  //Clear the preconditioner in case it has been created in the past
  if(!this->_is_initialized)
  {
    //Create the preconditioning object
    PCCreate(libMesh::COMM_WORLD,&_pc);

    //Set the PCType
    set_petsc_preconditioner_type(this->_preconditioner_type, _pc);

#ifdef LIBMESH_HAVE_PETSC_HYPRE
    if(this->_preconditioner_type == AMG_PRECOND)
      PCHYPRESetType(this->_pc, "boomeramg");
#endif

    PetscMatrix<T> * pmatrix = libmesh_cast_ptr<PetscMatrix<T>*, SparseMatrix<T> >(this->_matrix);

    _mat = pmatrix->mat();
  }

  PCSetOperators(_pc,_mat,_mat,SAME_NONZERO_PATTERN);

  this->_is_initialized = true;
}

template <typename T>
void
PetscPreconditioner<T>::set_petsc_preconditioner_type (const PreconditionerType & preconditioner_type, PC & pc)
{
  int ierr = 0;

  switch (preconditioner_type)
  {
  case IDENTITY_PRECOND:
    ierr = PCSetType (pc, (char*) PCNONE);      CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case CHOLESKY_PRECOND:
    ierr = PCSetType (pc, (char*) PCCHOLESKY);  CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case ICC_PRECOND:
    ierr = PCSetType (pc, (char*) PCICC);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case ILU_PRECOND:
    ierr = PCSetType (pc, (char*) PCILU);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case LU_PRECOND:
    ierr = PCSetType (pc, (char*) PCLU);        CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case ASM_PRECOND:
    ierr = PCSetType (pc, (char*) PCASM);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case JACOBI_PRECOND:
    ierr = PCSetType (pc, (char*) PCJACOBI);    CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case BLOCK_JACOBI_PRECOND:
    ierr = PCSetType (pc, (char*) PCBJACOBI);   CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case SOR_PRECOND:
    ierr = PCSetType (pc, (char*) PCSOR);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case EISENSTAT_PRECOND:
    ierr = PCSetType (pc, (char*) PCEISENSTAT); CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case AMG_PRECOND:
    ierr = PCSetType (pc, (char*) PCHYPRE);     CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

#if !(PETSC_VERSION_LESS_THAN(2,1,2))
    // Only available for PETSC >= 2.1.2
  case USER_PRECOND:
    ierr = PCSetType (pc, (char*) PCMAT);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;
#endif

  case SHELL_PRECOND:
    ierr = PCSetType (pc, (char*) PCSHELL);     CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  default:
    libMesh::err << "ERROR:  Unsupported PETSC Preconditioner: "
                  << preconditioner_type       << std::endl
                  << "Continuing with PETSC defaults" << std::endl;
  }

  //Let the commandline override stuff
  if( preconditioner_type != AMG_PRECOND )
    PCSetFromOptions(pc);
}

//------------------------------------------------------------------
// Explicit instantiations
template class PetscPreconditioner<Number>;

} // namespace libMesh

#endif // #ifdef LIBMESH_HAVE_PETSC
<commit_msg>Added missing calls to CHKERRABORT().<commit_after>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh_common.h"

#ifdef LIBMESH_HAVE_PETSC

// C++ includes

// Local Includes
#include "auto_ptr.h"
#include "petsc_preconditioner.h"
#include "petsc_macro.h"
#include "petsc_matrix.h"
#include "petsc_vector.h"
#include "petsc_macro.h"

#include "libmesh_common.h"

namespace libMesh
{

template <typename T>
void
PetscPreconditioner<T>::apply(const NumericVector<T> & x, NumericVector<T> & y)
{
  PetscVector<T> & x_pvec = libmesh_cast_ref<PetscVector<T>&>(const_cast<NumericVector<T>&>(x));
  PetscVector<T> & y_pvec = libmesh_cast_ref<PetscVector<T>&>(const_cast<NumericVector<T>&>(y));

  Vec x_vec = x_pvec.vec();
  Vec y_vec = y_pvec.vec();

  int ierr = PCApply(_pc,x_vec,y_vec);
  CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void
PetscPreconditioner<T>::init ()
{
  if(!this->_matrix)
  {
    libMesh::err << "ERROR: No matrix set for PetscPreconditioner, but init() called" << std::endl;
    libmesh_error();
  }

  //Clear the preconditioner in case it has been created in the past
  if(!this->_is_initialized)
  {
    //Create the preconditioning object
    int ierr = PCCreate(libMesh::COMM_WORLD,&_pc);
    CHKERRABORT(libMesh::COMM_WORLD,ierr);

    //Set the PCType
    set_petsc_preconditioner_type(this->_preconditioner_type, _pc);

#ifdef LIBMESH_HAVE_PETSC_HYPRE
    if(this->_preconditioner_type == AMG_PRECOND)
      {
	PCHYPRESetType(this->_pc, "boomeramg");
	CHKERRABORT(libMesh::COMM_WORLD,ierr);
      }
#endif

    PetscMatrix<T> * pmatrix = libmesh_cast_ptr<PetscMatrix<T>*, SparseMatrix<T> >(this->_matrix);

    _mat = pmatrix->mat();
  }

  int ierr = PCSetOperators(_pc,_mat,_mat,SAME_NONZERO_PATTERN);
  CHKERRABORT(libMesh::COMM_WORLD,ierr);

  this->_is_initialized = true;
}

template <typename T>
void
PetscPreconditioner<T>::set_petsc_preconditioner_type (const PreconditionerType & preconditioner_type, PC & pc)
{
  int ierr = 0;

  switch (preconditioner_type)
  {
  case IDENTITY_PRECOND:
    ierr = PCSetType (pc, (char*) PCNONE);      CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case CHOLESKY_PRECOND:
    ierr = PCSetType (pc, (char*) PCCHOLESKY);  CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case ICC_PRECOND:
    ierr = PCSetType (pc, (char*) PCICC);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case ILU_PRECOND:
    ierr = PCSetType (pc, (char*) PCILU);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case LU_PRECOND:
    ierr = PCSetType (pc, (char*) PCLU);        CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case ASM_PRECOND:
    ierr = PCSetType (pc, (char*) PCASM);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case JACOBI_PRECOND:
    ierr = PCSetType (pc, (char*) PCJACOBI);    CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case BLOCK_JACOBI_PRECOND:
    ierr = PCSetType (pc, (char*) PCBJACOBI);   CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case SOR_PRECOND:
    ierr = PCSetType (pc, (char*) PCSOR);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case EISENSTAT_PRECOND:
    ierr = PCSetType (pc, (char*) PCEISENSTAT); CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  case AMG_PRECOND:
    ierr = PCSetType (pc, (char*) PCHYPRE);     CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

#if !(PETSC_VERSION_LESS_THAN(2,1,2))
    // Only available for PETSC >= 2.1.2
  case USER_PRECOND:
    ierr = PCSetType (pc, (char*) PCMAT);       CHKERRABORT(libMesh::COMM_WORLD,ierr); break;
#endif

  case SHELL_PRECOND:
    ierr = PCSetType (pc, (char*) PCSHELL);     CHKERRABORT(libMesh::COMM_WORLD,ierr); break;

  default:
    libMesh::err << "ERROR:  Unsupported PETSC Preconditioner: "
                  << preconditioner_type       << std::endl
                  << "Continuing with PETSC defaults" << std::endl;
  }

  //Let the commandline override stuff
  if( preconditioner_type != AMG_PRECOND )
    {
      PCSetFromOptions(pc);
      CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }
}

//------------------------------------------------------------------
// Explicit instantiations
template class PetscPreconditioner<Number>;

} // namespace libMesh

#endif // #ifdef LIBMESH_HAVE_PETSC
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2010  Peter Colberg
 *
 * This file is part of cuda-wrapper.
 *
 * This software may be modified and distributed under the terms of the
 * 3-clause BSD license.  See accompanying file LICENSE for details.
 */

#ifndef CUDA_TRAITS_HPP
#define CUDA_TRAITS_HPP

#include <cstddef>
#include <cuda.h>

namespace cuda {

typedef std::size_t size_type;

} // namespace cuda

#endif /* ! CUDA_TRAITS_HPP */
<commit_msg>Remove traits.hpp<commit_after><|endoftext|>


<commit_before>#include "SFZDebug.h"

static LogFifo* fifo = NULL;

LogFifo::LogFifo()
	: fifo(capacity)
{
}

LogFifo::~LogFifo()
{
}

void LogFifo::logMessage(const String& message)
{
	const char* p;

	// Stupid String class doesn't really let us get number of bytes.
	const char* bytes = message.getCharPointer();
	unsigned long msgSize = strlen(bytes);
	int totalSize = sizeof(unsigned long) + msgSize;
	int start1, size1, start2, size2;
	fifo.prepareToWrite(totalSize, start1, size1, start2, size2);
	int givenSize = size1 + size2;
	if (givenSize < totalSize)
		msgSize -= givenSize - totalSize;

	// Write the count.
	if (size1 >= sizeof(unsigned long)) {
		memcpy(&buffer[start1], &msgSize, sizeof(unsigned long));
		size1 -= sizeof(unsigned long);
		start1 += sizeof(unsigned long);
		}
	else {
		p = (const char*) &msgSize;
		memcpy(&buffer[start1], p, size1);
		p += size1;
		size1 = 0;
		int bytesLeft = sizeof(unsigned long) - size1;
		memcpy(&buffer[start2], p, bytesLeft);
		start2 += bytesLeft;
		size2 -= bytesLeft;
		}

	// Write the string.
	p = bytes;
	if (size1 > 0) {
		memcpy(&buffer[start1], p, size1);
		p += size1;
		}
	if (size2 > 0)
		memcpy(&buffer[start2], p, size2);

	fifo.finishedWrite(givenSize);
}

void LogFifo::relayMessages()
{
	while (hasMessage()) {
		String message = nextMessage();
		Logger::writeToLog(message);
		}
}

String LogFifo::nextMessage()
{
	// Read the count.
	unsigned long msgSize = 0;
	int start1, size1, start2, size2;
	fifo.prepareToRead(sizeof(unsigned long), start1, size1, start2, size2);
	char* p = (char*) &msgSize;
	if (size1 > 0) {
		memcpy(p, &buffer[start1], size1);
		p += size1;
		}
	if (size2 > 0)
		memcpy(p, &buffer[start2], size2);
	fifo.finishedRead(size1 + size2);

	// Read the string.
	String result;
	fifo.prepareToRead(msgSize, start1, size1, start2, size2);
	if (start1 > 0) {
		p = &buffer[start1];
		result = String(CharPointer_UTF8(p), CharPointer_UTF8(p + size1));
		}
	if (start2 > 0) {
		p = &buffer[start2];
		result += String(CharPointer_UTF8(p), CharPointer_UTF8(p + size2));
		}
	fifo.finishedRead(size1 + size2);

	return result;
}

bool LogFifo::hasMessage()
{
	return fifo.getNumReady() > 0;
}

void setupLogging(Logger* logger)
{
	if (fifo == NULL)
		fifo = new LogFifo();
	Logger::setCurrentLogger(logger, true);
}

void fifoLogMessage(const String& message)
{
	if (fifo)
		fifo->logMessage(message);
}

void relayFifoLogMessages()
{
	if (fifo)
		fifo->relayMessages();
}

<commit_msg>More FIFO logging fixes.<commit_after>#include "SFZDebug.h"

static LogFifo* fifo = NULL;

LogFifo::LogFifo()
	: fifo(capacity)
{
}

LogFifo::~LogFifo()
{
}

void LogFifo::logMessage(const String& message)
{
	const char* p;

	// Stupid String class doesn't really let us get number of bytes.
	const char* bytes = message.getCharPointer();
	unsigned long msgSize = strlen(bytes);
	int totalSize = sizeof(unsigned long) + msgSize;
	int start1, size1, start2, size2;
	fifo.prepareToWrite(totalSize, start1, size1, start2, size2);
	int givenSize = size1 + size2;
	if (givenSize < totalSize)
		msgSize -= givenSize - totalSize;

	// Write the count.
	if (size1 >= sizeof(unsigned long)) {
		memcpy(&buffer[start1], &msgSize, sizeof(unsigned long));
		size1 -= sizeof(unsigned long);
		start1 += sizeof(unsigned long);
		}
	else {
		p = (const char*) &msgSize;
		memcpy(&buffer[start1], p, size1);
		p += size1;
		size1 = 0;
		int bytesLeft = sizeof(unsigned long) - size1;
		memcpy(&buffer[start2], p, bytesLeft);
		start2 += bytesLeft;
		size2 -= bytesLeft;
		}

	// Write the string.
	p = bytes;
	if (size1 > 0) {
		memcpy(&buffer[start1], p, size1);
		p += size1;
		}
	if (size2 > 0)
		memcpy(&buffer[start2], p, size2);

	fifo.finishedWrite(givenSize);
}

void LogFifo::relayMessages()
{
	while (hasMessage()) {
		String message = nextMessage();
		Logger::writeToLog(message);
		}
}

String LogFifo::nextMessage()
{
	// Read the count.
	unsigned long msgSize = 0;
	int start1, size1, start2, size2;
	fifo.prepareToRead(sizeof(unsigned long), start1, size1, start2, size2);
	char* p = (char*) &msgSize;
	if (size1 > 0) {
		memcpy(p, &buffer[start1], size1);
		p += size1;
		}
	if (size2 > 0)
		memcpy(p, &buffer[start2], size2);
	fifo.finishedRead(size1 + size2);

	// Read the string.
	String result;
	fifo.prepareToRead(msgSize, start1, size1, start2, size2);
	if (size1 > 0) {
		p = &buffer[start1];
		result = String(CharPointer_UTF8(p), CharPointer_UTF8(p + size1));
		}
	if (size2 > 0) {
		p = &buffer[start2];
		result += String(CharPointer_UTF8(p), CharPointer_UTF8(p + size2));
		}
	fifo.finishedRead(size1 + size2);

	return result;
}

bool LogFifo::hasMessage()
{
	return fifo.getNumReady() > 0;
}

void setupLogging(Logger* logger)
{
	if (fifo == NULL)
		fifo = new LogFifo();
	Logger::setCurrentLogger(logger, true);
}

void fifoLogMessage(const String& message)
{
	if (fifo)
		fifo->logMessage(message);
}

void relayFifoLogMessages()
{
	if (fifo)
		fifo->relayMessages();
}

<|endoftext|>