<commit_before>#include "NL_ImguiEditorUiManager.h"
#include "NL_ThreadLocal.h"
#include "NL_EngineServices.h"
#include "NL_SharedData.h"
#include "NL_SystemServices.h"
#include "NL_InputEvents.h"
#include "NL_ImguiInputMap.h"

#include <assert.h>
#include <string>
#include <iostream>
#include <algorithm>

namespace NLE
{
	namespace UI
	{
		ImguiEditorUiManager::ImguiEditorUiManager(
			EngineServices& eServices,
			CONSOLE::IConsoleQueue& consoleQueue,
			IWindowManager& windowManager,
			GAME::IGameManager& gameManager,
			INPUT::IInputProcessor& inputProcessor,
			GRAPHICS::IRenderingEngine& renderingEngine,
			SCRIPT::IScriptingEngine& scriptingEngine)
			:
			_eServices(eServices),
			_consoleQueue(consoleQueue),
			_windowManager(windowManager),
			_gameManager(gameManager),
			_inputProcessor(inputProcessor),
			_renderingEngine(renderingEngine),
			_scriptingEngine(scriptingEngine),

			_showEditor(true),
			_showEditorSettings(false),

			_windowBgColor(0.0f, 0.0f, 1.0f, 0.5f),
			_textColor(0.0f, 1.0f, 0.0f, 1.0f),
			_borderColor(0.0f, 1.0f, 0.0f, 1.0f),
			_itemColor(0.0f, 0.0f, 1.0f, 0.5f),
			_itemHoverColor(0.0f, 0.0f, 1.0f, 0.8f),
			_itemActiveColor(0.0f, 0.0f, 1.0f, 0.8f),
			_selectionColor(1.0f, 1.0f, 1.0f, 0.8f)
		{
		}

		ImguiEditorUiManager::~ImguiEditorUiManager()
		{
		}

		bool ImguiEditorUiManager::initialize()
		{
			return true;
		}

		int ImguiEditorUiManager::getScancodeForKeyEvent(INPUT::Event event)
		{
			assert(event.eventType == INPUT::EVENT_KEY);
			ImGuiIO& io = ImGui::GetIO();

			auto key = INPUT::NLEtoImguiKey(event.eventData.keyEvent.key);
			if (key == -1)
				return event.eventData.keyEvent.scancode;

			if (io.KeyMap[key] == -1)
				io.KeyMap[key] = event.eventData.keyEvent.scancode;

			return io.KeyMap[key];
		}

		ImDrawData* ImguiEditorUiManager::getDrawData()
		{
			return ImGui::GetDrawData();
		}

		void ImguiEditorUiManager::queueKeyAndCharEvent(INPUT::Event event)
		{
			_keyAndCharEvents.push(event);
		}

		void ImguiEditorUiManager::update(SystemServices* sServices, double deltaT, Size2D screenSize)
		{
			NLE::TLS::PerformanceTimer::reference timer = NLE::TLS::performanceTimer.local();
			timer.deltaT();

			DATA::SharedData& data = _eServices.data->getData();
			ImGuiIO& io = ImGui::GetIO();
			io.DisplaySize = ImVec2((float)screenSize.width, (float)screenSize.height);
			io.DeltaTime = (float)(deltaT * 0.000000001);

			//Need crossplatform implementation
			// Hide OS mouse cursor if ImGui is drawing it
			//SetCursor(io.MouseDrawCursor ? NULL : LoadCursor(NULL, IDC_ARROW));

			captureInput(sServices, deltaT, screenSize);
			ImGui::NewFrame();
			drawUI(sServices, screenSize);
			ImGui::Render();

			data.sysExecutionTimes.set(UI_MANAGER, timer.deltaT());
		}

		void ImguiEditorUiManager::captureInput(SystemServices* sServices, double deltaT, Size2D screenSize)
		{
			DATA::SharedData& data = _eServices.data->getData();
			ImGuiIO& io = ImGui::GetIO();

			io.MouseDown[0] = data.mouseButtonPressed.get()[INPUT::MOUSE_BUTTON_LEFT];
			io.MouseDown[1] = data.mouseButtonPressed.get()[INPUT::MOUSE_BUTTON_RIGHT];
			io.MouseDown[2] = data.mouseButtonPressed.get()[INPUT::MOUSE_BUTTON_MIDDLE];
			io.MouseWheel += (float)data.scrollOffset.get()[1];
			io.MousePos.x = (float)data.mouseCursorPosition.get()[0];
			io.MousePos.y = (float)data.mouseCursorPosition.get()[1];

			INPUT::Event event;
			while (_keyAndCharEvents.pop(event))
			{
				if (event.eventType == INPUT::EVENT_KEY)
				{
					int scancode = getScancodeForKeyEvent(event);
					io.KeysDown[scancode] = event.eventData.keyEvent.action == INPUT::ACTION_PRESS ? 1 : 0;
				}
				else
				{
					io.AddInputCharacter((unsigned short)event.eventData.charEvent.code);
				}
			}

			io.KeyCtrl = data.keyModsPressed.get()[INPUT::KEY_MOD_CONTROL];
			io.KeyShift = data.keyModsPressed.get()[INPUT::KEY_MOD_SHIFT];
			io.KeyAlt = data.keyModsPressed.get()[INPUT::KEY_MOD_ALT];
			io.KeySuper = data.keyModsPressed.get()[INPUT::KEY_MOD_SUPER];
		}

		void ImguiEditorUiManager::drawUI(SystemServices* sServices, Size2D screenSize)
		{
			ImGuiWindowFlags window_flags = 0;
			window_flags |= ImGuiWindowFlags_NoTitleBar;
			window_flags |= ImGuiWindowFlags_NoResize;
			window_flags |= ImGuiWindowFlags_NoMove;
			window_flags |= ImGuiWindowFlags_NoScrollbar;
			window_flags |= ImGuiWindowFlags_NoCollapse;
			window_flags |= ImGuiWindowFlags_MenuBar;
			window_flags |= ImGuiWindowFlags_NoSavedSettings;
			window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;

			ImGui::SetNextWindowPos(ImVec2(0,0));
			ImGui::SetNextWindowSize(ImVec2((float)screenSize.width, (float)screenSize.height), ImGuiSetCond_FirstUseEver);
			ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
			applyColorScheme(true);
			ImGui::Begin("Engine UI Overlay Window", &_showEditor, window_flags);

			if (ImGui::BeginMenuBar())
			{
				if (ImGui::BeginMenu("File"))
				{
					ImGui::EndMenu();
				}
				if (ImGui::BeginMenu("View"))
				{
					ImGui::MenuItem("Editor", NULL, &_showEditorSettings);
					ImGui::EndMenu();
				}
				ImGui::EndMenuBar();
			}

			ImGui::End();
			restoreColorScheme();
			ImGui::PopStyleVar();

			if(_showEditorSettings)
				showEditorSettings(sServices, screenSize);

		}

		void ImguiEditorUiManager::showEditorSettings(SystemServices* sServices, Size2D screenSize)
		{
			ImGuiWindowFlags window_flags = 0;
			window_flags |= ImGuiWindowFlags_NoSavedSettings;
			window_flags |= ImGuiWindowFlags_AlwaysAutoResize;

			ImGui::SetNextWindowPos(ImVec2((float)screenSize.width / 2, (float)screenSize.height / 2), ImGuiSetCond_FirstUseEver);
			ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
			applyColorScheme(false);
			ImGui::Begin("Editor Settings", &_showEditorSettings, window_flags);

			if (ImGui::CollapsingHeader("Color Scheme"))
			{
				ImGui::ColorEdit4("Window Background", (float*)&_windowBgColor);
				ImGui::ColorEdit4("Text Color", (float*)&_textColor);
				ImGui::ColorEdit4("Border Color", (float*)&_borderColor);
				ImGui::ColorEdit4("Item Color", (float*)&_itemColor);
				ImGui::ColorEdit4("Item Hover Color", (float*)&_itemHoverColor);
				ImGui::ColorEdit4("Item Active Color", (float*)&_itemActiveColor);
				ImGui::ColorEdit4("Selection Color", (float*)&_selectionColor);
			}

			ImGui::End();
			restoreColorScheme();
			ImGui::PopStyleVar();
		}

		void ImguiEditorUiManager::applyColorScheme(bool root)
		{
			if(root)
				ImGui::PushStyleColor(ImGuiCol_WindowBg, ImColor(0.0f, 0.0f, 0.0f, 0.0f));
			else
				ImGui::PushStyleColor(ImGuiCol_WindowBg, _windowBgColor);

			ImGui::PushStyleColor(ImGuiCol_MenuBarBg, _windowBgColor);
			ImGui::PushStyleColor(ImGuiCol_Text, _textColor);
			ImGui::PushStyleColor(ImGuiCol_Border, _borderColor);
			ImGui::PushStyleColor(ImGuiCol_Button, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_ButtonHovered, _itemHoverColor);
			ImGui::PushStyleColor(ImGuiCol_ButtonActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_Header, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_HeaderHovered, _itemHoverColor);
			ImGui::PushStyleColor(ImGuiCol_HeaderActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_FrameBg, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, _itemHoverColor);
			ImGui::PushStyleColor(ImGuiCol_FrameBgActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_TitleBg, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_TitleBgActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_CloseButton, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_CloseButtonHovered, _itemHoverColor);
			ImGui::PushStyleColor(ImGuiCol_CloseButtonActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, _selectionColor);
		}

		void ImguiEditorUiManager::restoreColorScheme()
		{
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
		}

		void ImguiEditorUiManager::show(bool show)
		{
			_showEditor = show;
		}
	}
}<commit_msg>added borders and fps counter<commit_after>#include "NL_ImguiEditorUiManager.h"
#include "NL_ThreadLocal.h"
#include "NL_EngineServices.h"
#include "NL_SharedData.h"
#include "NL_SystemServices.h"
#include "NL_InputEvents.h"
#include "NL_ImguiInputMap.h"

#include <assert.h>
#include <string>
#include <iostream>
#include <algorithm>

namespace NLE
{
	namespace UI
	{
		ImguiEditorUiManager::ImguiEditorUiManager(
			EngineServices& eServices,
			CONSOLE::IConsoleQueue& consoleQueue,
			IWindowManager& windowManager,
			GAME::IGameManager& gameManager,
			INPUT::IInputProcessor& inputProcessor,
			GRAPHICS::IRenderingEngine& renderingEngine,
			SCRIPT::IScriptingEngine& scriptingEngine)
			:
			_eServices(eServices),
			_consoleQueue(consoleQueue),
			_windowManager(windowManager),
			_gameManager(gameManager),
			_inputProcessor(inputProcessor),
			_renderingEngine(renderingEngine),
			_scriptingEngine(scriptingEngine),

			_showEditor(true),
			_showEditorSettings(false),

			_windowBgColor(0.0f, 0.0f, 1.0f, 0.5f),
			_textColor(0.0f, 1.0f, 0.0f, 1.0f),
			_borderColor(0.0f, 1.0f, 0.0f, 1.0f),
			_itemColor(0.0f, 0.0f, 1.0f, 0.5f),
			_itemHoverColor(0.0f, 0.0f, 1.0f, 0.8f),
			_itemActiveColor(0.0f, 0.0f, 1.0f, 0.8f),
			_selectionColor(1.0f, 1.0f, 1.0f, 0.8f)
		{
		}

		ImguiEditorUiManager::~ImguiEditorUiManager()
		{
		}

		bool ImguiEditorUiManager::initialize()
		{
			return true;
		}

		int ImguiEditorUiManager::getScancodeForKeyEvent(INPUT::Event event)
		{
			assert(event.eventType == INPUT::EVENT_KEY);
			ImGuiIO& io = ImGui::GetIO();

			auto key = INPUT::NLEtoImguiKey(event.eventData.keyEvent.key);
			if (key == -1)
				return event.eventData.keyEvent.scancode;

			if (io.KeyMap[key] == -1)
				io.KeyMap[key] = event.eventData.keyEvent.scancode;

			return io.KeyMap[key];
		}

		ImDrawData* ImguiEditorUiManager::getDrawData()
		{
			return ImGui::GetDrawData();
		}

		void ImguiEditorUiManager::queueKeyAndCharEvent(INPUT::Event event)
		{
			_keyAndCharEvents.push(event);
		}

		void ImguiEditorUiManager::update(SystemServices* sServices, double deltaT, Size2D screenSize)
		{
			NLE::TLS::PerformanceTimer::reference timer = NLE::TLS::performanceTimer.local();
			timer.deltaT();

			DATA::SharedData& data = _eServices.data->getData();
			ImGuiIO& io = ImGui::GetIO();
			io.DisplaySize = ImVec2((float)screenSize.width, (float)screenSize.height);
			io.DeltaTime = (float)(deltaT * 0.000000001);

			//Need crossplatform implementation
			// Hide OS mouse cursor if ImGui is drawing it
			//SetCursor(io.MouseDrawCursor ? NULL : LoadCursor(NULL, IDC_ARROW));

			captureInput(sServices, deltaT, screenSize);
			ImGui::NewFrame();
			drawUI(sServices, screenSize);
			ImGui::Render();

			data.sysExecutionTimes.set(UI_MANAGER, timer.deltaT());
		}

		void ImguiEditorUiManager::captureInput(SystemServices* sServices, double deltaT, Size2D screenSize)
		{
			DATA::SharedData& data = _eServices.data->getData();
			ImGuiIO& io = ImGui::GetIO();

			io.MouseDown[0] = data.mouseButtonPressed.get()[INPUT::MOUSE_BUTTON_LEFT];
			io.MouseDown[1] = data.mouseButtonPressed.get()[INPUT::MOUSE_BUTTON_RIGHT];
			io.MouseDown[2] = data.mouseButtonPressed.get()[INPUT::MOUSE_BUTTON_MIDDLE];
			io.MouseWheel += (float)data.scrollOffset.get()[1];
			io.MousePos.x = (float)data.mouseCursorPosition.get()[0];
			io.MousePos.y = (float)data.mouseCursorPosition.get()[1];

			INPUT::Event event;
			while (_keyAndCharEvents.pop(event))
			{
				if (event.eventType == INPUT::EVENT_KEY)
				{
					int scancode = getScancodeForKeyEvent(event);
					io.KeysDown[scancode] = event.eventData.keyEvent.action == INPUT::ACTION_PRESS ? 1 : 0;
				}
				else
				{
					io.AddInputCharacter((unsigned short)event.eventData.charEvent.code);
				}
			}

			io.KeyCtrl = data.keyModsPressed.get()[INPUT::KEY_MOD_CONTROL];
			io.KeyShift = data.keyModsPressed.get()[INPUT::KEY_MOD_SHIFT];
			io.KeyAlt = data.keyModsPressed.get()[INPUT::KEY_MOD_ALT];
			io.KeySuper = data.keyModsPressed.get()[INPUT::KEY_MOD_SUPER];
		}

		void ImguiEditorUiManager::drawUI(SystemServices* sServices, Size2D screenSize)
		{
			ImGuiWindowFlags window_flags = 0;
			window_flags |= ImGuiWindowFlags_NoTitleBar;
			window_flags |= ImGuiWindowFlags_NoResize;
			window_flags |= ImGuiWindowFlags_NoMove;
			window_flags |= ImGuiWindowFlags_NoScrollbar;
			window_flags |= ImGuiWindowFlags_NoCollapse;
			window_flags |= ImGuiWindowFlags_MenuBar;
			window_flags |= ImGuiWindowFlags_NoSavedSettings;
			window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;

			ImGui::SetNextWindowPos(ImVec2(0,0));
			ImGui::SetNextWindowSize(ImVec2((float)screenSize.width, (float)screenSize.height), ImGuiSetCond_FirstUseEver);
			ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
			applyColorScheme(true);
			ImGui::Begin("Engine UI Overlay Window", &_showEditor, window_flags);

			if (ImGui::BeginMenuBar())
			{
				if (ImGui::BeginMenu("File"))
				{
					ImGui::EndMenu();
				}
				if (ImGui::BeginMenu("View"))
				{
					ImGui::MenuItem("Editor", NULL, &_showEditorSettings);
					ImGui::EndMenu();
				}
				ImGui::SameLine(ImGui::GetWindowWidth() - 100);
				ImGui::Text("%.1f FPS", ImGui::GetIO().Framerate);
				ImGui::EndMenuBar();
			}

			ImGui::End();
			restoreColorScheme();
			ImGui::PopStyleVar();

			if(_showEditorSettings)
				showEditorSettings(sServices, screenSize);

		}

		void ImguiEditorUiManager::showEditorSettings(SystemServices* sServices, Size2D screenSize)
		{
			ImGuiWindowFlags window_flags = 0;
			window_flags |= ImGuiWindowFlags_NoSavedSettings;
			window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
			window_flags |= ImGuiWindowFlags_ShowBorders;

			ImGui::SetNextWindowPos(ImVec2((float)screenSize.width / 2, (float)screenSize.height / 2), ImGuiSetCond_FirstUseEver);
			ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
			applyColorScheme(false);
			ImGui::Begin("Editor Settings", &_showEditorSettings, window_flags);

			if (ImGui::CollapsingHeader("Color Scheme"))
			{
				ImGui::ColorEdit4("Window Background", (float*)&_windowBgColor);
				ImGui::ColorEdit4("Text Color", (float*)&_textColor);
				ImGui::ColorEdit4("Border Color", (float*)&_borderColor);
				ImGui::ColorEdit4("Item Color", (float*)&_itemColor);
				ImGui::ColorEdit4("Item Hover Color", (float*)&_itemHoverColor);
				ImGui::ColorEdit4("Item Active Color", (float*)&_itemActiveColor);
				ImGui::ColorEdit4("Selection Color", (float*)&_selectionColor);
			}

			ImGui::End();
			restoreColorScheme();
			ImGui::PopStyleVar();
		}

		void ImguiEditorUiManager::applyColorScheme(bool root)
		{
			if(root)
				ImGui::PushStyleColor(ImGuiCol_WindowBg, ImColor(0.0f, 0.0f, 0.0f, 0.0f));
			else
				ImGui::PushStyleColor(ImGuiCol_WindowBg, _windowBgColor);

			ImGui::PushStyleColor(ImGuiCol_MenuBarBg, _windowBgColor);
			ImGui::PushStyleColor(ImGuiCol_Text, _textColor);
			ImGui::PushStyleColor(ImGuiCol_Border, _borderColor);
			ImGui::PushStyleColor(ImGuiCol_Button, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_ButtonHovered, _itemHoverColor);
			ImGui::PushStyleColor(ImGuiCol_ButtonActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_Header, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_HeaderHovered, _itemHoverColor);
			ImGui::PushStyleColor(ImGuiCol_HeaderActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_FrameBg, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, _itemHoverColor);
			ImGui::PushStyleColor(ImGuiCol_FrameBgActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_TitleBg, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_TitleBgActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_CloseButton, _itemColor);
			ImGui::PushStyleColor(ImGuiCol_CloseButtonHovered, _itemHoverColor);
			ImGui::PushStyleColor(ImGuiCol_CloseButtonActive, _itemActiveColor);
			ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, _selectionColor);
		}

		void ImguiEditorUiManager::restoreColorScheme()
		{
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
			ImGui::PopStyleColor();
		}

		void ImguiEditorUiManager::show(bool show)
		{
			_showEditor = show;
		}
	}
}<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2013-2018 Igor Zinken - http://www.igorski.nl
 *
 * 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 "sequencercontroller.h"
#include "sequencer.h"
#include "audioengine.h"
#include <definitions/notifications.h>
#include <messaging/notifier.h>
#include <utilities/utils.h>
#include <utilities/diskwriter.h>
#include <utilities/volumeutil.h>

namespace MWEngine {

/* constructor / destructor */

SequencerController::SequencerController()
{
    // by default, function as a sixteen step sequencer

    stepsPerBar = 16;
    AudioEngine::max_step_position = stepsPerBar - 1;
};

SequencerController::~SequencerController()
{
    setPlaying( false );
};

/* public methods */

void SequencerController::prepare( float aQueuedTempo, int aTimeSigBeatAmount, int aTimeSigBeatUnit )
{
    // calculate buffers and ranges
    if ( aQueuedTempo > 0 )
    {
        setTempo( aQueuedTempo, aTimeSigBeatAmount, aTimeSigBeatUnit );
        AudioEngine::handleTempoUpdate( aQueuedTempo, false );   // just to initialize all buffer sizes
        setLoopRange( 0, ( AudioEngine::amount_of_bars * AudioEngine::samples_per_bar ) - 1, stepsPerBar );
    }
};

float SequencerController::getTempo()
{
    return AudioEngine::tempo;
}

void SequencerController::setTempo( float aTempo, int aTimeSigBeatAmount, int aTimeSigBeatUnit )
{
    AudioEngine::queuedTempo = aTempo;

    AudioEngine::queuedTime_sig_beat_amount = aTimeSigBeatAmount;
    AudioEngine::queuedTime_sig_beat_unit   = aTimeSigBeatUnit;
}

void SequencerController::setTempoNow( float aTempo, int aTimeSigBeatAmount, int aTimeSigBeatUnit )
{
    setTempo( aTempo, aTimeSigBeatAmount, aTimeSigBeatUnit );
    updateStepsPerBar( stepsPerBar );
    AudioEngine::handleTempoUpdate( AudioEngine::queuedTempo, true );
}

void SequencerController::setVolume( float aVolume )
{
    AudioEngine::volume = VolumeUtil::toLog( aVolume );
}

void SequencerController::setPlaying( bool aIsPlaying )
{
    Sequencer::playing = aIsPlaying;
}

void SequencerController::setLoopRange( int aStartPosition, int aEndPosition )
{
    setLoopRange( aStartPosition, aEndPosition, stepsPerBar );
}

/**
 * make the sequencer loop between two given points
 *
 * @param aStartPosition {int} buffer offset of the loops startpoint (starts at 0 !)
 * @param aEndPosition   {int} buffer offset of the loops endpoint
 * @param aStepsPerBar   {int} the amount of individual segments the sequencer subdivides a single bar into
 *                             this is used for periodic notifications when the sequencer switches step
 */
void SequencerController::setLoopRange( int aStartPosition, int aEndPosition, int aStepsPerBar )
{
    AudioEngine::min_buffer_position = aStartPosition;
    AudioEngine::max_buffer_position = aEndPosition;

    // keep current buffer read pointer within the new loop range
    if ( AudioEngine::bufferPosition < AudioEngine::min_buffer_position ||
         AudioEngine::bufferPosition > AudioEngine::max_buffer_position )
    {
        AudioEngine::bufferPosition = AudioEngine::min_buffer_position;
    }

    AudioEngine::min_step_position = ( int ) round(( aStartPosition / AudioEngine::samples_per_bar ) * aStepsPerBar );
    AudioEngine::max_step_position = ( int ) round(((( float ) aEndPosition / ( float ) AudioEngine::samples_per_bar ) * aStepsPerBar ) - 1 );

    // keep current sequencer step within the new loop range
    if ( AudioEngine::stepPosition < AudioEngine::min_step_position ||
         AudioEngine::stepPosition > AudioEngine::max_step_position )
    {
        AudioEngine::stepPosition = AudioEngine::min_step_position;
    }
    updateStepsPerBar( aStepsPerBar );
}

int SequencerController::getStepPosition()
{
    return AudioEngine::stepPosition;
}

int SequencerController::getBufferPosition()
{
    return AudioEngine::bufferPosition;
}

void SequencerController::setBufferPosition( int aPosition )
{
    // keep position within the sequences range (see "setLoopRange")

    if ( aPosition < AudioEngine::min_buffer_position ||
         aPosition > AudioEngine::max_buffer_position )
    {
        aPosition = AudioEngine::min_buffer_position;
    }

    AudioEngine::bufferPosition = aPosition;
    AudioEngine::stepPosition   = ( aPosition / AudioEngine::samples_per_bar ) * stepsPerBar;

    Notifier::broadcast( Notifications::SEQUENCER_POSITION_UPDATED );
}

int SequencerController::getSamplesPerBeat()
{
    return AudioEngine::samples_per_beat;
}

int SequencerController::getSamplesPerStep()
{
    return ( int ) AudioEngine::samples_per_step;
}

int SequencerController::getSamplesPerBar()
{
    return AudioEngine::samples_per_bar;
}

int SequencerController::getTimeSigBeatAmount()
{
    return AudioEngine::time_sig_beat_amount;
}

int SequencerController::getTimeSigBeatUnit()
{
    return AudioEngine::time_sig_beat_unit;
}

void SequencerController::updateStepsPerBar( int aStepsPerBar )
{
    stepsPerBar = aStepsPerBar;
    AudioEngine::max_step_position = ( stepsPerBar * AudioEngine::amount_of_bars ) - 1;
    AudioEngine::beat_subdivision  = stepsPerBar / AudioEngine::time_sig_beat_amount;

    // keep current sequencer step within the new loop range

    if ( AudioEngine::stepPosition > AudioEngine::max_step_position )
        AudioEngine::stepPosition = AudioEngine::min_step_position;
}

void SequencerController::updateMeasures( int aValue, int aStepsPerBar )
{
    AudioEngine::amount_of_bars      = aValue;
    AudioEngine::max_buffer_position = ( AudioEngine::samples_per_bar * AudioEngine::amount_of_bars ) - 1;

    updateStepsPerBar( aStepsPerBar );
}

void SequencerController::rewind()
{
    setBufferPosition( AudioEngine::min_buffer_position );
}

void SequencerController::setNotificationMarker( int aPosition )
{
    AudioEngine::marked_buffer_position = aPosition;
}

/**
 * used for intelligent pre-caching, get the BaseCacheableAudioEvents
 * belonging to a specific measure for on-demand caching
 *
 * @param aMeasure {int} the measure containing the events we'd like to precache
 */
void SequencerController::cacheAudioEventsForMeasure( int aMeasure )
{
    int startBufferPos = AudioEngine::samples_per_bar * aMeasure;
    int endBufferPos   = ( startBufferPos + AudioEngine::samples_per_bar ) - 1;

    std::vector<BaseCacheableAudioEvent*>* list = Sequencer::collectCacheableSequencerEvents( startBufferPos, endBufferPos );
    getBulkCacher()->addToQueue( list );

    delete list; // free memory

    if ( getBulkCacher()->hasQueue())
        getBulkCacher()->cacheQueue();
}

BulkCacher* SequencerController::getBulkCacher()
{
    return Sequencer::bulkCacher;
}

/**
 * when bouncing, the writing of buffers into the hardware is omitted
 * for an increase in bouncing speed (otherwise its real time)
 */
void SequencerController::setBounceState( bool aIsBouncing, int aMaxBuffers, char* aOutputFile )
{
    AudioEngine::bouncing = aIsBouncing;

    if ( AudioEngine::bouncing )
    {
        AudioEngine::bufferPosition = 0;
        AudioEngine::stepPosition   = 0;
    }
    setRecordingState( aIsBouncing, aMaxBuffers, aOutputFile );
}

/**
 * Record the output of the sequencer onto storage
 *
 * aRecording        {bool} toggles the recording state
 * aMaxBuffers        {int} the total recorded buffer size to store in memory
 *                          before writing the recorded snippet as .WAV file into
 *                          the given output file's directory.
 * aOutputDirectory {char*} name of the output WAV file to generate when recording completes
 *                          (when recording state is disabled), this will concatenate all snippets.
 */
void SequencerController::setRecordingState( bool aRecording, int aMaxBuffers, char* aOutputFile )
{
    // in case Sequencer was recording input from the Android device, halt recording of input
    if ( AudioEngine::recordInputToDisk )
        setRecordingFromDeviceState( false, 0, ( char* ) "\0" );

    bool wasRecording               = AudioEngine::recordOutputToDisk;
    AudioEngine::recordOutputToDisk = aRecording;

    if ( AudioEngine::recordOutputToDisk )
    {
        DiskWriter::prepare(
            std::string( aOutputFile ), roundTo( aMaxBuffers, AudioEngineProps::BUFFER_SIZE ),
            AudioEngineProps::OUTPUT_CHANNELS
        );
    }
    else if ( wasRecording )
    {
        // recording halted, write currently recording snippet into file
        // and concatenate all recorded snippets into the requested output file name
        // we can do this synchronously as this method is called from outside the
        // rendering thread and thus won't lead to buffer under runs

        DiskWriter::writeBufferToFile( DiskWriter::currentBufferIndex, false );

        if ( DiskWriter::finish())
            Notifier::broadcast( Notifications::RECORDING_COMPLETED );
    }
}

/**
 * record audio from the Androids input channel, this stores only the incoming audio
 * not the remaining audio processed / generated by the engine
 *
 * aRecording        {bool} toggles the recording state
 * aMaxBuffers        {int} the total recorded buffer size to store in memory
 *                          before writing the recorded contents as .WAV file into
 *                          the given output directory.
 * aOutputDirectory {char*} name of the folder to write each snippet into
 */
void SequencerController::setRecordingFromDeviceState( bool aRecording, int aMaxBuffers, char* aOutputFile )
{
    // in case Sequencer was recording its output, halt recording of output
    if ( AudioEngine::recordOutputToDisk )
        setRecordingState( false, 0, ( char* ) "\0" );

    bool wasRecording              = AudioEngine::recordInputToDisk;
    AudioEngine::recordInputToDisk = aRecording;

    if ( AudioEngine::recordInputToDisk )
    {
        DiskWriter::prepare(
            std::string( aOutputFile ), roundTo( aMaxBuffers, AudioEngineProps::BUFFER_SIZE ),
            AudioEngineProps::INPUT_CHANNELS
        );
    }
    else if ( wasRecording )
    {
        // recording halted, write currently recording snippet into file
        // and concatenate all recorded snippets into the requested output file name
        // we can do this synchronously as this method is called from outside the
        // rendering thread and thus won't lead to buffer under runs

        DiskWriter::writeBufferToFile( DiskWriter::currentBufferIndex, false );

        if ( DiskWriter::finish())
            Notifier::broadcast( Notifications::RECORDING_COMPLETED );
    }
}

/**
 * Save the contents of the snippet at given buffer index
 * onto storage. This should be invoked from a thread separate to the
 * audio rendering thread to prevent buffer under runs from happening
 */
void SequencerController::saveRecordedSnippet( int snippetBufferIndex )
{
    DiskWriter::writeBufferToFile( snippetBufferIndex, true );
}

} // E.O namespace MWEngine
<commit_msg>Removed global play operation from SequencerController destructor<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2013-2019 Igor Zinken - http://www.igorski.nl
 *
 * 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 "sequencercontroller.h"
#include "sequencer.h"
#include "audioengine.h"
#include <definitions/notifications.h>
#include <messaging/notifier.h>
#include <utilities/utils.h>
#include <utilities/diskwriter.h>
#include <utilities/volumeutil.h>

namespace MWEngine {

/* constructor / destructor */

SequencerController::SequencerController()
{
    // by default, function as a sixteen step sequencer

    stepsPerBar = 16;
    AudioEngine::max_step_position = stepsPerBar - 1;
};

SequencerController::~SequencerController()
{
    // nothing allocated here...
};

/* public methods */

void SequencerController::prepare( float aQueuedTempo, int aTimeSigBeatAmount, int aTimeSigBeatUnit )
{
    // calculate buffers and ranges
    if ( aQueuedTempo > 0 )
    {
        setTempo( aQueuedTempo, aTimeSigBeatAmount, aTimeSigBeatUnit );
        AudioEngine::handleTempoUpdate( aQueuedTempo, false );   // just to initialize all buffer sizes
        setLoopRange( 0, ( AudioEngine::amount_of_bars * AudioEngine::samples_per_bar ) - 1, stepsPerBar );
    }
};

float SequencerController::getTempo()
{
    return AudioEngine::tempo;
}

void SequencerController::setTempo( float aTempo, int aTimeSigBeatAmount, int aTimeSigBeatUnit )
{
    AudioEngine::queuedTempo = aTempo;

    AudioEngine::queuedTime_sig_beat_amount = aTimeSigBeatAmount;
    AudioEngine::queuedTime_sig_beat_unit   = aTimeSigBeatUnit;
}

void SequencerController::setTempoNow( float aTempo, int aTimeSigBeatAmount, int aTimeSigBeatUnit )
{
    setTempo( aTempo, aTimeSigBeatAmount, aTimeSigBeatUnit );
    updateStepsPerBar( stepsPerBar );
    AudioEngine::handleTempoUpdate( AudioEngine::queuedTempo, true );
}

void SequencerController::setVolume( float aVolume )
{
    AudioEngine::volume = VolumeUtil::toLog( aVolume );
}

void SequencerController::setPlaying( bool aIsPlaying )
{
    Sequencer::playing = aIsPlaying;
}

void SequencerController::setLoopRange( int aStartPosition, int aEndPosition )
{
    setLoopRange( aStartPosition, aEndPosition, stepsPerBar );
}

/**
 * make the sequencer loop between two given points
 *
 * @param aStartPosition {int} buffer offset of the loops startpoint (starts at 0 !)
 * @param aEndPosition   {int} buffer offset of the loops endpoint
 * @param aStepsPerBar   {int} the amount of individual segments the sequencer subdivides a single bar into
 *                             this is used for periodic notifications when the sequencer switches step
 */
void SequencerController::setLoopRange( int aStartPosition, int aEndPosition, int aStepsPerBar )
{
    AudioEngine::min_buffer_position = aStartPosition;
    AudioEngine::max_buffer_position = aEndPosition;

    // keep current buffer read pointer within the new loop range
    if ( AudioEngine::bufferPosition < AudioEngine::min_buffer_position ||
         AudioEngine::bufferPosition > AudioEngine::max_buffer_position )
    {
        AudioEngine::bufferPosition = AudioEngine::min_buffer_position;
    }

    AudioEngine::min_step_position = ( int ) round(( aStartPosition / AudioEngine::samples_per_bar ) * aStepsPerBar );
    AudioEngine::max_step_position = ( int ) round(((( float ) aEndPosition / ( float ) AudioEngine::samples_per_bar ) * aStepsPerBar ) - 1 );

    // keep current sequencer step within the new loop range
    if ( AudioEngine::stepPosition < AudioEngine::min_step_position ||
         AudioEngine::stepPosition > AudioEngine::max_step_position )
    {
        AudioEngine::stepPosition = AudioEngine::min_step_position;
    }
    updateStepsPerBar( aStepsPerBar );
}

int SequencerController::getStepPosition()
{
    return AudioEngine::stepPosition;
}

int SequencerController::getBufferPosition()
{
    return AudioEngine::bufferPosition;
}

void SequencerController::setBufferPosition( int aPosition )
{
    // keep position within the sequences range (see "setLoopRange")

    if ( aPosition < AudioEngine::min_buffer_position ||
         aPosition > AudioEngine::max_buffer_position )
    {
        aPosition = AudioEngine::min_buffer_position;
    }

    AudioEngine::bufferPosition = aPosition;
    AudioEngine::stepPosition   = ( aPosition / AudioEngine::samples_per_bar ) * stepsPerBar;

    Notifier::broadcast( Notifications::SEQUENCER_POSITION_UPDATED );
}

int SequencerController::getSamplesPerBeat()
{
    return AudioEngine::samples_per_beat;
}

int SequencerController::getSamplesPerStep()
{
    return ( int ) AudioEngine::samples_per_step;
}

int SequencerController::getSamplesPerBar()
{
    return AudioEngine::samples_per_bar;
}

int SequencerController::getTimeSigBeatAmount()
{
    return AudioEngine::time_sig_beat_amount;
}

int SequencerController::getTimeSigBeatUnit()
{
    return AudioEngine::time_sig_beat_unit;
}

void SequencerController::updateStepsPerBar( int aStepsPerBar )
{
    stepsPerBar = aStepsPerBar;
    AudioEngine::max_step_position = ( stepsPerBar * AudioEngine::amount_of_bars ) - 1;
    AudioEngine::beat_subdivision  = stepsPerBar / AudioEngine::time_sig_beat_amount;

    // keep current sequencer step within the new loop range

    if ( AudioEngine::stepPosition > AudioEngine::max_step_position )
        AudioEngine::stepPosition = AudioEngine::min_step_position;
}

void SequencerController::updateMeasures( int aValue, int aStepsPerBar )
{
    AudioEngine::amount_of_bars      = aValue;
    AudioEngine::max_buffer_position = ( AudioEngine::samples_per_bar * AudioEngine::amount_of_bars ) - 1;

    updateStepsPerBar( aStepsPerBar );
}

void SequencerController::rewind()
{
    setBufferPosition( AudioEngine::min_buffer_position );
}

void SequencerController::setNotificationMarker( int aPosition )
{
    AudioEngine::marked_buffer_position = aPosition;
}

/**
 * used for intelligent pre-caching, get the BaseCacheableAudioEvents
 * belonging to a specific measure for on-demand caching
 *
 * @param aMeasure {int} the measure containing the events we'd like to precache
 */
void SequencerController::cacheAudioEventsForMeasure( int aMeasure )
{
    int startBufferPos = AudioEngine::samples_per_bar * aMeasure;
    int endBufferPos   = ( startBufferPos + AudioEngine::samples_per_bar ) - 1;

    std::vector<BaseCacheableAudioEvent*>* list = Sequencer::collectCacheableSequencerEvents( startBufferPos, endBufferPos );
    getBulkCacher()->addToQueue( list );

    delete list; // free memory

    if ( getBulkCacher()->hasQueue())
        getBulkCacher()->cacheQueue();
}

BulkCacher* SequencerController::getBulkCacher()
{
    return Sequencer::bulkCacher;
}

/**
 * when bouncing, the writing of buffers into the hardware is omitted
 * for an increase in bouncing speed (otherwise its real time)
 */
void SequencerController::setBounceState( bool aIsBouncing, int aMaxBuffers, char* aOutputFile )
{
    AudioEngine::bouncing = aIsBouncing;

    if ( AudioEngine::bouncing )
    {
        AudioEngine::bufferPosition = 0;
        AudioEngine::stepPosition   = 0;
    }
    setRecordingState( aIsBouncing, aMaxBuffers, aOutputFile );
}

/**
 * Record the output of the sequencer onto storage
 *
 * aRecording        {bool} toggles the recording state
 * aMaxBuffers        {int} the total recorded buffer size to store in memory
 *                          before writing the recorded snippet as .WAV file into
 *                          the given output file's directory.
 * aOutputDirectory {char*} name of the output WAV file to generate when recording completes
 *                          (when recording state is disabled), this will concatenate all snippets.
 */
void SequencerController::setRecordingState( bool aRecording, int aMaxBuffers, char* aOutputFile )
{
    // in case Sequencer was recording input from the Android device, halt recording of input
    if ( AudioEngine::recordInputToDisk )
        setRecordingFromDeviceState( false, 0, ( char* ) "\0" );

    bool wasRecording               = AudioEngine::recordOutputToDisk;
    AudioEngine::recordOutputToDisk = aRecording;

    if ( AudioEngine::recordOutputToDisk )
    {
        DiskWriter::prepare(
            std::string( aOutputFile ), roundTo( aMaxBuffers, AudioEngineProps::BUFFER_SIZE ),
            AudioEngineProps::OUTPUT_CHANNELS
        );
    }
    else if ( wasRecording )
    {
        // recording halted, write currently recording snippet into file
        // and concatenate all recorded snippets into the requested output file name
        // we can do this synchronously as this method is called from outside the
        // rendering thread and thus won't lead to buffer under runs

        DiskWriter::writeBufferToFile( DiskWriter::currentBufferIndex, false );

        if ( DiskWriter::finish())
            Notifier::broadcast( Notifications::RECORDING_COMPLETED );
    }
}

/**
 * record audio from the Androids input channel, this stores only the incoming audio
 * not the remaining audio processed / generated by the engine
 *
 * aRecording        {bool} toggles the recording state
 * aMaxBuffers        {int} the total recorded buffer size to store in memory
 *                          before writing the recorded contents as .WAV file into
 *                          the given output directory.
 * aOutputDirectory {char*} name of the folder to write each snippet into
 */
void SequencerController::setRecordingFromDeviceState( bool aRecording, int aMaxBuffers, char* aOutputFile )
{
    // in case Sequencer was recording its output, halt recording of output
    if ( AudioEngine::recordOutputToDisk )
        setRecordingState( false, 0, ( char* ) "\0" );

    bool wasRecording              = AudioEngine::recordInputToDisk;
    AudioEngine::recordInputToDisk = aRecording;

    if ( AudioEngine::recordInputToDisk )
    {
        DiskWriter::prepare(
            std::string( aOutputFile ), roundTo( aMaxBuffers, AudioEngineProps::BUFFER_SIZE ),
            AudioEngineProps::INPUT_CHANNELS
        );
    }
    else if ( wasRecording )
    {
        // recording halted, write currently recording snippet into file
        // and concatenate all recorded snippets into the requested output file name
        // we can do this synchronously as this method is called from outside the
        // rendering thread and thus won't lead to buffer under runs

        DiskWriter::writeBufferToFile( DiskWriter::currentBufferIndex, false );

        if ( DiskWriter::finish())
            Notifier::broadcast( Notifications::RECORDING_COMPLETED );
    }
}

/**
 * Save the contents of the snippet at given buffer index
 * onto storage. This should be invoked from a thread separate to the
 * audio rendering thread to prevent buffer under runs from happening
 */
void SequencerController::saveRecordedSnippet( int snippetBufferIndex )
{
    DiskWriter::writeBufferToFile( snippetBufferIndex, true );
}

} // E.O namespace MWEngine
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Name:      vimacros.cpp
// Purpose:   Implementation of class wxExViMacros
// Author:    Anton van Wezenbeek
// Copyright: (c) 2012 Anton van Wezenbeek
////////////////////////////////////////////////////////////////////////////////

#include <algorithm>
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/stdpaths.h>
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include <wx/utils.h>
#include <wx/extension/vimacros.h>
#include <wx/extension/ex.h>
#include <wx/extension/stc.h>

#if wxUSE_GUI

bool wxExViMacros::m_IsExpand = false;
bool wxExViMacros::m_IsModified = false;
bool wxExViMacros::m_IsPlayback = false;

std::map <wxString, std::vector< wxString > > wxExViMacros::m_Macros;
std::map <wxString, wxExVariable > wxExViMacros::m_Variables;

wxExViMacros::wxExViMacros()
  : m_IsRecording(false)
{
}

void wxExViMacros::AskForInput()
{
  for (std::map<wxString, wxExVariable >::iterator it =
    m_Variables.begin();
    it != m_Variables.end();
    ++it)
  {
    it->second.AskForInput();
  }
}

const wxString wxExViMacros::Decode(const wxString& text)
{
  long c;

  if (text.ToLong(&c))
  {
    return char(c);
  }

  return text;
}

const wxString wxExViMacros::Encode(const wxString& text, bool& encoded)
{
  if (text.length() == 1)
  {
    int c = text[0];

    // Encode control characters, and whitespace.
    if (iscntrl(c) || isspace(c))
    {
      encoded = true;
      return wxString::Format("%d", c);
    }
  }

  return text;
}

bool wxExViMacros::Expand(wxExEx* ex, const wxString& variable)
{
  std::map<wxString, wxExVariable>::iterator it = m_Variables.find(variable);

  bool ok;

  if (it == m_Variables.end())
  {
    std::pair<std::map<wxString, wxExVariable>::iterator, bool> ret =
      m_Variables.insert(std::make_pair(variable, wxExVariable(variable)));

    wxLogStatus(_("Added variable") + ": "  +  variable);

    ok = ret.first->second.Expand(ex);

    if (ret.first->second.IsModified())
    {
      m_IsModified = true;
    }
  }
  else
  {
    ok = it->second.Expand(ex);

    if (it->second.IsModified())
    {
      m_IsModified = true;
    }
  }

  if (!ok)
  {
    wxLogStatus(_("Could not expand variable") + ": "  +  variable);
  }

  return ok;
}

bool wxExViMacros::Expand(wxExEx* ex, const wxString& variable, wxString& value)
{
  std::map<wxString, wxExVariable>::iterator it = m_Variables.find(variable);

  bool ok;

  if (it == m_Variables.end())
  {
    std::pair<std::map<wxString, wxExVariable>::iterator, bool> ret =
      m_Variables.insert(std::make_pair(variable, wxExVariable(variable)));

    wxLogStatus(_("Added variable") + ": "  +  variable);

    ok = ret.first->second.Expand(ex, value);

    // If we are expanding, one input is enough.
    if (m_IsExpand)
    {
      ret.first->second.SkipInput();
    }

    if (ret.first->second.IsModified())
    {
      m_IsModified = true;
    }
  }
  else
  {
    ok = it->second.Expand(ex, value);

    // If we are expanding, one input is enough.
    if (m_IsExpand)
    {
      it->second.SkipInput();
    }

    if (it->second.IsModified())
    {
      m_IsModified = true;
    }
  }

  if (!ok)
  {
    wxLogStatus(_("Could not expand variable") + ": "  +  variable);
  }

  return ok;
}

bool wxExViMacros::ExpandTemplate(
  wxExEx* ex, const wxExVariable& v, wxString& expanded)
{
  if (!m_IsExpand)
  {
    m_IsExpand = true;
    AskForInput();
  }

  // Read the file (file name is in m_Value), expand
  // all macro variables in it, and set expanded.
  const wxFileName filename(
#ifdef wxExUSE_PORTABLE
      wxPathOnly(wxStandardPaths::Get().GetExecutablePath())
#else
      wxStandardPaths::Get().GetUserDataDir()
#endif
      + wxFileName::GetPathSeparator() + v.GetValue());

  wxFileInputStream input(filename.GetFullPath());

  if (!input.IsOk())
  {
    wxLogError("Could not open template file: " + filename.GetFullPath());
    return false;
  }

  wxTextInputStream text(input);

  while (input.IsOk() && !input.Eof())
  {
    const wxChar c = text.GetChar();

    if (c != '@')
    {
      expanded += c;
    }
    else
    {
      wxString variable;
      bool completed = false;

      while (input.IsOk() && !input.Eof() && !completed)
      {
        const wxChar c = text.GetChar();

        if (c != '@')
        {
          variable += c;
        }
        else
        {
          completed = true;
        }
      }

      if (!completed)
      {
        return false;
      }

      // Prevent recursion.
      if (variable == v.GetName())
      {
        return false;
      }

      wxString value;

      if (!Expand(ex, variable, value))
      {
        return false;
      }

      expanded += value;
    }
  }

  m_IsExpand = false;

  // Set back to normal value.
  AskForInput();

  return true;
}

const wxArrayString wxExViMacros::Get() const
{
  wxArrayString as;

  for (
    std::map<wxString, std::vector<wxString> >::const_iterator it =
      m_Macros.begin();
    it != m_Macros.end();
    ++it)
  {
    as.Add(it->first);
  }

  return as;
}

const std::vector< wxString > wxExViMacros::Get(const wxString& macro) const
{
  std::map<wxString, std::vector< wxString > >::const_iterator it =
    m_Macros.find(macro);

  if (it != m_Macros.end())
  {
    return it->second;
  }
  else
  {
    std::vector<wxString> empty;
    return empty;
  }
}

const wxFileName wxExViMacros::GetFileName()
{
  return wxFileName(
#ifdef wxExUSE_PORTABLE
      wxPathOnly(wxStandardPaths::Get().GetExecutablePath())
#else
      wxStandardPaths::Get().GetUserDataDir()
#endif
      + wxFileName::GetPathSeparator() + "macros.xml");
}

bool wxExViMacros::IsRecorded(const wxString& macro) const
{
  if (macro.empty())
  {
    return !m_Macros.empty();
  }
  else
  {
    return !Get(macro).empty();
  }
}

bool wxExViMacros::Load(wxXmlDocument& doc)
{
  // This test is to prevent showing an error if the macro file does not exist,
  // as this is not required.
  if (!GetFileName().FileExists())
  {
    return false;
  }

  if (!doc.Load(GetFileName().GetFullPath()))
  {
    return false;
  }

  return true;
}

bool wxExViMacros::LoadDocument()
{
  wxXmlDocument doc;

  if (!Load(doc))
  {
    return false;
  }

  // If modified is true, then you did not save previous
  // recordings.
  // We assume that this is your choice, so we reset the member.
  m_IsModified = false;

  m_Macros.clear();
  m_Variables.clear();

  wxXmlNode* root = doc.GetRoot();
  wxXmlNode* child = root->GetChildren();

  while (child)
  {
    if (child->GetName() == "macro")
    {
      std::vector<wxString> v;

      wxXmlNode* command = child->GetChildren();

      while (command)
      {
        if (command->GetAttribute("encoded", "false") == "true")
        {
          v.push_back(Decode(command->GetNodeContent()));
        }
        else
        {
          v.push_back(command->GetNodeContent());
        }

        command = command->GetNext();
      }

      std::map<wxString, std::vector< wxString >>::const_iterator it =
        m_Macros.find(child->GetAttribute("name"));

      if (it != m_Macros.end())
      {
        wxLogError("Duplicate macro: %s on line: %d",
          child->GetAttribute("name"),
          child->GetLineNumber());
      }
      else
      {
        m_Macros.insert(std::make_pair(child->GetAttribute("name"), v));
      }
    }
    else if (child->GetName() == "variable")
    {
      wxExVariable variable(child);

      std::map<wxString, wxExVariable>::const_iterator it = m_Variables.find(variable.GetName());

      if (it != m_Variables.end())
      {
        wxLogError("Duplicate variable: %s on line: %d",
         variable.GetName(),
         child->GetLineNumber());
      }
      else
      {
        m_Variables.insert(std::make_pair(variable.GetName(), variable));
      }
    }

    child = child->GetNext();
  }

  return true;
}

bool wxExViMacros::Playback(wxExEx* ex, const wxString& macro, int repeat)
{
  if (!IsRecorded(macro) || macro.empty())
  {
    wxLogStatus(_("Unknown macro") + ": "  +  macro);
    return false;
  }

  if (m_IsPlayback && macro == m_Macro)
  {
    wxLogStatus(_("Already playing back"));
    return false;
  }

  if (repeat <= 0)
  {
    return false;
  }

  ex->GetSTC()->BeginUndoAction();

  bool stop = false;

  m_IsPlayback = true;

  m_Macro = macro;

  wxBusyCursor;

  AskForInput();

  for (int i = 0; i < repeat; i++)
  {
    for (
      std::vector<wxString>::const_iterator it = m_Macros[macro].begin();
      it != m_Macros[macro].end() && !stop;
      ++it)
    {
      stop = !ex->Command(*it);

      if (stop)
      {
        wxLogStatus(_("Macro aborted at '") + *it + "'");
      }
    }
  }

  ex->GetSTC()->EndUndoAction();

  if (!stop)
  {
    wxLogStatus(_("Macro played back"));
  }

  m_IsPlayback = false;

  return !stop;
}

void wxExViMacros::Record(const wxString& text, bool new_command)
{
  if (!m_IsRecording)
  {
    return;
  }

  m_IsModified = true;

  if (new_command)
  {
    m_Macros[m_Macro].push_back(text);
  }
  else
  {
    if (m_Macros[m_Macro].empty())
    {
      m_Macros[m_Macro].push_back(wxEmptyString);
    }

    m_Macros[m_Macro].back() += text;
  }
}

bool wxExViMacros::SaveDocument(bool only_if_modified)
{
  if (!m_IsModified && only_if_modified)
  {
    return false;
  }

  wxXmlDocument doc;

  if (!Load(doc))
  {
    return false;
  }

  wxXmlNode* root = doc.GetRoot();
  wxXmlNode* child;

  while (child = root->GetChildren())
  {
    root->RemoveChild(child);
    delete child;
  }

  for (
    std::map<wxString, std::vector<wxString> >::reverse_iterator it =
      m_Macros.rbegin();
    it != m_Macros.rend();
    ++it)
  {
    wxXmlNode* element = new wxXmlNode(root, wxXML_ELEMENT_NODE, "macro");
    element->AddAttribute("name", it->first);

    for (
      std::vector<wxString>::reverse_iterator it2 = it->second.rbegin();
      it2 != it->second.rend();
      ++it2)
    {
      bool encoded = false;
      const wxString contents(Encode(*it2, encoded));

      wxXmlNode* cmd = new wxXmlNode(element, wxXML_ELEMENT_NODE, "command");
      new wxXmlNode(cmd, wxXML_TEXT_NODE, "", contents);

      if (encoded)
      {
        cmd->AddAttribute("encoded", "true");
      }
    }
  }

  for (
    std::map< wxString, wxExVariable >::reverse_iterator it2 =
      m_Variables.rbegin();
    it2 != m_Variables.rend();
    ++it2)
  {
    wxXmlNode* element = new wxXmlNode(root, wxXML_ELEMENT_NODE, "variable");
    it2->second.Save(element);
  }

  const bool ok = doc.Save(GetFileName().GetFullPath());

  if (ok)
  {
    m_IsModified = false;
  }

  return ok;
}

void wxExViMacros::StartRecording(const wxString& macro)
{
  if (m_IsRecording || macro.empty())
  {
    return;
  }

  m_IsRecording = true;

  if (macro.size() == 1)
  {
    // We only use lower case macro's, to be able to
    // append to them using qA.
    m_Macro = macro.Lower();

    // Clear macro if it is lower case
    // (otherwise append to the macro).
    if (wxIslower(macro[0]))
    {
      m_Macros[m_Macro].clear();
    }
  }
  else
  {
    m_Macro = macro;
    m_Macros[m_Macro].clear();
  }

  wxLogStatus(_("Macro recording"));
}

void wxExViMacros::StopRecording()
{
  if (!m_IsRecording)
  {
    return;
  }

  m_IsRecording = false;

  if (!Get(m_Macro).empty())
  {
    wxLogStatus(wxString::Format(_("Macro '%s' is recorded"), m_Macro.c_str()));
  }
  else
  {
    wxLogStatus(_("Ready"));
  }
}

#endif // wxUSE_GUI
<commit_msg>fixed compile error<commit_after>////////////////////////////////////////////////////////////////////////////////
// Name:      vimacros.cpp
// Purpose:   Implementation of class wxExViMacros
// Author:    Anton van Wezenbeek
// Copyright: (c) 2012 Anton van Wezenbeek
////////////////////////////////////////////////////////////////////////////////

#include <algorithm>
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/stdpaths.h>
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include <wx/utils.h>
#include <wx/extension/vimacros.h>
#include <wx/extension/ex.h>
#include <wx/extension/stc.h>

#if wxUSE_GUI

bool wxExViMacros::m_IsExpand = false;
bool wxExViMacros::m_IsModified = false;
bool wxExViMacros::m_IsPlayback = false;

std::map <wxString, std::vector< wxString > > wxExViMacros::m_Macros;
std::map <wxString, wxExVariable > wxExViMacros::m_Variables;

wxExViMacros::wxExViMacros()
  : m_IsRecording(false)
{
}

void wxExViMacros::AskForInput()
{
  for (std::map<wxString, wxExVariable >::iterator it =
    m_Variables.begin();
    it != m_Variables.end();
    ++it)
  {
    it->second.AskForInput();
  }
}

const wxString wxExViMacros::Decode(const wxString& text)
{
  long c;

  if (text.ToLong(&c))
  {
    return char(c);
  }

  return text;
}

const wxString wxExViMacros::Encode(const wxString& text, bool& encoded)
{
  if (text.length() == 1)
  {
    int c = text[0];

    // Encode control characters, and whitespace.
    if (iscntrl(c) || isspace(c))
    {
      encoded = true;
      return wxString::Format("%d", c);
    }
  }

  return text;
}

bool wxExViMacros::Expand(wxExEx* ex, const wxString& variable)
{
  std::map<wxString, wxExVariable>::iterator it = m_Variables.find(variable);

  bool ok;

  if (it == m_Variables.end())
  {
    std::pair<std::map<wxString, wxExVariable>::iterator, bool> ret =
      m_Variables.insert(std::make_pair(variable, wxExVariable(variable)));

    wxLogStatus(_("Added variable") + ": "  +  variable);

    ok = ret.first->second.Expand(ex);

    if (ret.first->second.IsModified())
    {
      m_IsModified = true;
    }
  }
  else
  {
    ok = it->second.Expand(ex);

    if (it->second.IsModified())
    {
      m_IsModified = true;
    }
  }

  if (!ok)
  {
    wxLogStatus(_("Could not expand variable") + ": "  +  variable);
  }

  return ok;
}

bool wxExViMacros::Expand(wxExEx* ex, const wxString& variable, wxString& value)
{
  std::map<wxString, wxExVariable>::iterator it = m_Variables.find(variable);

  bool ok;

  if (it == m_Variables.end())
  {
    std::pair<std::map<wxString, wxExVariable>::iterator, bool> ret =
      m_Variables.insert(std::make_pair(variable, wxExVariable(variable)));

    wxLogStatus(_("Added variable") + ": "  +  variable);

    ok = ret.first->second.Expand(ex, value);

    // If we are expanding, one input is enough.
    if (m_IsExpand)
    {
      ret.first->second.SkipInput();
    }

    if (ret.first->second.IsModified())
    {
      m_IsModified = true;
    }
  }
  else
  {
    ok = it->second.Expand(ex, value);

    // If we are expanding, one input is enough.
    if (m_IsExpand)
    {
      it->second.SkipInput();
    }

    if (it->second.IsModified())
    {
      m_IsModified = true;
    }
  }

  if (!ok)
  {
    wxLogStatus(_("Could not expand variable") + ": "  +  variable);
  }

  return ok;
}

bool wxExViMacros::ExpandTemplate(
  wxExEx* ex, const wxExVariable& v, wxString& expanded)
{
  if (!m_IsExpand)
  {
    m_IsExpand = true;
    AskForInput();
  }

  // Read the file (file name is in m_Value), expand
  // all macro variables in it, and set expanded.
  const wxFileName filename(
#ifdef wxExUSE_PORTABLE
      wxPathOnly(wxStandardPaths::Get().GetExecutablePath())
#else
      wxStandardPaths::Get().GetUserDataDir()
#endif
      + wxFileName::GetPathSeparator() + v.GetValue());

  wxFileInputStream input(filename.GetFullPath());

  if (!input.IsOk())
  {
    wxLogError("Could not open template file: " + filename.GetFullPath());
    return false;
  }

  wxTextInputStream text(input);

  while (input.IsOk() && !input.Eof())
  {
    const wxChar c = text.GetChar();

    if (c != '@')
    {
      expanded += c;
    }
    else
    {
      wxString variable;
      bool completed = false;

      while (input.IsOk() && !input.Eof() && !completed)
      {
        const wxChar c = text.GetChar();

        if (c != '@')
        {
          variable += c;
        }
        else
        {
          completed = true;
        }
      }

      if (!completed)
      {
        return false;
      }

      // Prevent recursion.
      if (variable == v.GetName())
      {
        return false;
      }

      wxString value;

      if (!Expand(ex, variable, value))
      {
        return false;
      }

      expanded += value;
    }
  }

  m_IsExpand = false;

  // Set back to normal value.
  AskForInput();

  return true;
}

const wxArrayString wxExViMacros::Get() const
{
  wxArrayString as;

  for (
    std::map<wxString, std::vector<wxString> >::const_iterator it =
      m_Macros.begin();
    it != m_Macros.end();
    ++it)
  {
    as.Add(it->first);
  }

  return as;
}

const std::vector< wxString > wxExViMacros::Get(const wxString& macro) const
{
  std::map<wxString, std::vector< wxString > >::const_iterator it =
    m_Macros.find(macro);

  if (it != m_Macros.end())
  {
    return it->second;
  }
  else
  {
    std::vector<wxString> empty;
    return empty;
  }
}

const wxFileName wxExViMacros::GetFileName()
{
  return wxFileName(
#ifdef wxExUSE_PORTABLE
      wxPathOnly(wxStandardPaths::Get().GetExecutablePath())
#else
      wxStandardPaths::Get().GetUserDataDir()
#endif
      + wxFileName::GetPathSeparator() + "macros.xml");
}

bool wxExViMacros::IsRecorded(const wxString& macro) const
{
  if (macro.empty())
  {
    return !m_Macros.empty();
  }
  else
  {
    return !Get(macro).empty();
  }
}

bool wxExViMacros::Load(wxXmlDocument& doc)
{
  // This test is to prevent showing an error if the macro file does not exist,
  // as this is not required.
  if (!GetFileName().FileExists())
  {
    return false;
  }

  if (!doc.Load(GetFileName().GetFullPath()))
  {
    return false;
  }

  return true;
}

bool wxExViMacros::LoadDocument()
{
  wxXmlDocument doc;

  if (!Load(doc))
  {
    return false;
  }

  // If modified is true, then you did not save previous
  // recordings.
  // We assume that this is your choice, so we reset the member.
  m_IsModified = false;

  m_Macros.clear();
  m_Variables.clear();

  wxXmlNode* root = doc.GetRoot();
  wxXmlNode* child = root->GetChildren();

  while (child)
  {
    if (child->GetName() == "macro")
    {
      std::vector<wxString> v;

      wxXmlNode* command = child->GetChildren();

      while (command)
      {
        if (command->GetAttribute("encoded", "false") == "true")
        {
          v.push_back(Decode(command->GetNodeContent()));
        }
        else
        {
          v.push_back(command->GetNodeContent());
        }

        command = command->GetNext();
      }

      std::map<wxString, std::vector< wxString >>::const_iterator it =
        m_Macros.find(child->GetAttribute("name"));

      if (it != m_Macros.end())
      {
        wxLogError("Duplicate macro: %s on line: %d",
          child->GetAttribute("name"),
          child->GetLineNumber());
      }
      else
      {
        m_Macros.insert(std::make_pair(child->GetAttribute("name"), v));
      }
    }
    else if (child->GetName() == "variable")
    {
      wxExVariable variable(child);

      std::map<wxString, wxExVariable>::const_iterator it = m_Variables.find(variable.GetName());

      if (it != m_Variables.end())
      {
        wxLogError("Duplicate variable: %s on line: %d",
         variable.GetName(),
         child->GetLineNumber());
      }
      else
      {
        m_Variables.insert(std::make_pair(variable.GetName(), variable));
      }
    }

    child = child->GetNext();
  }

  return true;
}

