Compare commits
No commits in common. "b3ca7926b2fc88200538e6ea227cf9f3f425ff4d" and "b9b2b983cb8581a1eabcaa01d81659ee910e9c06" have entirely different histories.
b3ca7926b2
...
b9b2b983cb
|
@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
project(blah)
|
project(blah)
|
||||||
#set(CMAKE_BUILD_TYPE Debug)
|
#set(CMAKE_BUILD_TYPE Debug)
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/src/")
|
include_directories("${PROJECT_SOURCE_DIR}/src/")
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
|
||||||
#add_executable(test ${PROJECT_SOURCE_DIR}/src/test.cpp)
|
#add_executable(test ${PROJECT_SOURCE_DIR}/src/test.cpp)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
|
@ -5,29 +5,15 @@
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui_impl_glfw.h"
|
#include "imgui_impl_glfw.h"
|
||||||
#include "imgui_impl_opengl3.h"
|
#include "imgui_impl_opengl3.h"
|
||||||
#include "ui/console.h"
|
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
#include "common/logging.hpp"
|
|
||||||
#include "common/messaging/bus.hpp"
|
|
||||||
|
|
||||||
// Globals
|
|
||||||
static Logger logger;
|
|
||||||
static MessageBus messager(&logger);
|
|
||||||
|
|
||||||
// End globals
|
|
||||||
|
|
||||||
static void error_callback(int error, const char* description)
|
static void error_callback(int error, const char* description)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error %d: %s\n", error, description);
|
fprintf(stderr, "Error %d: %s\n", error, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void debug_messagebus_callback(Message *m) {
|
|
||||||
logger.log(info, "(debug_messagebus_callback) Processed message: '%s'", m->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
GLFWwindow* window;
|
GLFWwindow* window;
|
||||||
|
@ -93,19 +79,10 @@ int main(int argc, char** argv)
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
|
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data),
|
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data),
|
||||||
g_vertex_buffer_data, GL_STATIC_DRAW);
|
g_vertex_buffer_data, GL_STATIC_DRAW);
|
||||||
static Console console(&messager);
|
|
||||||
bool show_console = false;
|
|
||||||
messager.registerCallback(&debug_messagebus_callback);
|
|
||||||
while(!glfwWindowShouldClose(window)) {
|
while(!glfwWindowShouldClose(window)) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
// Process input.
|
// Process input.
|
||||||
|
|
||||||
// Process messages
|
|
||||||
int messages_treated = messager.processAll();
|
|
||||||
if (messages_treated > 0) {
|
|
||||||
logger.log(info, "Treated %d messages this tick", messages_treated);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @TODO Game tick
|
// @TODO Game tick
|
||||||
|
|
||||||
|
|
||||||
|
@ -134,19 +111,26 @@ int main(int argc, char** argv)
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
ImGui_ImplGlfw_NewFrame();
|
ImGui_ImplGlfw_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
{
|
{
|
||||||
static float f = 0.0f;
|
static float f = 0.0f;
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
|
|
||||||
ImGui::Begin("Misc"); // Create a window called "Hello, world!" and append into it.
|
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
|
||||||
|
|
||||||
|
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
|
||||||
|
|
||||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
|
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
|
||||||
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
|
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
|
||||||
|
|
||||||
|
if (ImGui::Button("Button")) { // Buttons return true when clicked (most widgets return true when edited/activated)
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("counter = %d", counter);
|
||||||
|
|
||||||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
console.Draw("Debug Console", &show_console);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalise rendering
|
// Finalise rendering
|
||||||
|
|
|
@ -1,377 +0,0 @@
|
||||||
#include "imgui.h"
|
|
||||||
#include <ctype.h> // toupper
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "common/messaging/bus.hpp"
|
|
||||||
|
|
||||||
struct Console
|
|
||||||
{
|
|
||||||
char InputBuf[256];
|
|
||||||
ImVector<char*> Items;
|
|
||||||
ImVector<const char*> Commands;
|
|
||||||
ImVector<char*> History;
|
|
||||||
int HistoryPos; // -1: new line, 0..History.Size-1 browsing history.
|
|
||||||
ImGuiTextFilter Filter;
|
|
||||||
bool AutoScroll;
|
|
||||||
bool ScrollToBottom;
|
|
||||||
MessageBus* messager;
|
|
||||||
|
|
||||||
Console(MessageBus *messager)
|
|
||||||
{
|
|
||||||
ClearLog();
|
|
||||||
memset(InputBuf, 0, sizeof(InputBuf));
|
|
||||||
HistoryPos = -1;
|
|
||||||
Commands.push_back("HELP");
|
|
||||||
Commands.push_back("HISTORY");
|
|
||||||
Commands.push_back("CLEAR");
|
|
||||||
Commands.push_back("CLASSIFY"); // "classify" is only here to provide an example of "C"+[tab] completing to "CL" and displaying matches.
|
|
||||||
Commands.push_back("CONNECT");
|
|
||||||
AutoScroll = true;
|
|
||||||
ScrollToBottom = false;
|
|
||||||
AddLog("Welcome to Dear ImGui!");
|
|
||||||
this->messager = messager;
|
|
||||||
}
|
|
||||||
~Console()
|
|
||||||
{
|
|
||||||
ClearLog();
|
|
||||||
for (int i = 0; i < History.Size; i++)
|
|
||||||
free(History[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Portable helpers
|
|
||||||
static int Stricmp(const char* str1, const char* str2) { int d; while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } return d; }
|
|
||||||
static int Strnicmp(const char* str1, const char* str2, int n) { int d = 0; while (n > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; n--; } return d; }
|
|
||||||
static char* Strdup(const char *str) { size_t len = strlen(str) + 1; void* buf = malloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)str, len); }
|
|
||||||
static void Strtrim(char* str) { char* str_end = str + strlen(str); while (str_end > str && str_end[-1] == ' ') str_end--; *str_end = 0; }
|
|
||||||
|
|
||||||
void ClearLog()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Items.Size; i++)
|
|
||||||
free(Items[i]);
|
|
||||||
Items.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Connect(const char *command_line) {
|
|
||||||
// Parse
|
|
||||||
bool gotHost = false, gotPort = false, gotParam = false;
|
|
||||||
char default_host[] = "127.0.0.1";
|
|
||||||
char default_port[] = "2800";
|
|
||||||
char* item, *sub_item;
|
|
||||||
char* host, *port;
|
|
||||||
char command[256];
|
|
||||||
memset(command, 0, sizeof(command));
|
|
||||||
strncpy(command, command_line, 256);
|
|
||||||
item = strtok(command, " ");
|
|
||||||
while (item != NULL) {
|
|
||||||
if (Strnicmp(item, "connect", 7) == 0) {
|
|
||||||
item = strtok(NULL, " ");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!gotParam) {
|
|
||||||
gotParam = true;
|
|
||||||
break;
|
|
||||||
// Everything after the the first token following connect is ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gotParam) {
|
|
||||||
sub_item = strtok(item, ":");
|
|
||||||
while (sub_item != NULL) {
|
|
||||||
AddLog("[debug] sub_item is '%s'", sub_item);
|
|
||||||
if (!gotHost) {
|
|
||||||
host = sub_item;
|
|
||||||
gotHost = true;
|
|
||||||
sub_item = strtok(NULL, ":");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!gotPort) {
|
|
||||||
port = sub_item;
|
|
||||||
gotPort = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!gotHost) {
|
|
||||||
host = default_host;
|
|
||||||
}
|
|
||||||
if (!gotPort) {
|
|
||||||
port = default_port;
|
|
||||||
}
|
|
||||||
AddLog("[debug] Parsed host '%s' [default:%d] and port '%s' [default:%d] from connect command.",
|
|
||||||
host, !gotHost, port, !gotPort);
|
|
||||||
// Raise an event
|
|
||||||
this->messager->add("connect");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddLog(const char* fmt, ...) IM_FMTARGS(2)
|
|
||||||
{
|
|
||||||
// FIXME-OPT
|
|
||||||
char buf[1024];
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args);
|
|
||||||
buf[IM_ARRAYSIZE(buf)-1] = 0;
|
|
||||||
va_end(args);
|
|
||||||
Items.push_back(Strdup(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Draw(const char* title, bool* p_open)
|
|
||||||
{
|
|
||||||
ImGui::SetNextWindowSize(ImVec2(520,600), ImGuiCond_FirstUseEver);
|
|
||||||
if (!ImGui::Begin(title, p_open))
|
|
||||||
{
|
|
||||||
ImGui::End();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar. So e.g. IsItemHovered() will return true when hovering the title bar.
|
|
||||||
// Here we create a context menu only available from the title bar.
|
|
||||||
if (ImGui::BeginPopupContextItem())
|
|
||||||
{
|
|
||||||
if (ImGui::MenuItem("Close Console"))
|
|
||||||
*p_open = false;
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::TextWrapped("Enter 'HELP' for help, press TAB to use text completion.");
|
|
||||||
|
|
||||||
// TODO: display items starting from the bottom
|
|
||||||
|
|
||||||
if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine();
|
|
||||||
if (ImGui::SmallButton("Add Dummy Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine();
|
|
||||||
if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine();
|
|
||||||
bool copy_to_clipboard = ImGui::SmallButton("Copy");
|
|
||||||
//static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
// Options menu
|
|
||||||
if (ImGui::BeginPopup("Options"))
|
|
||||||
{
|
|
||||||
ImGui::Checkbox("Auto-scroll", &AutoScroll);
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options, Filter
|
|
||||||
if (ImGui::Button("Options"))
|
|
||||||
ImGui::OpenPopup("Options");
|
|
||||||
ImGui::SameLine();
|
|
||||||
Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); // 1 separator, 1 input text
|
|
||||||
ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar); // Leave room for 1 separator + 1 InputText
|
|
||||||
if (ImGui::BeginPopupContextWindow())
|
|
||||||
{
|
|
||||||
if (ImGui::Selectable("Clear")) ClearLog();
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display every line as a separate entry so we can change their color or add custom widgets. If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());
|
|
||||||
// NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping to only process visible items.
|
|
||||||
// You can seek and display only the lines that are visible using the ImGuiListClipper helper, if your elements are evenly spaced and you have cheap random access to the elements.
|
|
||||||
// To use the clipper we could replace the 'for (int i = 0; i < Items.Size; i++)' loop with:
|
|
||||||
// ImGuiListClipper clipper(Items.Size);
|
|
||||||
// while (clipper.Step())
|
|
||||||
// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
|
||||||
// However, note that you can not use this code as is if a filter is active because it breaks the 'cheap random-access' property. We would need random-access on the post-filtered list.
|
|
||||||
// A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices that passed the filtering test, recomputing this array when user changes the filter,
|
|
||||||
// and appending newly elements as they are inserted. This is left as a task to the user until we can manage to improve this example code!
|
|
||||||
// If your items are of variable size you may want to implement code similar to what ImGuiListClipper does. Or split your data into fixed height items to allow random-seeking into your list.
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,1)); // Tighten spacing
|
|
||||||
if (copy_to_clipboard)
|
|
||||||
ImGui::LogToClipboard();
|
|
||||||
for (int i = 0; i < Items.Size; i++)
|
|
||||||
{
|
|
||||||
const char* item = Items[i];
|
|
||||||
if (!Filter.PassFilter(item))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Normally you would store more information in your item (e.g. make Items[] an array of structure, store color/type etc.)
|
|
||||||
bool pop_color = false;
|
|
||||||
if (strstr(item, "[error]")) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); pop_color = true; }
|
|
||||||
else if (strncmp(item, "# ", 2) == 0) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.6f, 1.0f)); pop_color = true; }
|
|
||||||
ImGui::TextUnformatted(item);
|
|
||||||
if (pop_color)
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
}
|
|
||||||
if (copy_to_clipboard)
|
|
||||||
ImGui::LogFinish();
|
|
||||||
|
|
||||||
if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
|
|
||||||
ImGui::SetScrollHereY(1.0f);
|
|
||||||
ScrollToBottom = false;
|
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
|
||||||
ImGui::EndChild();
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
// Command-line
|
|
||||||
bool reclaim_focus = false;
|
|
||||||
if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this))
|
|
||||||
{
|
|
||||||
char* s = InputBuf;
|
|
||||||
Strtrim(s);
|
|
||||||
if (s[0])
|
|
||||||
ExecCommand(s);
|
|
||||||
strcpy(s, "");
|
|
||||||
reclaim_focus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-focus on window apparition
|
|
||||||
ImGui::SetItemDefaultFocus();
|
|
||||||
if (reclaim_focus)
|
|
||||||
ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExecCommand(const char* command_line)
|
|
||||||
{
|
|
||||||
AddLog("# %s\n", command_line);
|
|
||||||
|
|
||||||
// Insert into history. First find match and delete it so it can be pushed to the back. This isn't trying to be smart or optimal.
|
|
||||||
HistoryPos = -1;
|
|
||||||
for (int i = History.Size-1; i >= 0; i--)
|
|
||||||
if (Stricmp(History[i], command_line) == 0)
|
|
||||||
{
|
|
||||||
free(History[i]);
|
|
||||||
History.erase(History.begin() + i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
History.push_back(Strdup(command_line));
|
|
||||||
|
|
||||||
// Process command
|
|
||||||
if (Stricmp(command_line, "CLEAR") == 0)
|
|
||||||
{
|
|
||||||
ClearLog();
|
|
||||||
}
|
|
||||||
else if (Stricmp(command_line, "HELP") == 0)
|
|
||||||
{
|
|
||||||
AddLog("Commands:");
|
|
||||||
for (int i = 0; i < Commands.Size; i++)
|
|
||||||
AddLog("- %s", Commands[i]);
|
|
||||||
}
|
|
||||||
else if (Stricmp(command_line, "HISTORY") == 0)
|
|
||||||
{
|
|
||||||
int first = History.Size - 10;
|
|
||||||
for (int i = first > 0 ? first : 0; i < History.Size; i++)
|
|
||||||
AddLog("%3d: %s\n", i, History[i]);
|
|
||||||
}
|
|
||||||
else if (Strnicmp(command_line, "CONNECT", 7) == 0) {
|
|
||||||
Connect(command_line);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddLog("Unknown command: '%s'\n", command_line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// On commad input, we scroll to bottom even if AutoScroll==false
|
|
||||||
ScrollToBottom = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) // In C++11 you are better off using lambdas for this sort of forwarding callbacks
|
|
||||||
{
|
|
||||||
Console* console = (Console*)data->UserData;
|
|
||||||
return console->TextEditCallback(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextEditCallback(ImGuiInputTextCallbackData* data)
|
|
||||||
{
|
|
||||||
//AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd);
|
|
||||||
switch (data->EventFlag)
|
|
||||||
{
|
|
||||||
case ImGuiInputTextFlags_CallbackCompletion:
|
|
||||||
{
|
|
||||||
// Example of TEXT COMPLETION
|
|
||||||
|
|
||||||
// Locate beginning of current word
|
|
||||||
const char* word_end = data->Buf + data->CursorPos;
|
|
||||||
const char* word_start = word_end;
|
|
||||||
while (word_start > data->Buf)
|
|
||||||
{
|
|
||||||
const char c = word_start[-1];
|
|
||||||
if (c == ' ' || c == '\t' || c == ',' || c == ';')
|
|
||||||
break;
|
|
||||||
word_start--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a list of candidates
|
|
||||||
ImVector<const char*> candidates;
|
|
||||||
for (int i = 0; i < Commands.Size; i++)
|
|
||||||
if (Strnicmp(Commands[i], word_start, (int)(word_end-word_start)) == 0)
|
|
||||||
candidates.push_back(Commands[i]);
|
|
||||||
|
|
||||||
if (candidates.Size == 0)
|
|
||||||
{
|
|
||||||
// No match
|
|
||||||
AddLog("No match for \"%.*s\"!\n", (int)(word_end-word_start), word_start);
|
|
||||||
}
|
|
||||||
else if (candidates.Size == 1)
|
|
||||||
{
|
|
||||||
// Single match. Delete the beginning of the word and replace it entirely so we've got nice casing
|
|
||||||
data->DeleteChars((int)(word_start-data->Buf), (int)(word_end-word_start));
|
|
||||||
data->InsertChars(data->CursorPos, candidates[0]);
|
|
||||||
data->InsertChars(data->CursorPos, " ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Multiple matches. Complete as much as we can, so inputing "C" will complete to "CL" and display "CLEAR" and "CLASSIFY"
|
|
||||||
int match_len = (int)(word_end - word_start);
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
int c = 0;
|
|
||||||
bool all_candidates_matches = true;
|
|
||||||
for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
|
|
||||||
if (i == 0)
|
|
||||||
c = toupper(candidates[i][match_len]);
|
|
||||||
else if (c == 0 || c != toupper(candidates[i][match_len]))
|
|
||||||
all_candidates_matches = false;
|
|
||||||
if (!all_candidates_matches)
|
|
||||||
break;
|
|
||||||
match_len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match_len > 0)
|
|
||||||
{
|
|
||||||
data->DeleteChars((int)(word_start - data->Buf), (int)(word_end-word_start));
|
|
||||||
data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// List matches
|
|
||||||
AddLog("Possible matches:\n");
|
|
||||||
for (int i = 0; i < candidates.Size; i++)
|
|
||||||
AddLog("- %s\n", candidates[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ImGuiInputTextFlags_CallbackHistory:
|
|
||||||
{
|
|
||||||
// Example of HISTORY
|
|
||||||
const int prev_history_pos = HistoryPos;
|
|
||||||
if (data->EventKey == ImGuiKey_UpArrow)
|
|
||||||
{
|
|
||||||
if (HistoryPos == -1)
|
|
||||||
HistoryPos = History.Size - 1;
|
|
||||||
else if (HistoryPos > 0)
|
|
||||||
HistoryPos--;
|
|
||||||
}
|
|
||||||
else if (data->EventKey == ImGuiKey_DownArrow)
|
|
||||||
{
|
|
||||||
if (HistoryPos != -1)
|
|
||||||
if (++HistoryPos >= History.Size)
|
|
||||||
HistoryPos = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A better implementation would preserve the data on the current input line along with cursor position.
|
|
||||||
if (prev_history_pos != HistoryPos)
|
|
||||||
{
|
|
||||||
const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
|
|
||||||
data->DeleteChars(0, data->BufTextLen);
|
|
||||||
data->InsertChars(0, history_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,117 +0,0 @@
|
||||||
#ifndef _LOGGING_H_
|
|
||||||
#define _LOGGING_H_
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
enum LogLevel {
|
|
||||||
vvvdebug,
|
|
||||||
debug,
|
|
||||||
info,
|
|
||||||
warning,
|
|
||||||
error
|
|
||||||
};
|
|
||||||
|
|
||||||
class Logger {
|
|
||||||
|
|
||||||
LogLevel currentLevel;
|
|
||||||
FILE **fds;
|
|
||||||
int fd_count = 0;
|
|
||||||
public:
|
|
||||||
Logger(LogLevel level = info) {
|
|
||||||
this->currentLevel = level;
|
|
||||||
this->attachFd(stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void attachFd(FILE *fd) {
|
|
||||||
int newSize = this->fd_count + 1;
|
|
||||||
FILE **newfds = (FILE **)calloc(newSize, sizeof(FILE*));
|
|
||||||
if (this->fds != NULL) {
|
|
||||||
memcpy(newfds, this->fds,this->fd_count * sizeof(FILE*));
|
|
||||||
free(this->fds);
|
|
||||||
this->fds = newfds;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->fds = newfds;
|
|
||||||
}
|
|
||||||
this->fds[newSize-1] = fd;
|
|
||||||
this->fd_count = newSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeFd(FILE *fd) {
|
|
||||||
int newSize = this->fd_count - 1;
|
|
||||||
if (newSize <= 0) {
|
|
||||||
free(this->fds);
|
|
||||||
this->fds = NULL;
|
|
||||||
this->fd_count = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FILE **newfds = (FILE **) calloc(newSize, sizeof(FILE*));
|
|
||||||
int new_i = 0;
|
|
||||||
for (int i = 0 ; i < this->fd_count ; i++) {
|
|
||||||
if (fd == this->fds[i]) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
newfds[new_i] = this->fds[i];
|
|
||||||
new_i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(this->fds);
|
|
||||||
this->fds = newfds;
|
|
||||||
this->fd_count = newSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* mapLevel(LogLevel l) {
|
|
||||||
switch (l) {
|
|
||||||
case vvvdebug:
|
|
||||||
return "vvvdebug";
|
|
||||||
break;
|
|
||||||
case debug:
|
|
||||||
return "debug";
|
|
||||||
break;
|
|
||||||
case info:
|
|
||||||
return "info";
|
|
||||||
break;
|
|
||||||
case warning:
|
|
||||||
return "warning";
|
|
||||||
break;
|
|
||||||
case error:
|
|
||||||
return "error";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLevel(LogLevel l) {
|
|
||||||
this->currentLevel = l;
|
|
||||||
}
|
|
||||||
|
|
||||||
void log(LogLevel level, char* str) {
|
|
||||||
this->log(level, "%s", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void log(LogLevel level, const char *fmt, ...) {
|
|
||||||
if (level < this->currentLevel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this->fd_count <= 0) {
|
|
||||||
// No attached fds.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
char buf[1024];
|
|
||||||
memset(buf, 0, 1024*sizeof(char));
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
vsnprintf(buf, 1024, fmt, args);
|
|
||||||
for (int i = 0; i < this->fd_count ; i++) {
|
|
||||||
fprintf(this->fds[i], "[%s]\t%s\n", this->mapLevel(level), buf);
|
|
||||||
fflush(this->fds[i]);
|
|
||||||
}
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif // _LOGGING_H_
|
|
|
@ -1,150 +0,0 @@
|
||||||
#ifndef _MESSAGE_BUS_H
|
|
||||||
#define _MESSAGE_BUS_H
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/logging.hpp"
|
|
||||||
|
|
||||||
struct MessageParam {
|
|
||||||
public:
|
|
||||||
int data_size;
|
|
||||||
void *data;
|
|
||||||
MessageParam(void *src, int size) {
|
|
||||||
data_size = size;
|
|
||||||
data = src;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Message {
|
|
||||||
const char *name;
|
|
||||||
int parameter_count;
|
|
||||||
Message(const char *name) {
|
|
||||||
this->name = name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class MessageBus {
|
|
||||||
// arrray of things
|
|
||||||
// This class is responsible for allocating and freeing memory for the
|
|
||||||
// messages and their parameters.
|
|
||||||
private:
|
|
||||||
Message *bufferStart;
|
|
||||||
int bufferSize;
|
|
||||||
Message *occupiedHead;
|
|
||||||
Message *occupiedTail;
|
|
||||||
int count;
|
|
||||||
Logger *logger;
|
|
||||||
std::vector<void (*)(Message*)> callbacks;
|
|
||||||
|
|
||||||
public:
|
|
||||||
MessageBus(Logger *l, int initial_size = 100) {
|
|
||||||
this->bufferSize = initial_size;
|
|
||||||
this->bufferStart = (Message *) calloc(initial_size, sizeof(struct Message));
|
|
||||||
this->occupiedHead = NULL; // the furthest "into" the buffer we are
|
|
||||||
this->occupiedTail = NULL; // the closest to the "start" of the buffer the useful bits are
|
|
||||||
this->count = 0;
|
|
||||||
this->logger = l;
|
|
||||||
l->log(info, "Initialized MessageBus with a buffer of %d items (%d bytes), starting at %d",
|
|
||||||
initial_size, initial_size*sizeof(struct Message), this->bufferStart);
|
|
||||||
}
|
|
||||||
~MessageBus() {
|
|
||||||
free(this->bufferStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
void registerCallback(void (*fp)(Message*)) {
|
|
||||||
this->callbacks.push_back(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeCallback(void (*fp)(Message*)) {
|
|
||||||
bool found = false;
|
|
||||||
int i;
|
|
||||||
for(i = 0 ; i < this->callbacks.size(); i++) {
|
|
||||||
if (fp == this->callbacks.at(i)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found) {
|
|
||||||
this->callbacks.erase(this->callbacks.begin() + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add(const char *name) {
|
|
||||||
Message *new_item;
|
|
||||||
if (occupiedHead == NULL) {
|
|
||||||
new_item = this->bufferStart;
|
|
||||||
this->occupiedHead = this->bufferStart;
|
|
||||||
this->occupiedTail = this->bufferStart;
|
|
||||||
this->count++;
|
|
||||||
this->logger->log(info, "Added new item at start of empty buffer");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Do we have free space?
|
|
||||||
if (count < bufferSize) {
|
|
||||||
new_item = this->occupiedHead + sizeof(struct Message);
|
|
||||||
this->occupiedHead += sizeof(struct Message);
|
|
||||||
this->count++;
|
|
||||||
this->logger->log(info, "Added new item in buffer with free space");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Free up space (realloc, grow if necessary);
|
|
||||||
this->realloc();
|
|
||||||
new_item = this->occupiedHead + sizeof(struct Message);
|
|
||||||
this->occupiedHead += sizeof(struct Message);
|
|
||||||
this->count++;
|
|
||||||
this->logger->log(info, "Add new item after re-allocation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_item->name = name;
|
|
||||||
this->logger->log(info, "%d items in MessageBus buffer", this->count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove() {
|
|
||||||
this->occupiedTail += sizeof(struct Message);
|
|
||||||
this->count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void realloc() {
|
|
||||||
// How much free space do we have at the start?
|
|
||||||
int start_available = (this->occupiedTail - this->bufferStart) / sizeof(struct Message);
|
|
||||||
// We'll do a full grow if there's less than 25% free
|
|
||||||
int newsize = this->bufferSize;
|
|
||||||
this->logger->log(info, "Re-allocating MessageBus buffer to gain some space");
|
|
||||||
if (start_available < this->bufferSize / 25) {
|
|
||||||
int newsize = this->bufferSize * 2;
|
|
||||||
this->logger->log(info, "Growing message buffer from %d to %d", this->bufferSize, newsize);
|
|
||||||
}
|
|
||||||
Message *newStart = (Message *) calloc(newsize, sizeof(struct Message));
|
|
||||||
int copySize = this->occupiedHead + sizeof(struct Message) - this->occupiedTail;
|
|
||||||
memcpy(newStart, this->occupiedTail, copySize);
|
|
||||||
Message *newHead;
|
|
||||||
newHead = newStart + (this->occupiedHead - this->occupiedTail);
|
|
||||||
this->bufferStart = newStart;
|
|
||||||
this->occupiedTail = newStart;
|
|
||||||
this->occupiedHead = newHead;
|
|
||||||
}
|
|
||||||
|
|
||||||
int processAll() {
|
|
||||||
int done = 0;
|
|
||||||
while(this->count > 0) {
|
|
||||||
this->process();
|
|
||||||
done++;
|
|
||||||
}
|
|
||||||
return done;
|
|
||||||
}
|
|
||||||
|
|
||||||
void process() {
|
|
||||||
assert(this->count > 0);
|
|
||||||
// Call all the listening entities that care about this message or whatever
|
|
||||||
for (int i = 0 ; i < this->callbacks.size() ; i++) {
|
|
||||||
this->callbacks.at(i)(this->occupiedTail);
|
|
||||||
}
|
|
||||||
this->remove();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue