init
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
#include "CAlgorithmGlobalDefinesGen.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
|
||||
CAlgorithmGlobalDefinesGen::CAlgorithmGlobalDefinesGen(const Kernel::IKernelContext& ctx, const std::string& filename)
|
||||
: CPluginObjectDescEnum(ctx)
|
||||
{
|
||||
m_usedIDs.clear();
|
||||
|
||||
const std::string fullFilename = filename + "/ovp_global_defines.h";
|
||||
|
||||
getLogManager() << Kernel::LogLevel_Info << "Writing global defines to '" << fullFilename << "\n";
|
||||
|
||||
m_file.open(fullFilename.c_str());
|
||||
|
||||
OV_ERROR_UNLESS_K(m_file.good(), "Error opening '" << fullFilename << "for writing.", Kernel::ErrorType::BadFileRead,)
|
||||
; // Please note the ",)" to simulate "return;"
|
||||
|
||||
m_file << "#pragma once\n\n// This file was automatically generated by Plugin Inspector\n\n";
|
||||
}
|
||||
|
||||
CAlgorithmGlobalDefinesGen::~CAlgorithmGlobalDefinesGen()
|
||||
{
|
||||
if (!m_file.good()) { return; }
|
||||
|
||||
m_file << "\n";
|
||||
m_file.close();
|
||||
}
|
||||
|
||||
void CAlgorithmGlobalDefinesGen::addIdentifier(const std::string& objectName, const CIdentifier& candidate, const std::string& spaces)
|
||||
{
|
||||
if (m_usedIDs.find(candidate) == m_usedIDs.end())
|
||||
{
|
||||
m_usedIDs[candidate] = objectName;
|
||||
m_file << "#define " << objectName << spaces << "CIdentifier" << candidate.str() << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
OV_WARNING_K(" Object '" << objectName << "' has duplicate id " << candidate.str() << " with " << m_usedIDs[candidate]) ;
|
||||
m_file << "#define " << objectName << spaces << "CIdentifier" << candidate.str() << " // Duplicate of " << m_usedIDs[candidate] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
bool CAlgorithmGlobalDefinesGen::callback(const Plugins::IPluginObjectDesc& pluginObjectDesc)
|
||||
{
|
||||
OV_ERROR_UNLESS_KRF(m_file.good(), "Cannot use file; Was it opened correctly ?", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
int width = 120;
|
||||
|
||||
getLogManager() << Kernel::LogLevel_Info << " Dumping [" << pluginObjectDesc.getName() << "]\n";
|
||||
|
||||
Kernel::IAlgorithmManager& manager = m_kernelCtx.getAlgorithmManager();
|
||||
const CIdentifier algorithmId = manager.createAlgorithm(pluginObjectDesc.getCreatedClass());
|
||||
OV_ERROR_UNLESS_KRF(algorithmId != CIdentifier::undefined(), " Unable to create algorithm", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
Kernel::IAlgorithmProxy& proxy = manager.getAlgorithm(algorithmId);
|
||||
|
||||
m_file << "// -----------------------------------------------------\n";
|
||||
m_file << "// " << pluginObjectDesc.getName().toASCIIString() << "\n";
|
||||
m_file << "// -----------------------------------------------------\n";
|
||||
m_file << "\n";
|
||||
|
||||
proxy.initialize();
|
||||
|
||||
const auto addIdentifierFromParameters = [&width, &pluginObjectDesc, this](const char* nameStart, const char* nameEnd, const CIdentifier& id,
|
||||
const std::string& toAppend)
|
||||
{
|
||||
std::string defName;
|
||||
defName += nameStart;
|
||||
defName += transform(pluginObjectDesc.getName().toASCIIString(), true);
|
||||
defName += nameEnd;
|
||||
if (!toAppend.empty()) { defName += transform(toAppend, true); }
|
||||
|
||||
std::string spaces;
|
||||
spaces.resize(width - defName.length(), ' ');
|
||||
|
||||
addIdentifier(defName.c_str(), id, spaces.c_str());
|
||||
};
|
||||
|
||||
addIdentifierFromParameters("OVP_GD_ClassId_Algorithm_", "", pluginObjectDesc.getCreatedClassIdentifier(), "");
|
||||
addIdentifierFromParameters("OVP_GD_ClassId_Algorithm_", "Desc", pluginObjectDesc.getCreatedClassIdentifier(), "");
|
||||
|
||||
CIdentifier id = CIdentifier::undefined();
|
||||
while ((id = proxy.getNextInputParameterIdentifier(id)) != CIdentifier::undefined())
|
||||
{
|
||||
addIdentifierFromParameters("OVP_GD_Algorithm_", "_InputParameterId_", id, proxy.getInputParameterName(id).toASCIIString());
|
||||
}
|
||||
|
||||
id = CIdentifier::undefined();
|
||||
while ((id = proxy.getNextOutputParameterIdentifier(id)) != CIdentifier::undefined())
|
||||
{
|
||||
addIdentifierFromParameters("OVP_GD_Algorithm_", "_OutputParameterId_", id, proxy.getOutputParameterName(id).toASCIIString());
|
||||
}
|
||||
|
||||
id = CIdentifier::undefined();
|
||||
while ((id = proxy.getNextInputTriggerIdentifier(id)) != CIdentifier::undefined())
|
||||
{
|
||||
addIdentifierFromParameters("OVP_GD_Algorithm_", "_InputTriggerId_", id, proxy.getInputTriggerName(id).toASCIIString());
|
||||
}
|
||||
|
||||
id = CIdentifier::undefined();
|
||||
while ((id = proxy.getNextOutputTriggerIdentifier(id)) != CIdentifier::undefined())
|
||||
{
|
||||
addIdentifierFromParameters("OVP_GD_Algorithm_", "_OutputTriggerId_", id, proxy.getOutputTriggerName(id).toASCIIString());
|
||||
}
|
||||
|
||||
proxy.uninitialize();
|
||||
|
||||
m_file << "\n";
|
||||
|
||||
manager.releaseAlgorithm(proxy);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "CPluginObjectDescEnum.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
class CAlgorithmGlobalDefinesGen final : public CPluginObjectDescEnum
|
||||
{
|
||||
public:
|
||||
|
||||
CAlgorithmGlobalDefinesGen(const Kernel::IKernelContext& ctx, const std::string& filename);
|
||||
~CAlgorithmGlobalDefinesGen() override;
|
||||
bool callback(const Plugins::IPluginObjectDesc& pluginObjectDesc) override;
|
||||
|
||||
protected:
|
||||
|
||||
std::ofstream m_file;
|
||||
|
||||
std::map<CIdentifier, std::string> m_usedIDs;
|
||||
// Adds the define to m_usedIdentifiers and m_File
|
||||
void addIdentifier(const std::string& objectName, const CIdentifier& candidate, const std::string& spaces);
|
||||
};
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
#include "CAlgorithmSnapshotGen.hpp"
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include <pango/pango.h>
|
||||
#include <array>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
CAlgorithmSnapshotGen::CAlgorithmSnapshotGen(const Kernel::IKernelContext& ctx, const std::string& snapshotDir, const std::string& docTemplateDir)
|
||||
: CPluginObjectDescEnum(ctx), m_snapshotDir(snapshotDir), m_docTemplateDir(docTemplateDir)
|
||||
{
|
||||
#define GDK_COLOR_SET(c, r, g, b) { c.pixel=0; c.red=r; c.green=g; c.blue=b; }
|
||||
GDK_COLOR_SET(m_colors[EColors::BackgroundPlayerStarted], 32767, 32767, 32767);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundSelected], 65535, 65535, 49151);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundMissing], 49151, 32767, 32767);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundObsolete], 32767, 49151, 49151);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBackground], 65535, 65535, 65535);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBorderSelected], 0, 0, 0);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBorder], 0, 0, 0);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxInputBackground], 65535, 49151, 32767);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxInputBorder], 16383, 16383, 16383);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxOutputBackground], 32767, 65535, 49151);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxOutputBorder], 16383, 16383, 16383);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxSettingBackground], 49151, 32767, 65535);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxSettingBorder], 16383, 16383, 16383);
|
||||
GDK_COLOR_SET(m_colors[EColors::Link], 0, 0, 0);
|
||||
GDK_COLOR_SET(m_colors[EColors::LinkSelected], 49151, 16383, 16383);
|
||||
GDK_COLOR_SET(m_colors[EColors::SelectionArea], 0x3f00, 0x3f00, 0x3f00);
|
||||
GDK_COLOR_SET(m_colors[EColors::SelectionAreaBorder], 0, 0, 0);
|
||||
#undef GDK_COLOR_SET
|
||||
|
||||
m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
m_widget = gtk_drawing_area_new();
|
||||
gtk_container_add(GTK_CONTAINER(m_window), m_widget);
|
||||
gtk_widget_set_size_request(m_widget, 512, 128);
|
||||
gtk_widget_show_all(m_window);
|
||||
gdk_flush();
|
||||
System::Time::sleep(1000);
|
||||
}
|
||||
|
||||
CAlgorithmSnapshotGen::~CAlgorithmSnapshotGen()
|
||||
{
|
||||
std::ofstream file;
|
||||
file.open((m_docTemplateDir + "/Doc_Algorithms.dox").c_str());
|
||||
file << "/**\n * \\page Doc_Algorithms Algorithms list\n\n * Available algorithms :\n";
|
||||
|
||||
size_t level = 0;
|
||||
std::string lastCat;
|
||||
std::vector<std::string> lastSplittedCat;
|
||||
std::sort(m_categories.begin(), m_categories.end());
|
||||
for (const auto& category : m_categories)
|
||||
{
|
||||
std::string cat = category.first;
|
||||
std::string name = category.second;
|
||||
|
||||
if (lastCat != cat)
|
||||
{
|
||||
std::vector<std::string> splittedCat;
|
||||
size_t i = size_t(-1);
|
||||
bool finished = false;
|
||||
while (!finished)
|
||||
{
|
||||
size_t j = cat.find('/', i + 1);
|
||||
if (j == std::string::npos)
|
||||
{
|
||||
j = cat.length();
|
||||
finished = true;
|
||||
}
|
||||
if (j != i + 1)
|
||||
{
|
||||
splittedCat.push_back(cat.substr(i + 1, j - i - 1));
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
level = splittedCat.size();
|
||||
|
||||
auto it1 = lastSplittedCat.size() < splittedCat.size()
|
||||
? std::mismatch(lastSplittedCat.begin(), lastSplittedCat.end(), splittedCat.begin()).second
|
||||
: std::mismatch(splittedCat.begin(), splittedCat.end(), lastSplittedCat.begin()).first;
|
||||
|
||||
for (; it1 != splittedCat.end(); ++it1) { file << " * " << std::string((it1 - splittedCat.begin()) * 3, ' ') << " - " << *it1 << " : \n"; }
|
||||
|
||||
lastCat = cat;
|
||||
lastSplittedCat = splittedCat;
|
||||
}
|
||||
|
||||
file << " * " << std::string(3 * (level + 1), ' ') << " - \\subpage Doc_algorithm_" << transform(cat + "/" + name) << " \"" << name << "\"\n";
|
||||
}
|
||||
|
||||
file << " */\n";
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool CAlgorithmSnapshotGen::callback(const Plugins::IPluginObjectDesc& pod)
|
||||
{
|
||||
const std::string fullName = std::string(pod.getCategory().toASCIIString()) + "/" + pod.getName().toASCIIString();
|
||||
const std::string filename = m_snapshotDir + "/Algorithm_" + transform(fullName);
|
||||
CIdentifier id;
|
||||
if ((id = m_kernelCtx.getAlgorithmManager().createAlgorithm(pod.getCreatedClassIdentifier())) == CIdentifier::undefined())
|
||||
{
|
||||
m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning << "Skipped [" << filename << "] (could not create corresponding algorithm)\n";
|
||||
return true;
|
||||
}
|
||||
Kernel::IAlgorithmProxy& algorithm = m_kernelCtx.getAlgorithmManager().getAlgorithm(id);
|
||||
|
||||
m_kernelCtx.getLogManager() << Kernel::LogLevel_Trace << "Working on [" << filename << "]\n";
|
||||
|
||||
PangoContext* ctx = gtk_widget_get_pango_context(m_widget);
|
||||
PangoLayout* layout = pango_layout_new(ctx);
|
||||
PangoRectangle rectangle;
|
||||
|
||||
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
||||
pango_layout_set_text(layout, pod.getName(), -1);
|
||||
pango_layout_get_pixel_extents(layout, nullptr, &rectangle);
|
||||
|
||||
const int marginX = 5;
|
||||
const int marginY = 5;
|
||||
const int circleMargin = 5;
|
||||
const int circleSize = 11;
|
||||
const int circleSpace = 4;
|
||||
|
||||
const int w = rectangle.width + marginX * 2;
|
||||
const int h = rectangle.height + marginY * 2;
|
||||
const int x = 16;
|
||||
const int y = 16;
|
||||
|
||||
gdk_window_invalidate_rect(m_widget->window, nullptr, true);
|
||||
|
||||
while (gtk_events_pending()) { gtk_main_iteration(); }
|
||||
|
||||
GdkGC* drawGC = gdk_gc_new(m_widget->window);
|
||||
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBackground]);
|
||||
gdk_draw_rectangle(m_widget->window, drawGC, TRUE, x, y, w, h);
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBorder]);
|
||||
gdk_draw_rectangle(m_widget->window, drawGC, FALSE, x, y, w, h);
|
||||
|
||||
size_t i = 0;
|
||||
while ((id = algorithm.getNextInputParameterIdentifier(id)) != CIdentifier::undefined())
|
||||
{
|
||||
GdkColor color = m_colors[EColors(algorithm.getInputParameter(id)->getType())];
|
||||
|
||||
std::array<GdkPoint, 4> points;
|
||||
points[0].x = circleSize >> 1;
|
||||
points[0].y = circleSize;
|
||||
points[1].x = 0;
|
||||
points[1].y = 0;
|
||||
points[2].x = circleSize - 1;
|
||||
points[2].y = 0;
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
points[j].x += gint(x + circleMargin + i * (circleSpace + circleSize));
|
||||
points[j].y += gint(y - (circleSize >> 1));
|
||||
}
|
||||
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &color);
|
||||
gdk_draw_polygon(m_widget->window, drawGC, TRUE, points.data(), 3);
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxInputBorder]);
|
||||
gdk_draw_polygon(m_widget->window, drawGC, FALSE, points.data(), 3);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while ((id = algorithm.getNextOutputParameterIdentifier(id)) != CIdentifier::undefined())
|
||||
{
|
||||
GdkColor color = m_colors[EColors(algorithm.getOutputParameter(id)->getType())];
|
||||
|
||||
std::array<GdkPoint, 4> points;
|
||||
points[0].x = circleSize >> 1;
|
||||
points[0].y = circleSize;
|
||||
points[1].x = 0;
|
||||
points[1].y = 0;
|
||||
points[2].x = circleSize - 1;
|
||||
points[2].y = 0;
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
points[j].x += gint(x + circleMargin + i * (circleSpace + circleSize));
|
||||
points[j].y += gint(y - (circleSize >> 1) + h);
|
||||
}
|
||||
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &color);
|
||||
gdk_draw_polygon(m_widget->window, drawGC, TRUE, points.data(), 3);
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxOutputBorder]);
|
||||
gdk_draw_polygon(m_widget->window, drawGC, FALSE, points.data(), 3);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
gdk_draw_layout(m_widget->window, m_widget->style->text_gc[GTK_WIDGET_STATE(m_widget)], x + marginX, y + marginY, layout);
|
||||
g_object_unref(layout);
|
||||
g_object_unref(drawGC);
|
||||
|
||||
GdkPixbuf* buf = gdk_pixbuf_get_from_drawable(nullptr, m_widget->window, nullptr, 0, 0, 0, 0, w + 32, h + 32);
|
||||
OV_WARNING_UNLESS_K(gdk_pixbuf_save(buf, (filename+".png").c_str(), "png", nullptr, nullptr), "Failed saving " << (filename+".png"));
|
||||
|
||||
g_object_unref(buf);
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#if 0
|
||||
|
||||
m_categories.push_back(pair < string, string >(pod.getCategory().toASCIIString(), pod.getName().toASCIIString()));
|
||||
|
||||
std::ofstream file;
|
||||
file.open((filename+".dox").toASCIIString());
|
||||
|
||||
file << "/**\n"
|
||||
<< " * \\page Doc_" << filename << " " << fullName.toASCIIString() << "\n"
|
||||
<< " * \\section Doc_" << filename << "_summary Summary\n"
|
||||
<< " * \\image html " << filename << ".png\n *\n"
|
||||
<< " * - Plugin name : " << pod.getName() << "\n"
|
||||
<< " * - Version : " << pod.getVersion() << "\n"
|
||||
<< " * - Author : " << pod.getAuthorName() << "\n"
|
||||
<< " * - Company : " << pod.getAuthorCompanyName() << "\n"
|
||||
<< " * - Short description : " << pod.getShortDescription() << "\n"
|
||||
<< " * - Documentation template generation date : " << __DATE__ << "\n *\n"
|
||||
<< " * \\section Doc_" << filename << "_description Description\n"
|
||||
<< " * " << pod.getDetailedDescription() << " TODO\n *\n";
|
||||
|
||||
file << " * \\section Doc_" << filename << "_inputs Inputs\n"
|
||||
<< " * Number of inputs : " << box.getInputCount() << "\n *\n";
|
||||
|
||||
for (i = 0; i < box.getInputCount(); ++i)
|
||||
{
|
||||
CString name;
|
||||
CIdentifier typeID;
|
||||
box.getInputName(i, name);
|
||||
box.getInputType(i, typeID);
|
||||
file << " * - Input " << i+1 << "\n"
|
||||
<< " * - Name : " << name.toASCIIString() << "\n"
|
||||
<< " * - Type identifier : " << m_kernelCtx.getTypeManager().getTypeName(typeID).toASCIIString() << " " << typeID.str() << "\n"
|
||||
<< " * - Description : TODO\n *\n";
|
||||
}
|
||||
|
||||
file << " * \\section Doc_" << filename << "_outputs Outputs\n"
|
||||
<< " * Number of outputs : " << box.getOutputCount() << "\n *\n";
|
||||
|
||||
for (i = 0; i < box.getOutputCount(); ++i)
|
||||
{
|
||||
CString name;
|
||||
CIdentifier typeID;
|
||||
box.getOutputName(i, name);
|
||||
box.getOutputType(i, typeID);
|
||||
file
|
||||
<< " * - Output " << i+1 << "\n"
|
||||
<< " * - Name : " << name.toASCIIString() << "\n"
|
||||
<< " * - Type identifier : " << m_kernelCtx.getTypeManager().getTypeName(typeID).toASCIIString() << " " << typeID.str() << "\n"
|
||||
<< " * - Description : TODO\n"
|
||||
<< " *\n";
|
||||
}
|
||||
|
||||
file << " * \\section Doc_" << filename << "_settings Settings\n"
|
||||
<< " * Number of settings : " << box.getSettingCount() << "\n *\n";
|
||||
|
||||
for (i = 0; i < box.getSettingCount(); ++i)
|
||||
{
|
||||
CString name;
|
||||
CIdentifier typeID;
|
||||
CString defaultValue;
|
||||
box.getSettingName(i, name);
|
||||
box.getSettingType(i, typeID);
|
||||
box.getSettingDefaultValue(i, defaultValue);
|
||||
file
|
||||
<< " * - Setting " << i+1 << "\n"
|
||||
<< " * - Name : " << name.toASCIIString() << "\n"
|
||||
<< " * - Type identifier : " << m_kernelCtx.getTypeManager().getTypeName(typeID).toASCIIString() << " " << typeID.str() << "\n"
|
||||
<< " * - Default value : " << defaultValue.toASCIIString() << "\n"
|
||||
<< " * - Description : TODO\n *\n";
|
||||
}
|
||||
|
||||
file << " * \\section Doc_" << filename << "_examples Examples\n * TODO\n *\n"
|
||||
<< " * \\section Doc_" << filename << "_misc Miscellaneous\n * TODO\n */";
|
||||
|
||||
file.close();
|
||||
|
||||
#endif
|
||||
|
||||
m_kernelCtx.getAlgorithmManager().releaseAlgorithm(algorithm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "CPluginObjectDescEnum.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
class CAlgorithmSnapshotGen final : public CPluginObjectDescEnum
|
||||
{
|
||||
public:
|
||||
|
||||
CAlgorithmSnapshotGen(const Kernel::IKernelContext& ctx, const std::string& snapshotDir, const std::string& docTemplateDir);
|
||||
~CAlgorithmSnapshotGen() override;
|
||||
bool callback(const Plugins::IPluginObjectDesc& pod) override;
|
||||
|
||||
protected:
|
||||
|
||||
std::string m_snapshotDir;
|
||||
std::string m_docTemplateDir;
|
||||
std::vector<std::pair<std::string, std::string>> m_categories;
|
||||
std::map<EColors, GdkColor> m_colors;
|
||||
GtkWidget* m_window = nullptr;
|
||||
GtkWidget* m_widget = nullptr;
|
||||
};
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#include "CBoxAlgorithmDumper.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
CBoxAlgorithmDumper::CBoxAlgorithmDumper(const Kernel::IKernelContext& ctx, const std::string& outFile)
|
||||
: CPluginObjectDescEnum(ctx)
|
||||
{
|
||||
if (outFile.empty()) { m_writeToFile = false; }
|
||||
if (m_writeToFile) { m_file.open(outFile.c_str()); }
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
bool CBoxAlgorithmDumper::callback(const Plugins::IPluginObjectDesc& pluginObjectDesc)
|
||||
{
|
||||
const CIdentifier boxID = pluginObjectDesc.getCreatedClassIdentifier();
|
||||
(m_writeToFile ? m_file : std::cout) << "BoxAlgorithm " << transform(pluginObjectDesc.getName().toASCIIString()) << " " << boxID.str() << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Dumps all registered box algorithms to a text file or cout. The intended use case
|
||||
// is to check the current scenarios to see if all boxes have at least one
|
||||
// scenario where it is used. n.b. The checking was done by a shell script in
|
||||
//
|
||||
// test/check-scenario-coverage.sh
|
||||
//
|
||||
|
||||
#include "CPluginObjectDescEnum.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
class CBoxAlgorithmDumper final : public CPluginObjectDescEnum
|
||||
{
|
||||
public:
|
||||
|
||||
CBoxAlgorithmDumper(const Kernel::IKernelContext& ctx, const std::string& outFile);
|
||||
~CBoxAlgorithmDumper() override { if (m_writeToFile) { m_file.close(); } }
|
||||
bool callback(const Plugins::IPluginObjectDesc& pluginObjectDesc) override;
|
||||
|
||||
protected:
|
||||
std::ofstream m_file;
|
||||
bool m_writeToFile = true; // if false, to console
|
||||
};
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
#include "CBoxAlgorithmSnapshotGen.hpp"
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include <pango/pango.h>
|
||||
#include <array>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
|
||||
CBoxAlgorithmSnapshotGen::CBoxAlgorithmSnapshotGen(const Kernel::IKernelContext& ctx, const std::string& snapshotDir, const std::string& docTemplateDir)
|
||||
: CPluginObjectDescEnum(ctx), m_snapshotDir(snapshotDir), m_docTemplateDir(docTemplateDir)
|
||||
{
|
||||
m_kernelCtx.getScenarioManager().createScenario(m_scenarioID);
|
||||
m_scenario = &m_kernelCtx.getScenarioManager().getScenario(m_scenarioID);
|
||||
|
||||
#define GDK_COLOR_SET(c, r, g, b) { c.pixel=0; c.red=r; c.green=g; c.blue=b; }
|
||||
GDK_COLOR_SET(m_colors[EColors::BackgroundPlayerStarted], 32767, 32767, 32767);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundSelected], 65535, 65535, 49151);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundMissing], 49151, 32767, 32767);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundDeprecated], 16383, 24575, 24575);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBackground], 65535, 65535, 65535);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBorderSelected], 0, 0, 0);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxBorder], 0, 0, 0);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxInputBackground], 65535, 49151, 32767);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxInputBorder], 16383, 16383, 16383);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxOutputBackground], 32767, 65535, 49151);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxOutputBorder], 16383, 16383, 16383);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxSettingBackground], 49151, 32767, 65535);
|
||||
GDK_COLOR_SET(m_colors[EColors::BoxSettingBorder], 16383, 16383, 16383);
|
||||
GDK_COLOR_SET(m_colors[EColors::Link], 0, 0, 0);
|
||||
GDK_COLOR_SET(m_colors[EColors::LinkSelected], 49151, 16383, 16383);
|
||||
GDK_COLOR_SET(m_colors[EColors::SelectionArea], 0x3f00, 0x3f00, 0x3f00);
|
||||
GDK_COLOR_SET(m_colors[EColors::SelectionAreaBorder], 0, 0, 0);
|
||||
#undef GDK_COLOR_SET
|
||||
|
||||
m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
m_widget = gtk_drawing_area_new();
|
||||
gtk_container_add(GTK_CONTAINER(m_window), m_widget);
|
||||
gtk_widget_set_size_request(m_widget, 512, 128);
|
||||
gtk_widget_show_all(m_window);
|
||||
gdk_flush();
|
||||
System::Time::sleep(1000);
|
||||
}
|
||||
|
||||
CBoxAlgorithmSnapshotGen::~CBoxAlgorithmSnapshotGen()
|
||||
{
|
||||
m_kernelCtx.getScenarioManager().releaseScenario(m_scenarioID);
|
||||
|
||||
std::ofstream file;
|
||||
file.open((m_docTemplateDir + "/Doc_BoxAlgorithms.dox").c_str());
|
||||
file << "/**\n * \\page Doc_BoxAlgorithms Box algorithms list\n *\n * Available box algorithms :\n";
|
||||
|
||||
size_t level = 0;
|
||||
std::string lastCat;
|
||||
std::vector<std::string> lastSplittedCat;
|
||||
std::sort(m_categories.begin(), m_categories.end());
|
||||
for (const auto& category : m_categories)
|
||||
{
|
||||
std::string cat = category.first;
|
||||
std::string name = category.second;
|
||||
|
||||
if (lastCat != cat)
|
||||
{
|
||||
std::vector<std::string> splittedCat;
|
||||
size_t i = size_t(-1);
|
||||
bool finished = false;
|
||||
while (!finished)
|
||||
{
|
||||
size_t j = cat.find('/', i + 1);
|
||||
if (j == std::string::npos)
|
||||
{
|
||||
j = cat.length();
|
||||
finished = true;
|
||||
}
|
||||
if (j != i + 1)
|
||||
{
|
||||
splittedCat.push_back(cat.substr(i + 1, j - i - 1));
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
level = splittedCat.size();
|
||||
// @FIXME C++14 : auto it1 = std::mismatch(lastSplittedCat.begin(), lastSplittedCat.end(), splittedCat.begin(), splittedCat.end()).second;
|
||||
auto it1 = lastSplittedCat.size() < splittedCat.size()
|
||||
? std::mismatch(lastSplittedCat.begin(), lastSplittedCat.end(), splittedCat.begin()).second
|
||||
: std::mismatch(splittedCat.begin(), splittedCat.end(), lastSplittedCat.begin()).first;
|
||||
|
||||
for (; it1 != splittedCat.end(); ++it1) { file << " * " << std::string((it1 - splittedCat.begin()) * 3, ' ') << " - " << *it1 << " : \n"; }
|
||||
|
||||
lastCat = cat;
|
||||
lastSplittedCat = splittedCat;
|
||||
}
|
||||
|
||||
file << " * " << std::string(3 * level, ' ') << " - \\subpage Doc_BoxAlgorithm_" << transform(name) << " \"" << name << "\"\n";
|
||||
}
|
||||
|
||||
file << " */\n";
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmSnapshotGen::callback(const Plugins::IPluginObjectDesc& pod)
|
||||
{
|
||||
//string fullName = string(rPluginObjectDesc.getCategory().toASCIIString()) + "/" + string(rPluginObjectDesc.getName().toASCIIString());
|
||||
std::string filename = "BoxAlgorithm_" + transform(pod.getName().toASCIIString());
|
||||
CIdentifier boxID;
|
||||
if (!m_scenario->addBox(boxID, pod.getCreatedClassIdentifier(), CIdentifier::undefined()))
|
||||
{
|
||||
OV_WARNING_K("Skipped [" << filename << "] (could not create corresponding box)");
|
||||
return true;
|
||||
}
|
||||
Kernel::IBox& box = *m_scenario->getBoxDetails(boxID);
|
||||
getLogManager() << Kernel::LogLevel_Trace << "Working on [" << filename << "]\n";
|
||||
|
||||
PangoContext* ctx = gtk_widget_get_pango_context(m_widget);
|
||||
PangoLayout* layout = pango_layout_new(ctx);
|
||||
PangoRectangle rectangle;
|
||||
|
||||
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
||||
pango_layout_set_text(layout, pod.getName(), -1);
|
||||
pango_layout_get_pixel_extents(layout, nullptr, &rectangle);
|
||||
|
||||
const int marginX = 5;
|
||||
const int marginY = 5;
|
||||
const int circleSize = 11;
|
||||
const int circleSpace = 4;
|
||||
|
||||
int w = rectangle.width + marginX * 2;
|
||||
int h = rectangle.height + marginY * 2;
|
||||
int x = 16;
|
||||
int y = 16;
|
||||
|
||||
gdk_window_invalidate_rect(m_widget->window, nullptr, true);
|
||||
|
||||
while (gtk_events_pending()) { gtk_main_iteration(); }
|
||||
|
||||
GdkGC* drawGC = gdk_gc_new(m_widget->window);
|
||||
|
||||
bool deprecated = m_kernelCtx.getPluginManager().isPluginObjectFlaggedAsDeprecated(box.getAlgorithmClassIdentifier());
|
||||
if (deprecated) { gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBackgroundDeprecated]); }
|
||||
else { gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBackground]); }
|
||||
|
||||
gdkDrawRoundedRectangle(m_widget->window, drawGC, TRUE, x, y, w, h);
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBorder]);
|
||||
gdkDrawRoundedRectangle(m_widget->window, drawGC, FALSE, x, y, w, h);
|
||||
|
||||
int offset = int(w - box.getInputCount() * (circleSpace + circleSize) + circleSize / 2) / 2;
|
||||
for (size_t i = 0; i < box.getInputCount(); ++i)
|
||||
{
|
||||
CIdentifier id;
|
||||
box.getInputType(i, id);
|
||||
GdkColor color = colorFromIdentifier(id);
|
||||
|
||||
std::array<GdkPoint, 4> points;
|
||||
points[0].x = circleSize >> 1;
|
||||
points[0].y = circleSize;
|
||||
points[1].x = 0;
|
||||
points[1].y = 0;
|
||||
points[2].x = circleSize - 1;
|
||||
points[2].y = 0;
|
||||
for (size_t j = 0; j < 3; ++j)
|
||||
{
|
||||
points[j].x += gint(x + i * (circleSpace + circleSize) + offset);
|
||||
points[j].y += gint(y - (circleSize >> 1));
|
||||
}
|
||||
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &color);
|
||||
gdk_draw_polygon(m_widget->window, drawGC, TRUE, points.data(), 3);
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxInputBorder]);
|
||||
gdk_draw_polygon(m_widget->window, drawGC, FALSE, points.data(), 3);
|
||||
}
|
||||
|
||||
offset = int(w - box.getOutputCount() * (circleSpace + circleSize) + circleSize / 2) / 2;
|
||||
for (size_t i = 0; i < box.getOutputCount(); ++i)
|
||||
{
|
||||
CIdentifier id;
|
||||
box.getOutputType(i, id);
|
||||
GdkColor color = colorFromIdentifier(id);
|
||||
|
||||
std::array<GdkPoint, 4> points;
|
||||
points[0].x = circleSize >> 1;
|
||||
points[0].y = circleSize;
|
||||
points[1].x = 0;
|
||||
points[1].y = 0;
|
||||
points[2].x = circleSize - 1;
|
||||
points[2].y = 0;
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
points[j].x += gint(x + i * (circleSpace + circleSize) + offset);
|
||||
points[j].y += gint(y - (circleSize >> 1) + h);
|
||||
}
|
||||
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &color);
|
||||
gdk_draw_polygon(m_widget->window, drawGC, TRUE, points.data(), 3);
|
||||
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxOutputBorder]);
|
||||
gdk_draw_polygon(m_widget->window, drawGC, FALSE, points.data(), 3);
|
||||
}
|
||||
|
||||
gdk_draw_layout(m_widget->window, m_widget->style->text_gc[GTK_WIDGET_STATE(m_widget)], x + marginX, y + marginY, layout);
|
||||
g_object_unref(layout);
|
||||
g_object_unref(drawGC);
|
||||
|
||||
GdkPixbuf* buf = gdk_pixbuf_get_from_drawable(nullptr, m_widget->window, nullptr, 0, 0, 0, 0, w + 32, h + 32);
|
||||
gdk_pixbuf_save(buf, (m_snapshotDir + "/Doc_" + filename + ".png").c_str(), "png", nullptr, nullptr);
|
||||
|
||||
g_object_unref(buf);
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
m_categories.push_back(std::pair<std::string, std::string>(pod.getCategory().toASCIIString(), pod.getName().toASCIIString()));
|
||||
|
||||
std::ofstream fileSkeleton;
|
||||
std::ofstream filePart;
|
||||
fileSkeleton.open((m_docTemplateDir + "/Doc_" + filename + ".dox-skeleton").c_str());
|
||||
filePart.open((m_docTemplateDir + "/Doc_" + filename + ".dox-part-skeleton").c_str());
|
||||
|
||||
fileSkeleton << "/**\n"
|
||||
<< " * \\page Doc_" << filename << " " << pod.getName() << "\n"
|
||||
<< " * \\section Doc_" << filename << "_Summary Summary\n"
|
||||
<< " * \\image html Doc_" << filename << ".png\n"
|
||||
<< " *\n"
|
||||
<< " * - Plugin name : " << pod.getName() << "\n"
|
||||
<< " * - Version : " << pod.getVersion() << "\n"
|
||||
<< " * - Author : " << pod.getAuthorName() << "\n"
|
||||
<< " * - Company : " << pod.getAuthorCompanyName() << "\n"
|
||||
<< " * - Short description : " << pod.getShortDescription() << "\n"
|
||||
<< " * - Documentation template generation date : " << __DATE__ << "\n";
|
||||
|
||||
bool deprectated = m_kernelCtx.getPluginManager().isPluginObjectFlaggedAsDeprecated(pod.getCreatedClassIdentifier());
|
||||
if (deprectated)
|
||||
{
|
||||
fileSkeleton << " * - <b>WARNING : this box has been marked as DEPRECATED by the developer.</b>\n"
|
||||
<< " * It will be removed soon or later, so you should consider not using this\n"
|
||||
<< " * box and turn to another \"equivalent\" one.\n";
|
||||
}
|
||||
|
||||
// @FIXME CERT
|
||||
#pragma message("WARNING: 'Unstable' block commented out due to Certivibe")
|
||||
|
||||
/*
|
||||
bool isUnstable=m_kernelCtx.getPluginManager().isPluginObjectFlaggedAsUnstable(rPluginObjectDesc.getCreatedClassIdentifier());
|
||||
if(isUnstable)
|
||||
{
|
||||
fileSkeleton
|
||||
<< " * - <b>WARNING : this box has been marked as UNSTABLE by the developer.\n"
|
||||
<< " * It means that its implementation may be incomplete or that the box can only work\n"
|
||||
<< " * under well known conditions. It may possibly crash or cause data loss.\n"
|
||||
<< " * Use this box at your own risk, you've been warned.</b>\n";
|
||||
}
|
||||
*/
|
||||
|
||||
fileSkeleton << " *\n"
|
||||
<< " * \\section Doc_" << filename << "_Description Description\n"
|
||||
<< " * " << pod.getDetailedDescription() << "\n *\n"
|
||||
<< " * @Doc_" << filename << "_Description_Content@\n *\n";
|
||||
|
||||
filePart << "/**\n"
|
||||
<< " * \\page " << filename << " " << pod.getName() << "\n"
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< "Detailed description\n"
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< " * |OVP_DocBegin_" << filename << "_Description|\n"
|
||||
<< " * |OVP_DocEnd_" << filename << "_Description|\n";
|
||||
|
||||
if (box.getInputCount())
|
||||
{
|
||||
fileSkeleton << " * \\section Doc_" << filename << "_Inputs Inputs\n"
|
||||
// << " * Number of inputs : " << box.getInputCount() << "\n"
|
||||
<< " *\n"
|
||||
<< " * @Doc_" << filename << "_Inputs_Content@\n";
|
||||
|
||||
filePart << "__________________________________________________________________\n\n"
|
||||
<< "Inputs description\n"
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< " * |OVP_DocBegin_" << filename << "_Inputs|\n"
|
||||
<< " * |OVP_DocEnd_" << filename << "_Inputs|\n";
|
||||
|
||||
for (size_t i = 0; i < box.getInputCount(); ++i)
|
||||
{
|
||||
CString name;
|
||||
CIdentifier typeID;
|
||||
CString typeName;
|
||||
box.getInputName(i, name);
|
||||
box.getInputType(i, typeID);
|
||||
typeName = m_kernelCtx.getTypeManager().getTypeName(typeID);
|
||||
|
||||
fileSkeleton << " * \\subsection Doc_" << filename << "_Input_" << i + 1 << " " << i + 1 << ". " << name
|
||||
<< "\n * @Doc_" << filename << "_Input" << i + 1 << "_Content@\n *\n * - Type identifier : \\ref Doc_Streams_"
|
||||
<< transform(typeName.toASCIIString()) << " \"" << typeName << "\" <em>" << typeID.str() << "</em>\n *\n";
|
||||
|
||||
filePart << "\n * |OVP_DocBegin_" << filename << "_Input" << i + 1 << "|\n * |OVP_DocEnd_" << filename << "_Input" << i + 1 <<
|
||||
"|\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (box.getOutputCount())
|
||||
{
|
||||
fileSkeleton << " * \\section Doc_" << filename << "_Outputs Outputs\n"
|
||||
// << " * Number of outputs : " << box.getOutputCount() << "\n"
|
||||
<< " *\n * @Doc_" << filename << "_Outputs_Content@\n";
|
||||
|
||||
filePart << "__________________________________________________________________\n\n"
|
||||
<< "Outputs description\n"
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< " * |OVP_DocBegin_" << filename << "_Outputs|\n * |OVP_DocEnd_" << filename << "_Outputs|\n";
|
||||
|
||||
for (size_t i = 0; i < box.getOutputCount(); ++i)
|
||||
{
|
||||
CString name;
|
||||
CIdentifier typeID;
|
||||
CString typeName;
|
||||
box.getOutputName(i, name);
|
||||
box.getOutputType(i, typeID);
|
||||
typeName = m_kernelCtx.getTypeManager().getTypeName(typeID);
|
||||
|
||||
fileSkeleton
|
||||
<< " * \\subsection Doc_" << filename << "_Output_" << i + 1 << " " << i + 1 << ". " << name << "\n"
|
||||
<< " * @Doc_" << filename << "_Output" << i + 1 << "_Content@\n *\n * - Type identifier : \\ref Doc_Streams_"
|
||||
<< transform(typeName.toASCIIString()) << " \"" << typeName << "\" <em>" << typeID.str() << "</em>\n *\n";
|
||||
|
||||
filePart << "\n * |OVP_DocBegin_" << filename << "_Output" << i + 1 << "|\n * |OVP_DocEnd_" << filename << "_Output" << i + 1 <<
|
||||
"|\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (box.getSettingCount())
|
||||
{
|
||||
fileSkeleton
|
||||
<< " * \\section Doc_" << filename << "_Settings Settings\n"
|
||||
// << " * Number of settings : " << box.getSettingCount() << "\n"
|
||||
<< " *\n * @Doc_" << filename << "_Settings_Content@\n";
|
||||
|
||||
filePart
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< "Settings description\n"
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< " * |OVP_DocBegin_" << filename << "_Settings|\n * |OVP_DocEnd_" << filename << "_Settings|\n";
|
||||
|
||||
for (size_t i = 0; i < box.getSettingCount(); ++i)
|
||||
{
|
||||
CString name;
|
||||
CIdentifier typeID;
|
||||
CString defaultValue;
|
||||
CString typeName;
|
||||
box.getSettingName(i, name);
|
||||
box.getSettingType(i, typeID);
|
||||
box.getSettingDefaultValue(i, defaultValue);
|
||||
typeName = m_kernelCtx.getTypeManager().getTypeName(typeID);
|
||||
|
||||
fileSkeleton
|
||||
<< " * \\subsection Doc_" << filename << "_Setting_" << i + 1 << " " << i + 1 << ". " << name << "\n"
|
||||
<< " * @Doc_" << filename << "_Setting" << i + 1 << "_Content@\n"
|
||||
<< " *\n"
|
||||
// "\\ref Doc_Types_" << transform(typeName.toASCIIString())
|
||||
<< " * - Type identifier : <em> " << typeName << " " << typeID.str() << "</em>\n"
|
||||
<< " *\n"
|
||||
<< " * - Default value : [ <em>" << defaultValue << "</em> ]\n"
|
||||
<< " *\n";
|
||||
|
||||
filePart << "\n * |OVP_DocBegin_" << filename << "_Setting" << i + 1 << "|\n * |OVP_DocEnd_" << filename << "_Setting" << i + 1 << "|\n";
|
||||
}
|
||||
}
|
||||
|
||||
// @FIXME CERT
|
||||
#pragma message("WARNING: PluginFunctionality_Visualization block commented out due to Certivibe")
|
||||
|
||||
/*
|
||||
if(rPluginObjectDesc.hasFunctionality(PluginFunctionality_Visualization))
|
||||
{
|
||||
fileSkeleton
|
||||
<< " * \\section Doc_" << filename << "_OnlineVisualizationSettings Online visualization settings\n"
|
||||
<< " *\n"
|
||||
<< " * @Doc_" << filename << "_OnlineVisualizationSettings_Content@\n";
|
||||
|
||||
filePart
|
||||
<< "__________________________________________________________________\n"
|
||||
<< "\n"
|
||||
<< "Online visualization settings\n"
|
||||
<< "__________________________________________________________________\n"
|
||||
<< "\n"
|
||||
<< " * |OVP_DocBegin_" << filename << "_OnlineVisualizationSettings|\n"
|
||||
<< " * |OVP_DocEnd_" << filename << "_OnlineVisualizationSettings|\n";
|
||||
}
|
||||
*/
|
||||
|
||||
fileSkeleton
|
||||
<< " *\n"
|
||||
<< " * \\section Doc_" << filename << "_Examples Examples\n"
|
||||
<< " * @Doc_" << filename << "_Examples_Content@\n"
|
||||
<< " *\n"
|
||||
<< " * \\section Doc_" << filename << "_Miscellaneous Miscellaneous\n"
|
||||
<< " * @Doc_" << filename << "_Miscellaneous_Content@\n"
|
||||
<< " */\n";
|
||||
|
||||
filePart
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< "Examples description\n"
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< " * |OVP_DocBegin_" << filename << "_Examples|\n"
|
||||
<< " * |OVP_DocEnd_" << filename << "_Examples|\n"
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< "Miscellaneous description\n"
|
||||
<< "__________________________________________________________________\n\n"
|
||||
<< " * |OVP_DocBegin_" << filename << "_Miscellaneous|\n"
|
||||
<< " * |OVP_DocEnd_" << filename << "_Miscellaneous|\n"
|
||||
<< " */\n";
|
||||
|
||||
fileSkeleton.close();
|
||||
filePart.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "CPluginObjectDescEnum.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------
|
||||
class CBoxAlgorithmSnapshotGen final : public CPluginObjectDescEnum
|
||||
{
|
||||
public:
|
||||
|
||||
CBoxAlgorithmSnapshotGen(const Kernel::IKernelContext& ctx, const std::string& snapshotDir, const std::string& docTemplateDir);
|
||||
~CBoxAlgorithmSnapshotGen() override;
|
||||
bool callback(const Plugins::IPluginObjectDesc& pod) override;
|
||||
|
||||
protected:
|
||||
|
||||
std::string m_snapshotDir;
|
||||
std::string m_docTemplateDir;
|
||||
std::vector<std::pair<std::string, std::string>> m_categories;
|
||||
std::map<EColors, GdkColor> m_colors;
|
||||
GtkWidget* m_window = nullptr;
|
||||
GtkWidget* m_widget = nullptr;
|
||||
CIdentifier m_scenarioID = CIdentifier::undefined();
|
||||
Kernel::IScenario* m_scenario = nullptr;
|
||||
};
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#include "CPluginObjectDescEnum.hpp"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
|
||||
bool CPluginObjectDescEnum::enumeratePluginObjectDesc()
|
||||
{
|
||||
CIdentifier id;
|
||||
while ((id = m_kernelCtx.getPluginManager().getNextPluginObjectDescIdentifier(id)) != CIdentifier::undefined())
|
||||
{
|
||||
this->callback(*m_kernelCtx.getPluginManager().getPluginObjectDesc(id));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPluginObjectDescEnum::enumeratePluginObjectDesc(const CIdentifier& parentClassID)
|
||||
{
|
||||
CIdentifier id;
|
||||
while ((id = m_kernelCtx.getPluginManager().getNextPluginObjectDescIdentifier(id, parentClassID)) !=
|
||||
CIdentifier::undefined()) { this->callback(*m_kernelCtx.getPluginManager().getPluginObjectDesc(id)); }
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CPluginObjectDescEnum::transform(const std::string& in, const bool removeSlash)
|
||||
{
|
||||
std::string tmp(in);
|
||||
std::string out;
|
||||
bool lastWasSeparator = true;
|
||||
|
||||
for (std::string::size_type i = 0; i < tmp.length(); ++i)
|
||||
{
|
||||
if (std::isalpha(tmp[i]) || std::isdigit(tmp[i]) || (!removeSlash && tmp[i] == '/'))
|
||||
{
|
||||
if (tmp[i] == '/') { out += "_"; }
|
||||
else { out += lastWasSeparator ? std::toupper(tmp[i]) : tmp[i]; }
|
||||
lastWasSeparator = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//if(!lastWasSeparator) { dst+="_"; }
|
||||
lastWasSeparator = true;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace PluginInspector {
|
||||
class CPluginObjectDescEnum
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CPluginObjectDescEnum(const Kernel::IKernelContext& ctx): m_kernelCtx(ctx) { }
|
||||
virtual ~CPluginObjectDescEnum() { }
|
||||
|
||||
virtual bool enumeratePluginObjectDesc();
|
||||
virtual bool enumeratePluginObjectDesc(const CIdentifier& parentClassID);
|
||||
|
||||
virtual bool callback(const Plugins::IPluginObjectDesc& pod) = 0;
|
||||
|
||||
static std::string transform(const std::string& in, const bool removeSlash = false);
|
||||
|
||||
protected:
|
||||
enum class EColors
|
||||
{
|
||||
BackgroundPlayerStarted,
|
||||
BoxBackground, BoxBackgroundSelected, BoxBackgroundMissing, BoxBackgroundObsolete, BoxBackgroundDeprecated,
|
||||
BoxBorder, BoxBorderSelected,
|
||||
BoxInputBackground, BoxInputBorder,
|
||||
BoxOutputBackground, BoxOutputBorder,
|
||||
BoxSettingBackground, BoxSettingBorder,
|
||||
Link, LinkSelected,
|
||||
SelectionArea, SelectionAreaBorder
|
||||
};
|
||||
|
||||
virtual Kernel::ILogManager& getLogManager() const { return m_kernelCtx.getLogManager(); }
|
||||
virtual Kernel::IErrorManager& getErrorManager() const { return m_kernelCtx.getErrorManager(); }
|
||||
const Kernel::IKernelContext& m_kernelCtx;
|
||||
|
||||
static GdkColor colorFromIdentifier(const CIdentifier& identifier)
|
||||
{
|
||||
GdkColor color;
|
||||
const uint64_t res = identifier.id();
|
||||
|
||||
color.pixel = guint16(0);
|
||||
color.red = guint16(((res) & 0xffff) | 0x8000);
|
||||
color.green = guint16(((res >> 16) & 0xffff) | 0x8000);
|
||||
color.blue = guint16(((res >> 32) & 0xffff) | 0x8000);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
static void gdkDrawRoundedRectangle(GdkDrawable* drawable, GdkGC* gc, const gboolean fill, const gint x, const gint y, const gint w, const gint h,
|
||||
const gint r = 8)
|
||||
{
|
||||
if (fill)
|
||||
{
|
||||
gdk_draw_rectangle(drawable, gc, TRUE, x + r, y, w - 2 * r, h);
|
||||
gdk_draw_rectangle(drawable, gc, TRUE, x, y + r, w, h - 2 * r);
|
||||
}
|
||||
else
|
||||
{
|
||||
gdk_draw_line(drawable, gc, x + r, y, x + w - r, y);
|
||||
gdk_draw_line(drawable, gc, x + r, y + h, x + w - r, y + h);
|
||||
gdk_draw_line(drawable, gc, x, y + r, x, y + h - r);
|
||||
gdk_draw_line(drawable, gc, x + w, y + r, x + w, y + h - r);
|
||||
}
|
||||
gdk_draw_arc(drawable, gc, fill, x + w - r * 2, y, r * 2, r * 2, 0 * 64, 90 * 64);
|
||||
gdk_draw_arc(drawable, gc, fill, x, y, r * 2, r * 2, 90 * 64, 90 * 64);
|
||||
gdk_draw_arc(drawable, gc, fill, x, y + h - r * 2, r * 2, r * 2, 180 * 64, 90 * 64);
|
||||
gdk_draw_arc(drawable, gc, fill, x + w - r * 2, y + h - r * 2, r * 2, r * 2, 270 * 64, 90 * 64);
|
||||
}
|
||||
};
|
||||
} // namespace PluginInspector
|
||||
} // namespace OpenViBE
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdk.h>
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
#include "CAlgorithmGlobalDefinesGen.hpp"
|
||||
#include "CBoxAlgorithmDumper.hpp"
|
||||
#include "CBoxAlgorithmSnapshotGen.hpp"
|
||||
#include "CAlgorithmSnapshotGen.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <clocale> // std::setlocale
|
||||
|
||||
typedef struct SConfiguration
|
||||
{
|
||||
SConfiguration() { }
|
||||
|
||||
bool kernelPathOverload = false;
|
||||
bool loadPluginsFromPaths = false;
|
||||
bool dumpPathOverload = false;
|
||||
bool configPathOverload = false;
|
||||
bool listBoxes = false;
|
||||
std::string kernelPath;
|
||||
std::string dumpPath;
|
||||
std::string configPath;
|
||||
std::string listBoxesFile;
|
||||
std::vector<std::string> pluginPaths;
|
||||
} configuration_t;
|
||||
|
||||
|
||||
bool parse_arguments(int argc, char** argv, configuration_t& config)
|
||||
{
|
||||
std::vector<std::string> argValues;
|
||||
|
||||
for (int i = 1; i < argc; ++i) { argValues.push_back(argv[i]); }
|
||||
|
||||
for (auto it = argValues.begin(); it != argValues.end(); ++it)
|
||||
{
|
||||
if (*it == "-h" || *it == "--help")
|
||||
{
|
||||
std::cout << "Usage: " << argv[0] << " [-p <dir1#dir2...>] [-d <dump_path>] [-l boxListFile]" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
// get a list of folders to load plugins from
|
||||
if (*it == "-p")
|
||||
{
|
||||
if (it + 1 != argValues.end())
|
||||
{
|
||||
std::string pluginDirectories = *++it;
|
||||
|
||||
// split the argument to separate paths, the delimiter is "#"
|
||||
std::stringstream ss(pluginDirectories);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, '#')) { config.pluginPaths.push_back(item); }
|
||||
config.loadPluginsFromPaths = true;
|
||||
}
|
||||
else { return false; }
|
||||
}
|
||||
// Configuration path
|
||||
else if (*it == "-c")
|
||||
{
|
||||
if (it + 1 != argValues.end())
|
||||
{
|
||||
config.configPathOverload = true;
|
||||
config.configPath = *++it;
|
||||
}
|
||||
else { return false; }
|
||||
}
|
||||
// List path
|
||||
else if (*it == "-l")
|
||||
{
|
||||
config.listBoxes = true;
|
||||
if (it + 1 != argValues.end()) { config.listBoxesFile = *++it; }
|
||||
else { config.listBoxesFile = ""; } // to cout
|
||||
}
|
||||
// Kernel path
|
||||
else if (*it == "-k")
|
||||
{
|
||||
if (it + 1 != argValues.end())
|
||||
{
|
||||
config.kernelPathOverload = true;
|
||||
config.kernelPath = *++it;
|
||||
}
|
||||
else { return false; }
|
||||
}
|
||||
// Dump path
|
||||
else if (*it == "-d")
|
||||
{
|
||||
if (it + 1 != argValues.end())
|
||||
{
|
||||
config.dumpPathOverload = true;
|
||||
config.dumpPath = *++it;
|
||||
}
|
||||
else { return false; }
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
configuration_t config;
|
||||
|
||||
if (!parse_arguments(argc, argv, config)) { std::cout << "Error parsing arguments" << std::endl; }
|
||||
|
||||
OpenViBE::CKernelLoader loader;
|
||||
|
||||
std::cout << "[ INF ] Created kernel loader, trying to load kernel module" << std::endl;
|
||||
#if defined TARGET_OS_Windows
|
||||
std::string kernelFile = "/openvibe-kernel.dll";
|
||||
#else
|
||||
std::string kernelFile = "/libopenvibe-kernel.so";
|
||||
#endif
|
||||
|
||||
if (config.kernelPathOverload) { kernelFile = config.kernelPath + kernelFile; }
|
||||
else { kernelFile = std::string(OpenViBE::Directories::getLibDir().toASCIIString()) + kernelFile; }
|
||||
|
||||
OpenViBE::CString error;
|
||||
if (!loader.load(OpenViBE::CString(kernelFile.c_str()), &error))
|
||||
{
|
||||
std::cout << "[ FAILED ] Error loading kernel (" << error << ")" << " from [" << kernelFile << "]\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "[ INF ] Kernel module loaded, trying to get kernel descriptor" << std::endl;
|
||||
OpenViBE::Kernel::IKernelDesc* kernelDesc = nullptr;
|
||||
OpenViBE::Kernel::IKernelContext* kernelCtx = nullptr;
|
||||
loader.initialize();
|
||||
loader.getKernelDesc(kernelDesc);
|
||||
if (!kernelDesc) { std::cout << "[ FAILED ] No kernel descriptor" << std::endl; }
|
||||
else
|
||||
{
|
||||
std::cout << "[ INF ] Got kernel descriptor, trying to create kernel" << std::endl;
|
||||
std::string configPath = std::string(OpenViBE::Directories::getDataDir().toASCIIString()) + "/kernel/openvibe.conf";
|
||||
if (config.configPathOverload) { configPath = config.configPath; }
|
||||
kernelCtx = kernelDesc->createKernel("plugin-inspector", OpenViBE::CString(configPath.c_str()));
|
||||
if (!kernelCtx || !kernelCtx->initialize()) { std::cout << "[ FAILED ] No kernel created by kernel descriptor" << std::endl; }
|
||||
else
|
||||
{
|
||||
kernelCtx->getConfigurationManager().addConfigurationFromFile(OpenViBE::CString(configPath.c_str()));
|
||||
kernelCtx->getConfigurationManager().addConfigurationFromFile(
|
||||
OpenViBE::Directories::getDataDir() + "/applications/plugin-inspector/plugin-inspector.conf");
|
||||
OpenViBE::Toolkit::initialize(*kernelCtx);
|
||||
|
||||
OpenViBE::Kernel::IConfigurationManager& configManager = kernelCtx->getConfigurationManager();
|
||||
|
||||
if (config.loadPluginsFromPaths)
|
||||
{
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Loading plugins from specified folders\n";
|
||||
|
||||
std::string pluginPattern;
|
||||
for (const auto& libpath : config.pluginPaths)
|
||||
{
|
||||
// Choose the right pattern for libraries to load depending on the OS
|
||||
#if defined TARGET_OS_Windows
|
||||
pluginPattern = libpath + "/openvibe-plugins-*.dll;";
|
||||
#elif defined TARGET_OS_Linux
|
||||
pluginPattern = libpath + "/libopenvibe-plugins-*.so;"; // + *it + "/lib???.so"
|
||||
#endif
|
||||
kernelCtx->getPluginManager().addPluginsFromFiles(pluginPattern.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Loading plugins as specified by kernel\n";
|
||||
kernelCtx->getPluginManager().addPluginsFromFiles(configManager.expand("${Kernel_Plugins}"));
|
||||
}
|
||||
|
||||
//initialise Gtk before 3D context
|
||||
gtk_init(&argc, &argv);
|
||||
// gtk_rc_parse(Directories::getDataDir() + "/applications/designer/interface.gtkrc");
|
||||
|
||||
// We rely on this with 64bit/gtk 2.24, to roll back gtk_init() sometimes switching
|
||||
// the locale to one where ',' is needed instead of '.' for separators of floats,
|
||||
// causing issues, for example getConfigurationManager.expandAsFloat("0.05") -> 0;
|
||||
// due to implementation by std::stod().
|
||||
std::setlocale(LC_ALL, "C");
|
||||
|
||||
if (configManager.expandAsBoolean("${Kernel_3DVisualizationEnabled}"))
|
||||
{
|
||||
// kernelCtx->getVisualizationManager().initialize3DContext();
|
||||
}
|
||||
|
||||
std::string globalDefinesDir = "";
|
||||
|
||||
std::string algorithmSnapshotDir = "";
|
||||
std::string algorithmDocTemplateDir = "";
|
||||
std::string boxAlgorithmSnapshotDir = "";
|
||||
std::string boxAlgorithmDocTemplateDir = "";
|
||||
|
||||
if (config.dumpPathOverload)
|
||||
{
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping stuff to [" << config.dumpPath << "]\n";
|
||||
algorithmSnapshotDir = config.dumpPath + "/algorithm-snapshots";
|
||||
algorithmDocTemplateDir = config.dumpPath + "/algorithm-doc";
|
||||
boxAlgorithmSnapshotDir = config.dumpPath + "/box-algorithm-snapshots";
|
||||
boxAlgorithmDocTemplateDir = config.dumpPath + "/box-algorithm-doc";
|
||||
globalDefinesDir = config.dumpPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Loading paths from Kernel configuration\n";
|
||||
algorithmSnapshotDir = configManager.expand("${PluginInspector_DumpAlgorithmSnapshotDirectory}");
|
||||
algorithmDocTemplateDir = configManager.expand("${PluginInspector_DumpAlgorithmDocTemplateDirectory}");
|
||||
boxAlgorithmSnapshotDir = configManager.expand("${PluginInspector_DumpBoxAlgorithmSnapshotDirectory}");
|
||||
boxAlgorithmDocTemplateDir = configManager.expand("${PluginInspector_DumpBoxAlgorithmDocTemplateDirectory}");
|
||||
globalDefinesDir = configManager.expand("${PluginInspector_DumpGlobalDefinesDirectory}");
|
||||
}
|
||||
|
||||
if (config.listBoxes)
|
||||
{
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping box identifiers ...\n";
|
||||
OpenViBE::PluginInspector::CBoxAlgorithmDumper boxIDGenerator(*kernelCtx, config.listBoxesFile);
|
||||
boxIDGenerator.enumeratePluginObjectDesc(OV_ClassId_Plugins_BoxAlgorithmDesc);
|
||||
}
|
||||
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping global defines...\n";
|
||||
if (!globalDefinesDir.empty())
|
||||
{
|
||||
OpenViBE::PluginInspector::CAlgorithmGlobalDefinesGen globalDefinesGenerator(*kernelCtx, globalDefinesDir);
|
||||
globalDefinesGenerator.enumeratePluginObjectDesc(OV_ClassId_Plugins_AlgorithmDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Skipped, related PluginInspector tokens are empty in openvibe.conf\n";
|
||||
}
|
||||
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping algorithm snapshots... to [" << algorithmSnapshotDir << "]\n";
|
||||
if (!algorithmSnapshotDir.empty() && !algorithmDocTemplateDir.empty())
|
||||
{
|
||||
OpenViBE::PluginInspector::CAlgorithmSnapshotGen algorithmSnapshotGenerator(*kernelCtx, algorithmSnapshotDir, algorithmDocTemplateDir);
|
||||
algorithmSnapshotGenerator.enumeratePluginObjectDesc(OV_ClassId_Plugins_AlgorithmDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Skipped, related PluginInspector tokens are empty in openvibe.conf\n";
|
||||
}
|
||||
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping box algorithm snapshots...\n";
|
||||
if (!boxAlgorithmSnapshotDir.empty() && !boxAlgorithmDocTemplateDir.empty())
|
||||
{
|
||||
OpenViBE::PluginInspector::CBoxAlgorithmSnapshotGen generator(*kernelCtx, boxAlgorithmSnapshotDir, boxAlgorithmDocTemplateDir);
|
||||
generator.enumeratePluginObjectDesc(OV_ClassId_Plugins_BoxAlgorithmDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Skipped, related PluginInspector tokens are empty in openvibe.conf\n";
|
||||
}
|
||||
|
||||
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Application terminated, releasing allocated objects \n";
|
||||
|
||||
OpenViBE::Toolkit::uninitialize(*kernelCtx);
|
||||
|
||||
kernelDesc->releaseKernel(kernelCtx);
|
||||
}
|
||||
}
|
||||
loader.uninitialize();
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
#pragma once
|
||||
Reference in New Issue
Block a user