bool wxExViMacros::Playback(wxExEx* ex, const wxString& macro, int repeat)
{
  if (!IsRecorded(macro) || macro.empty())
  {
    wxLogStatus(_("Unknown macro") + ": "  +  macro);
    return false;
  }

  if (m_IsPlayback && macro == m_Macro)
  {
    wxLogStatus(_("Already playing back"));
    return false;
  }

  if (repeat <= 0)
  {
    return false;
  }

  ex->GetSTC()->BeginUndoAction();

  bool stop = false;

  m_IsPlayback = true;

  m_Macro = macro;

  wxBusyCursor wait;

  AskForInput();

  for (int i = 0; i < repeat; i++)
  {
    for (
      std::vector<wxString>::const_iterator it = m_Macros[macro].begin();
      it != m_Macros[macro].end() && !stop;
      ++it)
    {
      stop = !ex->Command(*it);

      if (stop)
      {
        wxLogStatus(_("Macro aborted at '") + *it + "'");
      }
    }
  }

  ex->GetSTC()->EndUndoAction();

  if (!stop)
  {
    wxLogStatus(_("Macro played back"));
  }

  m_IsPlayback = false;

  return !stop;
}

void wxExViMacros::Record(const wxString& text, bool new_command)
{
  if (!m_IsRecording)
  {
    return;
  }

  m_IsModified = true;

  if (new_command)
  {
    m_Macros[m_Macro].push_back(text);
  }
  else
  {
    if (m_Macros[m_Macro].empty())
    {
      m_Macros[m_Macro].push_back(wxEmptyString);
    }

    m_Macros[m_Macro].back() += text;
  }
}

bool wxExViMacros::SaveDocument(bool only_if_modified)
{
  if (!m_IsModified && only_if_modified)
  {
    return false;
  }

  wxXmlDocument doc;

  if (!Load(doc))
  {
    return false;
  }

  wxXmlNode* root = doc.GetRoot();
  wxXmlNode* child;

  while (child = root->GetChildren())
  {
    root->RemoveChild(child);
    delete child;
  }

  for (
    std::map<wxString, std::vector<wxString> >::reverse_iterator it =
      m_Macros.rbegin();
    it != m_Macros.rend();
    ++it)
  {
    wxXmlNode* element = new wxXmlNode(root, wxXML_ELEMENT_NODE, "macro");
    element->AddAttribute("name", it->first);

    for (
      std::vector<wxString>::reverse_iterator it2 = it->second.rbegin();
      it2 != it->second.rend();
      ++it2)
    {
      bool encoded = false;
      const wxString contents(Encode(*it2, encoded));

      wxXmlNode* cmd = new wxXmlNode(element, wxXML_ELEMENT_NODE, "command");
      new wxXmlNode(cmd, wxXML_TEXT_NODE, "", contents);

      if (encoded)
      {
        cmd->AddAttribute("encoded", "true");
      }
    }
  }

  for (
    std::map< wxString, wxExVariable >::reverse_iterator it2 =
      m_Variables.rbegin();
    it2 != m_Variables.rend();
    ++it2)
  {
    wxXmlNode* element = new wxXmlNode(root, wxXML_ELEMENT_NODE, "variable");
    it2->second.Save(element);
  }

  const bool ok = doc.Save(GetFileName().GetFullPath());

  if (ok)
  {
    m_IsModified = false;
  }

  return ok;
}

void wxExViMacros::StartRecording(const wxString& macro)
{
  if (m_IsRecording || macro.empty())
  {
    return;
  }

  m_IsRecording = true;

  if (macro.size() == 1)
  {
    // We only use lower case macro's, to be able to
    // append to them using qA.
    m_Macro = macro.Lower();

    // Clear macro if it is lower case
    // (otherwise append to the macro).
    if (wxIslower(macro[0]))
    {
      m_Macros[m_Macro].clear();
    }
  }
  else
  {
    m_Macro = macro;
    m_Macros[m_Macro].clear();
  }

  wxLogStatus(_("Macro recording"));
}

void wxExViMacros::StopRecording()
{
  if (!m_IsRecording)
  {
    return;
  }

  m_IsRecording = false;

  if (!Get(m_Macro).empty())
  {
    wxLogStatus(wxString::Format(_("Macro '%s' is recorded"), m_Macro.c_str()));
  }
  else
  {
    wxLogStatus(_("Ready"));
  }
}

#endif // wxUSE_GUI
<|endoftext|>


<commit_before>/** Copyright (C) 2016, 2017 European Spallation Source ERIC */

#include <common/Producer.h>
#include <cstring>
#include <dlfcn.h>
#include <librdkafka/rdkafkacpp.h>
#include <test/TestBase.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"

int fail = -1; // Dont fail

// Create pointers to the retuned objects
typedef RdKafka::Conf *(*pcreate)(RdKafka::Conf::ConfType);
typedef RdKafka::Producer *(*pcreateprod)(RdKafka::Conf *, std::string &);
typedef RdKafka::Topic *(*pcreatetopic)(RdKafka::Handle *, std::string const &,
                                        RdKafka::Conf *, std::string &);

/**Attmpt to load primary and alternatively secondary symbols using dlsym()
 * Created to cover different compilations of librdkafka (old and new abis)
 * Used for forcing external functions to fail for unit testing purposes
 * exit if none of the symbols can be loaded.
**/
void * loadsyms(const char * primary, const char * secondary) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"

  void * vpsym = dlsym(RTLD_NEXT, primary);

  if (vpsym == NULL) {
    printf("Could not load primary symbol: %s\n", primary);
    printf("Trying secondary...\n");

    dlerror();
    vpsym = dlsym(RTLD_NEXT, secondary);
    if (vpsym == NULL) {
      printf("Could not load secondary symbol: %s\n", secondary);
      printf("Error stubbing kafka functions\n");
      exit(1);
    }
  }
#pragma GCC diagnostic pop
  return vpsym;
}

/** Intercept RdKafka::Conf::create() */
RdKafka::Conf *RdKafka::Conf::create(ConfType type) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
  pcreate real_create = (pcreate)loadsyms(
      "_ZN7RdKafka4Conf6createENS0_8ConfTypeE",
      "_ZN7RdKafka4Conf6createENS0_8ConfTypeE"); // nm -C
#pragma GCC diagnostic pop

  if (fail != -1 && type == fail) {
    printf("Forcing RdKafka::Conf::create() to fail\n");
    return nullptr;
  } else {
    return real_create(type);
  }
}

/** Intercept RdKafka::Producer::create() */
RdKafka::Producer *RdKafka::Producer::create(RdKafka::Conf *type,
                                             std::string &str) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
  pcreateprod real_create = (pcreateprod)loadsyms(
      "_ZN7RdKafka8Producer6createEPNS_4ConfERSs",
      "_ZN7RdKafka8Producer6createEPNS_4ConfERNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"); // nm -C librdkafka.a
#pragma GCC diagnostic pop

  if (fail == 777) {
    printf("Forcing RdKafka::Producer::create() to fail\n");
    return nullptr;
  } else {
    return real_create(type, str);
  }
}

/** Intercept RdKafka::Topic::create() */
RdKafka::Topic *RdKafka::Topic::create(RdKafka::Handle *handle,
                                       std::string const &topic,
                                       RdKafka::Conf *conf, std::string &str) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
  pcreatetopic real_create = (pcreatetopic)loadsyms(
      "_ZN7RdKafka5Topic6createEPNS_6HandleERKSsPNS_4ConfERSs",
      "_ZN7RdKafka5Topic6createEPNS_6HandleERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPNS_4ConfERS8_");
#pragma GCC diagnostic pop

  if (fail == 888) {
    printf("Forcing RdKafka::Topic::create() to fail\n");
    return nullptr;
  } else {
    return real_create(handle, topic, conf, str);
  }
}

//
// Google test code below
//

class ProducerTest : public TestBase {
  virtual void SetUp() { fail = -1; }
  virtual void TearDown() {}
};

TEST_F(ProducerTest, ConstructorOK) {
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_NO_ERROR);
  ASSERT_EQ(prod.dr_errors, 0);
  ASSERT_EQ(prod.dr_noerrors, 0);
  ASSERT_EQ(prod.ev_errors, 0);
  ASSERT_EQ(prod.stats.ev_others, 0);
}

TEST_F(ProducerTest, CreateConfGlobalFail) {
  fail = RdKafka::Conf::CONF_GLOBAL;
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_UNKNOWN);
}

TEST_F(ProducerTest, CreateConfTopicFail) {
  fail = RdKafka::Conf::CONF_TOPIC;
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_UNKNOWN);
}

TEST_F(ProducerTest, CreateProducerFail) {
  fail = 777;
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_UNKNOWN);
}

TEST_F(ProducerTest, CreateTopicFail) {
  fail = 888;
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_UNKNOWN);

}

TEST_F(ProducerTest, ProducerFail) {
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce((char *)10000, 100000000);
  ASSERT_EQ(ret, RdKafka::ERR_MSG_SIZE_TOO_LARGE);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

#pragma GCC diagnostic pop
<commit_msg>fixing unit test<commit_after>/** Copyright (C) 2016, 2017 European Spallation Source ERIC */

#include <common/Producer.h>
#include <cstring>
#include <dlfcn.h>
#include <librdkafka/rdkafkacpp.h>
#include <test/TestBase.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"

int fail = -1; // Dont fail

// Create pointers to the retuned objects
typedef RdKafka::Conf *(*pcreate)(RdKafka::Conf::ConfType);
typedef RdKafka::Producer *(*pcreateprod)(RdKafka::Conf *, std::string &);
typedef RdKafka::Topic *(*pcreatetopic)(RdKafka::Handle *, std::string const &,
                                        RdKafka::Conf *, std::string &);

/**Attmpt to load primary and alternatively secondary symbols using dlsym()
 * Created to cover different compilations of librdkafka (old and new abis)
 * Used for forcing external functions to fail for unit testing purposes
 * exit if none of the symbols can be loaded.
**/
void * loadsyms(const char * primary, const char * secondary) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"

  void * vpsym = dlsym(RTLD_NEXT, primary);

  if (vpsym == NULL) {
    printf("Could not load primary symbol: %s\n", primary);
    printf("Trying secondary...\n");

    dlerror();
    vpsym = dlsym(RTLD_NEXT, secondary);
    if (vpsym == NULL) {
      printf("Could not load secondary symbol: %s\n", secondary);
      printf("Error stubbing kafka functions\n");
      exit(1);
    }
  }
#pragma GCC diagnostic pop
  return vpsym;
}

/** Intercept RdKafka::Conf::create() */
RdKafka::Conf *RdKafka::Conf::create(ConfType type) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
  pcreate real_create = (pcreate)loadsyms(
      "_ZN7RdKafka4Conf6createENS0_8ConfTypeE",
      "_ZN7RdKafka4Conf6createENS0_8ConfTypeE"); // nm -C
#pragma GCC diagnostic pop

  if (fail != -1 && type == fail) {
    printf("Forcing RdKafka::Conf::create() to fail\n");
    return nullptr;
  } else {
    return real_create(type);
  }
}

/** Intercept RdKafka::Producer::create() */
RdKafka::Producer *RdKafka::Producer::create(RdKafka::Conf *type,
                                             std::string &str) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
  pcreateprod real_create = (pcreateprod)loadsyms(
      "_ZN7RdKafka8Producer6createEPNS_4ConfERSs",
      "_ZN7RdKafka8Producer6createEPNS_4ConfERNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"); // nm -C librdkafka.a
#pragma GCC diagnostic pop

  if (fail == 777) {
    printf("Forcing RdKafka::Producer::create() to fail\n");
    return nullptr;
  } else {
    return real_create(type, str);
  }
}

/** Intercept RdKafka::Topic::create() */
RdKafka::Topic *RdKafka::Topic::create(RdKafka::Handle *handle,
                                       std::string const &topic,
                                       RdKafka::Conf *conf, std::string &str) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
  pcreatetopic real_create = (pcreatetopic)loadsyms(
      "_ZN7RdKafka5Topic6createEPNS_6HandleERKSsPNS_4ConfERSs",
      "_ZN7RdKafka5Topic6createEPNS_6HandleERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPNS_4ConfERS8_");
#pragma GCC diagnostic pop

  if (fail == 888) {
    printf("Forcing RdKafka::Topic::create() to fail\n");
    return nullptr;
  } else {
    return real_create(handle, topic, conf, str);
  }
}

//
// Google test code below
//

class ProducerTest : public TestBase {
  virtual void SetUp() { fail = -1; }
  virtual void TearDown() {}
};

TEST_F(ProducerTest, ConstructorOK) {
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_NO_ERROR);
  ASSERT_EQ(prod.stats.dr_errors, 0);
  ASSERT_EQ(prod.stats.dr_noerrors, 0);
  ASSERT_EQ(prod.stats.ev_errors, 0);
  ASSERT_EQ(prod.stats.ev_others, 0);
  ASSERT_EQ(prod.stats.produce_fails, 0);
}

TEST_F(ProducerTest, CreateConfGlobalFail) {
  fail = RdKafka::Conf::CONF_GLOBAL;
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_UNKNOWN);
}

TEST_F(ProducerTest, CreateConfTopicFail) {
  fail = RdKafka::Conf::CONF_TOPIC;
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_UNKNOWN);
}

TEST_F(ProducerTest, CreateProducerFail) {
  fail = 777;
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_UNKNOWN);
}

TEST_F(ProducerTest, CreateTopicFail) {
  fail = 888;
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce(0, 0);
  ASSERT_EQ(ret, RdKafka::ERR_UNKNOWN);

}

TEST_F(ProducerTest, ProducerFail) {
  Producer prod{"nobroker", "notopic"};
  int ret = prod.produce((char *)10000, 100000000);
  ASSERT_EQ(ret, RdKafka::ERR_MSG_SIZE_TOO_LARGE);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

#pragma GCC diagnostic pop
<|endoftext|>


<commit_before>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015-2017. All Rights Reserved.                        */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "InstanceImpl.h"

using namespace nt;

std::atomic<int> InstanceImpl::s_default{-1};
std::atomic<InstanceImpl*> InstanceImpl::s_fast_instances[10];
wpi::UidVector<std::unique_ptr<InstanceImpl>, 10> InstanceImpl::s_instances;
std::mutex InstanceImpl::s_mutex;

using namespace std::placeholders;

InstanceImpl::InstanceImpl(int inst)
    : logger_impl(inst),
      logger(std::bind(&LoggerImpl::Log, &logger_impl, _1, _2, _3, _4)),
      connection_notifier(inst),
      entry_notifier(inst, logger),
      rpc_server(inst, logger),
      storage(entry_notifier, rpc_server, logger),
      dispatcher(storage, connection_notifier, logger),
      ds_client(dispatcher, logger) {}

InstanceImpl::~InstanceImpl() { logger.SetLogger(nullptr); }

InstanceImpl* InstanceImpl::GetDefault() { return Get(GetDefaultIndex()); }

InstanceImpl* InstanceImpl::Get(int inst) {
  if (inst < 0) return nullptr;

  // fast path, just an atomic read
  if (static_cast<unsigned int>(inst) <
      (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
    InstanceImpl* ptr = s_fast_instances[inst];
    if (ptr) return ptr;
  }

  // slow path
  std::lock_guard<std::mutex> lock(s_mutex);

  // static fast-path block
  if (static_cast<unsigned int>(inst) <
      (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
    // double-check
    return s_fast_instances[inst];
  }

  // vector
  if (static_cast<unsigned int>(inst) < s_instances.size()) {
    return s_instances[inst].get();
  }

  // doesn't exist
  return nullptr;
}

int InstanceImpl::GetDefaultIndex() {
  int inst = s_default;
  if (inst >= 0) return inst;

  // slow path
  std::lock_guard<std::mutex> lock(s_mutex);

  // double-check
  inst = s_default;
  if (inst >= 0) return inst;

  // alloc and save
  inst = AllocImpl();
  s_default = inst;
  return inst;
}

int InstanceImpl::Alloc() {
  std::lock_guard<std::mutex> lock(s_mutex);
  return AllocImpl();
}

int InstanceImpl::AllocImpl() {
  unsigned int inst = s_instances.emplace_back();
  InstanceImpl* ptr = new InstanceImpl(inst);
  s_instances[inst].reset(ptr);

  if (inst < (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
    s_fast_instances[inst] = ptr;
  }

  return static_cast<int>(inst);
}

void InstanceImpl::Destroy(int inst) {
  std::lock_guard<std::mutex> lock(s_mutex);
  if (inst < 0 || static_cast<unsigned int>(inst) >= s_instances.size()) return;

  if (static_cast<unsigned int>(inst) <
      (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
    s_fast_instances[inst] = nullptr;
  }

  s_instances.erase(inst);
}
<commit_msg>Initialize logger min level from logger_impl.<commit_after>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015-2017. All Rights Reserved.                        */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "InstanceImpl.h"

using namespace nt;

std::atomic<int> InstanceImpl::s_default{-1};
std::atomic<InstanceImpl*> InstanceImpl::s_fast_instances[10];
wpi::UidVector<std::unique_ptr<InstanceImpl>, 10> InstanceImpl::s_instances;
std::mutex InstanceImpl::s_mutex;

using namespace std::placeholders;

InstanceImpl::InstanceImpl(int inst)
    : logger_impl(inst),
      logger(std::bind(&LoggerImpl::Log, &logger_impl, _1, _2, _3, _4)),
      connection_notifier(inst),
      entry_notifier(inst, logger),
      rpc_server(inst, logger),
      storage(entry_notifier, rpc_server, logger),
      dispatcher(storage, connection_notifier, logger),
      ds_client(dispatcher, logger) {
  logger.set_min_level(logger_impl.GetMinLevel());
}

InstanceImpl::~InstanceImpl() { logger.SetLogger(nullptr); }

InstanceImpl* InstanceImpl::GetDefault() { return Get(GetDefaultIndex()); }

InstanceImpl* InstanceImpl::Get(int inst) {
  if (inst < 0) return nullptr;

  // fast path, just an atomic read
  if (static_cast<unsigned int>(inst) <
      (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
    InstanceImpl* ptr = s_fast_instances[inst];
    if (ptr) return ptr;
  }

  // slow path
  std::lock_guard<std::mutex> lock(s_mutex);

  // static fast-path block
  if (static_cast<unsigned int>(inst) <
      (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
    // double-check
    return s_fast_instances[inst];
  }

  // vector
  if (static_cast<unsigned int>(inst) < s_instances.size()) {
    return s_instances[inst].get();
  }

  // doesn't exist
  return nullptr;
}

int InstanceImpl::GetDefaultIndex() {
  int inst = s_default;
  if (inst >= 0) return inst;

  // slow path
  std::lock_guard<std::mutex> lock(s_mutex);

  // double-check
  inst = s_default;
  if (inst >= 0) return inst;

  // alloc and save
  inst = AllocImpl();
  s_default = inst;
  return inst;
}

int InstanceImpl::Alloc() {
  std::lock_guard<std::mutex> lock(s_mutex);
  return AllocImpl();
}

int InstanceImpl::AllocImpl() {
  unsigned int inst = s_instances.emplace_back();
  InstanceImpl* ptr = new InstanceImpl(inst);
  s_instances[inst].reset(ptr);

  if (inst < (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
    s_fast_instances[inst] = ptr;
  }

  return static_cast<int>(inst);
}

void InstanceImpl::Destroy(int inst) {
  std::lock_guard<std::mutex> lock(s_mutex);
  if (inst < 0 || static_cast<unsigned int>(inst) >= s_instances.size()) return;

  if (static_cast<unsigned int>(inst) <
      (sizeof(s_fast_instances) / sizeof(s_fast_instances[0]))) {
    s_fast_instances[inst] = nullptr;
  }

  s_instances.erase(inst);
}
<|endoftext|>