commit 6632e58e61221cb06ee376a2d111a19ae20bdd2e Author: blankie Date: Sat Dec 31 15:35:58 2022 +0700 Initial commit - Basic main window - Are you sure you want to exit modal - Basic settings window diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..668de5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +*.obj +logmeow +logmeow.exe diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e3ede80 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "imgui"] + path = imgui + url = https://github.com/ocornut/imgui.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4569556 --- /dev/null +++ b/Makefile @@ -0,0 +1,91 @@ +# +# Cross Platform Makefile +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# +# You will need SDL2 (http://www.libsdl.org): +# Linux: +# apt-get install libsdl2-dev +# Mac OS X: +# brew install sdl2 +# MSYS2: +# pacman -S mingw-w64-i686-SDL2 +# + +#CXX = g++ +#CXX = clang++ + +EXE = logmeow +IMGUI_DIR = imgui +SOURCES = main.cpp config.cpp event_loop.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/misc/cpp/imgui_stdlib.cpp $(IMGUI_DIR)/backends/imgui_impl_sdl.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) +LINUX_GL_LIBS = -lGL + +CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/misc/cpp -I$(IMGUI_DIR)/backends +CXXFLAGS += -g -Wall -Wformat +LIBS = + +##--------------------------------------------------------------------- +## OPENGL ES +##--------------------------------------------------------------------- + +## This assumes a GL ES library available in the system, e.g. libGLESv2.so +# CXXFLAGS += -DIMGUI_IMPL_OPENGL_ES2 +# LINUX_GL_LIBS = -lGLESv2 +## If you're on a Raspberry Pi and want to use the legacy drivers, +## use the following instead: +# LINUX_GL_LIBS = -L/opt/vc/lib -lbrcmGLESv2 + +##--------------------------------------------------------------------- +## BUILD FLAGS PER PLATFORM +##--------------------------------------------------------------------- + +ifeq ($(UNAME_S), Linux) #LINUX + ECHO_MESSAGE = "Linux" + LIBS += $(LINUX_GL_LIBS) -ldl `sdl2-config --libs` + + CXXFLAGS += `sdl2-config --cflags` + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(UNAME_S), Darwin) #APPLE + ECHO_MESSAGE = "Mac OS X" + LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl2-config --libs` + LIBS += -L/usr/local/lib -L/opt/local/lib + + CXXFLAGS += `sdl2-config --cflags` + CXXFLAGS += -I/usr/local/include -I/opt/local/include + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(OS), Windows_NT) + ECHO_MESSAGE = "MinGW" + LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2` + + CXXFLAGS += `pkg-config --cflags sdl2` + CFLAGS = $(CXXFLAGS) +endif + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/misc/cpp/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) diff --git a/config.cpp b/config.cpp new file mode 100644 index 0000000..22a1207 --- /dev/null +++ b/config.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include + +#include "config.h" + +std::string get_config_path() { + const char* path; + + path = getenv("XDG_CONFIG_HOME"); + if (path) { + return std::string(path) + "/logmeow/config"; + } + path = getenv("HOME"); + if (path) { + return std::string(path) + "/.config/logmeow/config"; + } + + throw std::runtime_error("cannot find suitable config folder, please set XDG_CONFIG_HOME or HOME"); +} + +Config parse_config(FILE* file) { + size_t line_capacity_size = 128 * sizeof(char); + char* line = static_cast(malloc(line_capacity_size)); + if (line == nullptr) { + throw std::bad_alloc(); + } + Config config; + + while (true) { + errno = 0; + if (getline(&line, &line_capacity_size, file) < 0) { + int errsv = errno; + free(line); + if (errsv == ENOMEM) { + throw std::bad_alloc(); + } else if (errsv != 0) { + throw std::system_error(errsv, std::generic_category(), "getline()"); + } else { + break; + } + } + if (line_capacity_size == 0 || line[0] == '\0' || line[0] == '#') { + continue; + } + if (char* newline = strchr(line, '\n')) { + *newline = '\0'; + } + + if (strncmp(line, "logcat_command=", 15 * sizeof(char)) == 0) { + config.logcat_command = &line[16]; + } else { + std::invalid_argument e = std::invalid_argument(std::string("unknown config line: ") + line); + free(line); + throw e; + } + } + + return config; +} + +void write_config(std::ofstream& file, const Config& config) { + file << "# This is an auto-generated file, comments made will be lost\n" + << "# This is a poor man's config file \"format\", there are only three legal lines:\n" + << "# # a comment, only available at the start of a line\n" + << "# (an empty line, no whitespace)\n" + << "# key=value pairs, no spaces around the delimiter, and no unknown keys\n\n" + << "logcat_command=" << config.logcat_command; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..b2476a9 --- /dev/null +++ b/config.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +struct Config { + std::string logcat_command; +}; + +std::string get_config_path(); +Config parse_config(FILE* file); +void write_config(std::ofstream& file, const Config& config); diff --git a/event_loop.cpp b/event_loop.cpp new file mode 100644 index 0000000..b6591ea --- /dev/null +++ b/event_loop.cpp @@ -0,0 +1,75 @@ +#include +#include + +#include "config.h" + +static inline void settings_window(Config& inactive_config, bool* show_settings_window) { + // by default, the settings window is a bit cramped + ImGui::SetNextWindowSize(ImVec2(575, 75), ImGuiCond_FirstUseEver); + if (!ImGui::Begin("Settings", show_settings_window)) { + ImGui::End(); + return; + } + ImGui::Text("Persistence coming soon(tm)"); + ImGui::InputTextWithHint("Logcat command", "adb logcat -Dv 'threadtime UTC epoch usec'", &inactive_config.logcat_command); + ImGui::End(); +} + +static inline void main_window(Config& active_config, Config& inactive_config, bool* show_demo_window, + bool* show_settings_window, bool* save_settings_when_settings_window_closed, bool* exit_requested_rev) { + if (!ImGui::Begin("LogMeow", exit_requested_rev)) { + ImGui::End(); + return; + } + ImGui::Checkbox("Show Dear ImGui Demo Window", show_demo_window); + if (ImGui::Button("Settings")) { + if (!*show_settings_window) { + inactive_config = active_config; + } + *show_settings_window = true; + *save_settings_when_settings_window_closed = true; + } + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::End(); +} + +static inline void exit_modal_if_necessary(bool* run_event_loop) { + if (ImGui::BeginPopupModal("Exit?", nullptr, ImGuiWindowFlags_NoResize)) { + ImGui::Text("Are you sure you want to exit?"); + + if (ImGui::Button("Yes", ImVec2(120, 0))) { + *run_event_loop = false; + } + ImGui::SameLine(); + if (ImGui::Button("No", ImVec2(120, 0))) { + ImGui::CloseCurrentPopup(); + } + ImGui::SetItemDefaultFocus(); + ImGui::EndPopup(); + } +} + +void event_loop(Config& active_config, bool* run_event_loop) { + static bool show_demo_window = false; + static bool show_settings_window = false; + static bool save_settings_when_settings_window_closed = false; + static bool exit_requested_rev = true; + static Config inactive_config; + + if (show_demo_window) { + ImGui::ShowDemoWindow(&show_demo_window); + } + + if (show_settings_window) { + settings_window(inactive_config, &show_settings_window); + } else if (save_settings_when_settings_window_closed) { + active_config = inactive_config; + } + + if (!exit_requested_rev) { + ImGui::OpenPopup("Exit?"); + exit_requested_rev = true; + } + exit_modal_if_necessary(run_event_loop); + main_window(active_config, inactive_config, &show_demo_window, &show_settings_window, &save_settings_when_settings_window_closed, &exit_requested_rev); +} diff --git a/event_loop.h b/event_loop.h new file mode 100644 index 0000000..855abde --- /dev/null +++ b/event_loop.h @@ -0,0 +1,5 @@ +#pragma once + +#include "config.h" + +void event_loop(Config& config, bool* run_event_loop); diff --git a/imgui b/imgui new file mode 160000 index 0000000..e57871b --- /dev/null +++ b/imgui @@ -0,0 +1 @@ +Subproject commit e57871bb95faec757e51214bc0e1ae29b13258ab diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..7e4db8e --- /dev/null +++ b/imgui.ini @@ -0,0 +1,20 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][LogMeow] +Pos=60,60 +Size=339,94 +Collapsed=0 + +[Window][Exit?] +Pos=549,324 +Size=264,71 +Collapsed=0 + +[Window][Settings] +Pos=88,228 +Size=575,75 +Collapsed=0 + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..67e69e1 --- /dev/null +++ b/main.cpp @@ -0,0 +1,158 @@ +#include +#include + +#include +#include +#include +#include +#if defined(IMGUI_IMPL_OPENGL_ES2) +#include +#else +#include +#endif + +#include "config.h" +#include "event_loop.h" + +int main(int, char**) +{ + Config config; + FILE* config_file = nullptr; + try { + config_file = fopen(get_config_path().c_str(), "r"); + if (config_file) { + config = parse_config(config_file); + } else if (errno != ENOENT) { + perror("Failed to open config file"); + return 1; + } + } catch (const std::exception& e) { + fprintf(stderr, "Failed to parse config: %s\n", e.what()); + return 1; + } + if (config_file) { + if (fclose(config_file)) { + perror("Failed to close config file"); + } + } + + // Setup SDL + // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems, + // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to the latest version of SDL is recommended!) + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) + { + printf("Error: %s\n", SDL_GetError()); + return -1; + } + + // Decide GL+GLSL versions +#if defined(IMGUI_IMPL_OPENGL_ES2) + // GL ES 2.0 + GLSL 100 + const char* glsl_version = "#version 100"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#elif defined(__APPLE__) + // GL 3.2 Core + GLSL 150 + const char* glsl_version = "#version 150"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); +#else + // GL 3.0 + GLSL 130 + const char* glsl_version = "#version 130"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#endif + + // Create window with graphics context + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_Window* window = SDL_CreateWindow("LogMeow", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + SDL_GLContext gl_context = SDL_GL_CreateContext(window); + SDL_GL_MakeCurrent(window, gl_context); + SDL_GL_SetSwapInterval(1); // Enable vsync + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup Platform/Renderer backends + ImGui_ImplSDL2_InitForOpenGL(window, gl_context); + ImGui_ImplOpenGL3_Init(glsl_version); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - Read 'docs/FONTS.md' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != NULL); + + // Main loop + bool run_event_loop = true; + while (run_event_loop) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL2_ProcessEvent(&event); + if (event.type == SDL_QUIT) { + run_event_loop = false; + } else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) + run_event_loop = false; + } + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + + event_loop(config, &run_event_loop); + + // Rendering + ImGui::Render(); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + glClearColor(0.15f, 0.15f, 0.15f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + SDL_GL_SwapWindow(window); + } + + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + + SDL_GL_DeleteContext(gl_context); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +}