]> code.bitgloo.com Git - clyne/stmdspgui.git/commitdiff
more refactor; draw samples grid and cursor; gen load fixes
authorClyne Sullivan <clyne@bitgloo.com>
Tue, 23 Nov 2021 00:44:48 +0000 (19:44 -0500)
committerClyne Sullivan <clyne@bitgloo.com>
Tue, 23 Nov 2021 00:44:48 +0000 (19:44 -0500)
13 files changed:
Makefile
source/circular.hpp [new file with mode: 0644]
source/device.cpp
source/device_formula.cpp
source/file.cpp
source/gui.cpp
source/gui_device.cpp
source/imgui/TextEditor.cpp
source/imgui/TextEditor.h
source/imgui/imgui.h
source/logview.cpp [new file with mode: 0644]
source/logview.h
source/main.cpp

index 26e0208e4f9062f0f9ab52cd0fc5275509b556e2..81e580d2162b48a0af44f7a2ac11b25cfec113c9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -24,7 +24,7 @@ $(OUTPUT): $(OFILES)
 
 clean:
        @echo "  CLEAN"
-       @rm $(OFILES) $(OUTPUT)
+       @rm -f $(OFILES) $(OUTPUT)
 
 %.o: %.cpp
        @echo "  CXX   " $<
diff --git a/source/circular.hpp b/source/circular.hpp
new file mode 100644 (file)
index 0000000..33b8ee0
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef CIRCULAR_HPP
+#define CIRCULAR_HPP
+
+#include <iterator>
+
+template<template<typename> class Container, typename T>
+class CircularBuffer
+{
+public:
+    CircularBuffer(Container<T>& container) :
+        m_begin(std::begin(container)),
+        m_end(std::end(container)),
+        m_current(std::begin(container)) {}
+
+    void put(const T& value) noexcept {
+        *m_current = value;
+        if (++m_current == m_end)
+            m_current = m_begin;
+    }
+
+    std::size_t size() const noexcept {
+        return std::distance(m_begin, m_end);
+    }
+
+private:
+    Container<T>::iterator m_begin;
+    Container<T>::iterator m_end;
+    Container<T>::iterator m_current;
+};
+
+#endif // CIRCULAR_HPP
+
index df3f3614728bd92ab68eadec47986286edf3be5e..abcc88ab1979176feb12c54518c9b752640633ad 100644 (file)
 
 #include "stmdsp.hpp"
 
+#include "circular.hpp"
 #include "imgui.h"
 #include "wav.hpp"
 
 #include <array>
+#include <cctype>
 #include <charconv>
 #include <cmath>
 #include <deque>
@@ -30,7 +32,7 @@
 
 extern std::string tempFileName;
 extern void log(const std::string& str);
-extern std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string_view);
+extern std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string&);
 
 std::shared_ptr<stmdsp::device> m_device;
 
@@ -62,14 +64,16 @@ static std::vector<stmdsp::dacsample_t> tryReceiveChunk(
     std::shared_ptr<stmdsp::device> device,
     auto readFunc)
 {
-    int tries = -1;
-    do {
+    for (int tries = 0; tries < 100; ++tries) {
+        if (!device->is_running())
+            break;
+
         const auto chunk = readFunc(device.get());
         if (!chunk.empty())
             return chunk;
         else
             std::this_thread::sleep_for(std::chrono::microseconds(20));
-    } while (++tries < 100 && device->is_running());
+    }
 
     return {};
 }
@@ -94,15 +98,13 @@ static void drawSamplesTask(std::shared_ptr<stmdsp::device> device)
 
     const auto bufferTime = getBufferPeriod(device);
 
-    std::unique_lock<std::timed_mutex> lockDraw (mutexDrawSamples, std::defer_lock);
-    std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
-
-    auto addToQueue = [&lockDraw](auto& queue, const auto& chunk) {
-        lockDraw.lock();
+    const auto addToQueue = [](auto& queue, const auto& chunk) {
+        std::scoped_lock lock (mutexDrawSamples);
         std::copy(chunk.cbegin(), chunk.cend(), std::back_inserter(queue));
-        lockDraw.unlock();
     };
 
+    std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
+
     while (device && device->is_running()) {
         const auto next = std::chrono::high_resolution_clock::now() + bufferTime;
 
@@ -112,7 +114,7 @@ static void drawSamplesTask(std::shared_ptr<stmdsp::device> device)
             lockDevice.unlock();
 
             addToQueue(drawSamplesQueue, chunk);
-            if (logSamplesFile.good()) {
+            if (logSamplesFile.is_open()) {
                 for (const auto& s : chunk)
                     logSamplesFile << s << '\n';
             }
@@ -145,29 +147,29 @@ static void feedSigGenTask(std::shared_ptr<stmdsp::device> device)
 
     std::vector<stmdsp::dacsample_t> wavBuf (device->get_buffer_size() * 2, 2048);
 
-    std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
+    {
+        std::scoped_lock lock (mutexDeviceLoad);
+        device->siggen_upload(wavBuf.data(), wavBuf.size());
+        device->siggen_start();
+        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+    }
 
-    lockDevice.lock();
-    device->siggen_upload(wavBuf.data(), wavBuf.size());
     wavBuf.resize(wavBuf.size() / 2);
-    device->siggen_start();
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-    lockDevice.unlock();
-
     std::vector<int16_t> wavIntBuf (wavBuf.size());
 
     while (device->is_siggening()) {
         const auto next = std::chrono::high_resolution_clock::now() + delay;
 
         wavOutput.next(wavIntBuf.data(), wavIntBuf.size());
-        auto src = wavIntBuf.cbegin();
-        std::generate(wavBuf.begin(), wavBuf.end(),
-            [&src] { return static_cast<stmdsp::dacsample_t>(*src++ / 16 + 2048); });
+        std::transform(wavIntBuf.cbegin(), wavIntBuf.cend(),
+            wavBuf.begin(),
+            [](auto i) { return static_cast<stmdsp::dacsample_t>(i / 16 + 2048); });
 
-        lockDevice.lock();
-        while (!device->siggen_upload(wavBuf.data(), wavBuf.size()))
-            std::this_thread::sleep_for(uploadDelay);
-        lockDevice.unlock();
+        {
+            std::scoped_lock lock (mutexDeviceLoad);
+            while (!device->siggen_upload(wavBuf.data(), wavBuf.size()))
+                std::this_thread::sleep_for(uploadDelay);
+        }
 
         std::this_thread::sleep_until(next);
     }
@@ -179,10 +181,9 @@ static void statusTask(std::shared_ptr<stmdsp::device> device)
         return;
 
     while (device->connected()) {
-        std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
-        lockDevice.lock();
+        mutexDeviceLoad.lock();
         const auto [status, error] = device->get_status();
-        lockDevice.unlock();
+        mutexDeviceLoad.unlock();
 
         if (error != stmdsp::Error::None) {
             switch (error) {
@@ -214,7 +215,7 @@ void deviceLoadAudioFile(const std::string& file)
 void deviceLoadLogFile(const std::string& file)
 {
     logSamplesFile = std::ofstream(file);
-    if (logSamplesFile.good())
+    if (logSamplesFile.is_open())
         log("Log file ready.");
     else
         log("Error: Could not open log file.");
@@ -261,7 +262,7 @@ bool deviceConnect()
 
     if (!m_device) {
         stmdsp::scanner scanner;
-        if (auto devices = scanner.scan(); !devices.empty()) {
+        if (const auto devices = scanner.scan(); !devices.empty()) {
             try {
                 m_device.reset(new stmdsp::device(devices.front()));
             } catch (...) {
@@ -307,7 +308,7 @@ void deviceStart(bool measureCodeTime, bool logResults, bool drawSamples)
             std::this_thread::sleep_for(std::chrono::microseconds(150));
             m_device->continuous_stop();
         }
-        if (logSamplesFile.good()) {
+        if (logSamplesFile.is_open()) {
             logSamplesFile.close();
             log("Log file saved and closed.");
         }
@@ -329,12 +330,9 @@ void deviceAlgorithmUpload()
 {
     if (!m_device) {
         log("No device connected.");
-        return;
     } else if (m_device->is_running()) {
-        return;
-    }
-
-    if (std::ifstream algo (tempFileName + ".o"); algo.good()) {
+        log("Cannot upload algorithm while running.");
+    } else if (std::ifstream algo (tempFileName + ".o"); algo.is_open()) {
         std::ostringstream sstr;
         sstr << algo.rdbuf();
         auto str = sstr.str();
@@ -350,7 +348,9 @@ void deviceAlgorithmUnload()
 {
     if (!m_device) {
         log("No device connected.");
-    } else if (!m_device->is_running()) {
+    } else if (m_device->is_running()) {
+        log("Cannot unload algorithm while running.");
+    } else {
         m_device->unload_filter();
         log("Algorithm unloaded.");
     }
@@ -361,85 +361,82 @@ void deviceGenLoadList(const std::string_view list)
     std::vector<stmdsp::dacsample_t> samples;
 
     auto it = list.cbegin();
-    while (it != list.cend() && samples.size() < stmdsp::SAMPLES_MAX * 2) {
-        const auto end = list.find_first_not_of("0123456789",
-            std::distance(list.cbegin(), it));
-        const auto itend = end != std::string_view::npos ? list.cbegin() + end
-                                                         : list.cend();
+    while (it != list.cend()) {
+        const auto itend = std::find_if(it, list.cend(),
+            [](char c) { return !isdigit(c); });
+
         unsigned long n;
-        const auto [ptr, ec] = std::from_chars(it, itend, n);
-        if (ec != std::errc())
+        const auto ec = std::from_chars(it, itend, n).ec;
+        if (ec != std::errc()) {
+            log("Error: Bad data in sample list.");
+            break;
+        } else if (n > 4095) {
+            log("Error: Sample data value larger than max of 4095.");
             break;
+        } else {
+            samples.push_back(n & 4095);
+            if (samples.size() >= stmdsp::SAMPLES_MAX * 2) {
+                log("Error: Too many samples for signal generator.");
+                break;
+            }
+        }
 
-        samples.push_back(n & 4095);
         it = itend;
     }
 
-    if (samples.size() <= stmdsp::SAMPLES_MAX * 2) {
+    if (it == list.cend()) {
         // DAC buffer must be of even size
         if (samples.size() % 2 != 0)
             samples.push_back(samples.back());
 
-        if (m_device)
-            m_device->siggen_upload(samples.data(), samples.size());
+        m_device->siggen_upload(samples.data(), samples.size());
         log("Generator ready.");
-    } else {
-        log("Error: Too many samples for signal generator.");
     }
 }
 
-void deviceGenLoadFormula(std::string_view formula)
+void deviceGenLoadFormula(const std::string& formula)
 {
     auto samples = deviceGenLoadFormulaEval(formula);
 
     if (!samples.empty()) {
-        if (m_device)
-            m_device->siggen_upload(samples.data(), samples.size());
-
+        m_device->siggen_upload(samples.data(), samples.size());
         log("Generator ready.");
     } else {
         log("Error: Bad formula.");
     }
 }
 
-void pullFromQueue(
+std::size_t pullFromQueue(
     std::deque<stmdsp::dacsample_t>& queue,
-    std::vector<stmdsp::dacsample_t>& buffer,
-    decltype(buffer.begin())& bufferCursor,
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ,
     double timeframe)
 {
-    if (buffer.size() != drawSamplesBufferSize) {
-        buffer.resize(drawSamplesBufferSize);
-        bufferCursor = buffer.begin();
-    }
+    if (circ.size() != drawSamplesBufferSize)
+        return drawSamplesBufferSize;
 
     std::scoped_lock lock (mutexDrawSamples);
 
-    auto count = drawSamplesBufferSize / (60. * timeframe) * 1.025;
-    count = std::min(drawSamplesInputQueue.size(),
-        static_cast<std::size_t>(count));
-    for (auto i = count; i; --i) {
-        *bufferCursor = queue.front();
+    const auto desiredCount = drawSamplesBufferSize / (60. * timeframe) * 1.025;
+    auto count = std::min(queue.size(), static_cast<std::size_t>(desiredCount));
+    while (count--) {
+        circ.put(queue.front());
         queue.pop_front();
-
-        if (++bufferCursor == buffer.end())
-            bufferCursor = buffer.begin();
     }
+
+    return 0;
 }
 
-void pullFromDrawQueue(
-    std::vector<stmdsp::dacsample_t>& buffer,
-    decltype(buffer.begin())& bufferCursor,
+std::size_t pullFromDrawQueue(
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ,
     double timeframe)
 {
-    pullFromQueue(drawSamplesQueue, buffer, bufferCursor, timeframe);
+    return pullFromQueue(drawSamplesQueue, circ, timeframe);
 }
 
-void pullFromInputDrawQueue(
-    std::vector<stmdsp::dacsample_t>& buffer,
-    decltype(buffer.begin())& bufferCursor,
+std::size_t pullFromInputDrawQueue(
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ,
     double timeframe)
 {
-    pullFromQueue(drawSamplesInputQueue, buffer, bufferCursor, timeframe);
+    return pullFromQueue(drawSamplesInputQueue, circ, timeframe);
 }
 
index 5c1ca16e162ea64da81587936256018055995399..c94eab74de1c6e115760b5213128a2e3f8e0be6b 100644 (file)
@@ -5,7 +5,7 @@
 #include <string_view>
 #include <vector>
 
-std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string_view formulaString)
+std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string& formulaString)
 {
     double x = 0;
 
@@ -16,13 +16,17 @@ std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string_view
     symbol_table.add_variable("x", x);
     symbol_table.add_constants();
     expression.register_symbol_table(symbol_table);
-    parser.compile(std::string(formulaString), expression);
+    parser.compile(formulaString, expression);
 
     std::vector<stmdsp::dacsample_t> samples (stmdsp::SAMPLES_MAX);
 
-    std::generate(samples.begin(), samples.end(),
-                  [&] { ++x; return static_cast<stmdsp::dacsample_t>(expression.value()); });
+    auto genFun = [&x, &expression] {
+        stmdsp::dacsample_t s = expression.value();
+        ++x;
+        return s;
+    };
 
+    std::generate(samples.begin(), samples.end(), genFun);
     return samples;
 }
 
index dfd914827502c588f5b82a02ed43ead6465c15c8..a5ef1d8cb156a2faf5b7c8d3f25d9b6a09524268 100644 (file)
@@ -26,6 +26,8 @@
 #include <string>
 #include <vector>
 
+#include <SDL2/SDL.h>
+
 extern TextEditor editor;
 extern void log(const std::string& str);
 
@@ -133,8 +135,8 @@ void fileRenderMenu()
 
         ImGui::Separator();
         if (ImGui::MenuItem("Quit")) {
-            extern bool done;
-            done = true;
+            SDL_Event quitEvent (SDL_QUIT);
+            SDL_PushEvent(&quitEvent);
         }
 
         ImGui::EndMenu();
index 9720442b4835f7231be84f1ab99ad48f33c20c51..515d4718537f741efc419e10967ddf9e332773f4 100644 (file)
@@ -108,8 +108,10 @@ void guiRender(void (*func)())
    SDL_GL_SwapWindow(window);
 }
 
-void guiHandleEvents(bool& done)
+bool guiHandleEvents()
 {
+    bool done = false;
+
     for (SDL_Event event; SDL_PollEvent(&event);) {
         ImGui_ImplSDL2_ProcessEvent(&event);
         if (event.type == SDL_QUIT)
@@ -117,6 +119,8 @@ void guiHandleEvents(bool& done)
         if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
             done = true;
     }
+
+    return done;
 }
 
 void guiShutdown()
index 5399c8bb5dd62c7b2d3cffea22a19a1debe9d8af..a1d05552ffef32ee5c6ec6cc5d7c2be9cff27683 100644 (file)
@@ -1,3 +1,4 @@
+#include "circular.hpp"
 #include "imgui.h"
 #include "imgui_internal.h"
 #include "ImGuiFileDialog.h"
@@ -5,6 +6,7 @@
 #include "stmdsp.hpp"
 
 #include <array>
+#include <cstdio>
 #include <memory>
 #include <string>
 #include <string_view>
@@ -15,7 +17,7 @@ extern std::shared_ptr<stmdsp::device> m_device;
 void deviceAlgorithmUnload();
 void deviceAlgorithmUpload();
 bool deviceConnect();
-void deviceGenLoadFormula(std::string_view list);
+void deviceGenLoadFormula(const std::string& list);
 void deviceGenLoadList(std::string_view list);
 bool deviceGenStartToggle();
 void deviceLoadAudioFile(const std::string& file);
@@ -24,13 +26,11 @@ void deviceSetSampleRate(unsigned int index);
 void deviceSetInputDrawing(bool enabled);
 void deviceStart(bool measureCodeTime, bool logResults, bool drawSamples);
 void deviceUpdateDrawBufferSize(double timeframe);
-void pullFromDrawQueue(
-    std::vector<stmdsp::dacsample_t>& buffer,
-    decltype(buffer.begin())& bufferCursor,
+std::size_t pullFromDrawQueue(
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ,
     double timeframe);
-void pullFromInputDrawQueue(
-    std::vector<stmdsp::dacsample_t>& buffer,
-    decltype(buffer.begin())& bufferCursor,
+std::size_t pullFromInputDrawQueue(
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ,
     double timeframe);
 
 static std::string sampleRatePreview = "?";
@@ -56,11 +56,8 @@ void deviceRenderMenu()
     };
 
     if (ImGui::BeginMenu("Run")) {
-        const bool isConnected = m_device ? true : false;
-        const bool isRunning = isConnected && m_device->is_running();
-
         static std::string connectLabel ("Connect");
-        addMenuItem(connectLabel, !isConnected || !isRunning, [&] {
+        addMenuItem(connectLabel, !m_device || !m_device->is_running(), [&] {
                 if (deviceConnect()) {
                     connectLabel = "Disconnect";
                     sampleRatePreview =
@@ -68,9 +65,15 @@ void deviceRenderMenu()
                     deviceUpdateDrawBufferSize(drawSamplesTimeframe);
                 } else {
                     connectLabel = "Connect";
+                    measureCodeTime = false;
+                    logResults = false;
+                    drawSamples = false;
                 }
             });
 
+        const bool isConnected = m_device ? true : false;
+        const bool isRunning = isConnected && m_device->is_running();
+
         ImGui::Separator();
 
         static std::string startLabel ("Start");
@@ -153,12 +156,11 @@ void deviceRenderToolbar()
 
 void deviceRenderWidgets()
 {
-    static std::string siggenInput;
+    static std::string siggenInput (32768, '\0');
     static int siggenOption = 0;
 
     if (popupRequestSiggen) {
         popupRequestSiggen = false;
-        siggenInput.clear();
         ImGui::OpenPopup("siggen");
     } else if (popupRequestBuffer) {
         popupRequestBuffer = false;
@@ -166,15 +168,19 @@ void deviceRenderWidgets()
     } else if (popupRequestLog) {
         popupRequestLog = false;
         ImGuiFileDialog::Instance()->OpenModal(
-            "ChooseFileLogGen", "Choose File", ".csv", ".");
+            "ChooseFileLog", "Choose File", ".csv", ".");
     }
 
     if (ImGui::BeginPopup("siggen")) {
-        if (ImGui::RadioButton("List", &siggenOption, 0))
-            siggenInput.clear();
+        if (ImGui::RadioButton("List", &siggenOption, 0)) {
+            siggenInput.resize(32768);
+            siggenInput[0] = '\0';
+        }
         ImGui::SameLine();
-        if (ImGui::RadioButton("Formula", &siggenOption, 1))
-            siggenInput.clear();
+        if (ImGui::RadioButton("Formula", &siggenOption, 1)) {
+            siggenInput.resize(1024);
+            siggenInput[0] = '\0';
+        }
         ImGui::SameLine();
         if (ImGui::RadioButton("Audio File", &siggenOption, 2))
             siggenInput.clear();
@@ -183,7 +189,7 @@ void deviceRenderWidgets()
             if (ImGui::Button("Choose File")) {
                 // This dialog will override the siggen popup, closing it.
                 ImGuiFileDialog::Instance()->OpenModal(
-                    "ChooseFileLogGen", "Choose File", ".wav", ".");
+                    "ChooseFileGen", "Choose File", ".wav", ".");
             }
         } else {
             ImGui::Text(siggenOption == 0 ? "Enter a list of numbers:"
@@ -201,10 +207,10 @@ void deviceRenderWidgets()
         if (ImGui::Button("Save")) {
             switch (siggenOption) {
             case 0:
-                deviceGenLoadList(siggenInput);
+                deviceGenLoadList(siggenInput.substr(0, siggenInput.find('\0')));
                 break;
             case 1:
-                deviceGenLoadFormula(siggenInput);
+                deviceGenLoadFormula(siggenInput.substr(0, siggenInput.find('\0')));
                 break;
             case 2:
                 break;
@@ -238,18 +244,27 @@ void deviceRenderWidgets()
         ImGui::EndPopup();
     }
 
-    if (ImGuiFileDialog::Instance()->Display("ChooseFileLogGen",
+    if (ImGuiFileDialog::Instance()->Display("ChooseFileLog",
                                              ImGuiWindowFlags_NoCollapse,
                                              ImVec2(460, 540)))
     {
         if (ImGuiFileDialog::Instance()->IsOk()) {
-            auto filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
-            auto ext = filePathName.substr(filePathName.size() - 4);
+            const auto filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
+            deviceLoadLogFile(filePathName);
+        } else {
+            logResults = false;
+        }
+
+        ImGuiFileDialog::Instance()->Close();
+    }
 
-            if (ext.compare(".wav") == 0)
-                deviceLoadAudioFile(filePathName);
-            else if (ext.compare(".csv") == 0)
-                deviceLoadLogFile(filePathName);
+    if (ImGuiFileDialog::Instance()->Display("ChooseFileGen",
+                                             ImGuiWindowFlags_NoCollapse,
+                                             ImVec2(460, 540)))
+    {
+        if (ImGuiFileDialog::Instance()->IsOk()) {
+            const auto filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
+            deviceLoadAudioFile(filePathName);
         }
 
         ImGuiFileDialog::Instance()->Close();
@@ -260,9 +275,9 @@ void deviceRenderDraw()
 {
     if (drawSamples) {
         static std::vector<stmdsp::dacsample_t> buffer;
-        static decltype(buffer.begin()) bufferCursor;
         static std::vector<stmdsp::dacsample_t> bufferInput;
-        static decltype(bufferInput.begin()) bufferInputCursor;
+        static auto bufferCirc = CircularBuffer(buffer);
+        static auto bufferInputCirc = CircularBuffer(bufferInput);
 
         static bool drawSamplesInput = false;
         static unsigned int yMinMax = 4095;
@@ -295,16 +310,50 @@ void deviceRenderDraw()
             yMinMax = std::min(4095u, (yMinMax << 1) | 1);
         }
 
-        pullFromDrawQueue(buffer, bufferCursor, drawSamplesTimeframe);
-        if (drawSamplesInput)
-            pullFromInputDrawQueue(bufferInput, bufferInputCursor, drawSamplesTimeframe);
+        auto newSize = pullFromDrawQueue(bufferCirc, drawSamplesTimeframe);
+        if (newSize > 0) {
+            buffer.resize(newSize);
+            bufferCirc = CircularBuffer(buffer);
+            pullFromDrawQueue(bufferCirc, drawSamplesTimeframe);
+        }
+
+        if (drawSamplesInput) {
+            auto newSize = pullFromInputDrawQueue(bufferInputCirc, drawSamplesTimeframe);
+            if (newSize > 0) {
+                bufferInput.resize(newSize);
+                bufferInputCirc = CircularBuffer(bufferInput);
+                pullFromInputDrawQueue(bufferInputCirc, drawSamplesTimeframe);
+            }
+        }
 
         auto drawList = ImGui::GetWindowDrawList();
         ImVec2 p0 = ImGui::GetWindowPos();
         auto size = ImGui::GetWindowSize();
         p0.y += 65;
         size.y -= 70;
-        drawList->AddRectFilled(p0, {p0.x + size.x, p0.y + size.y}, IM_COL32(0, 0, 0, 255));
+        drawList->AddRectFilled(p0, {p0.x + size.x, p0.y + size.y}, IM_COL32_BLACK);
+
+        const auto lcMinor = ImGui::GetColorU32(IM_COL32(40, 40, 40, 255));
+        const auto lcMajor = ImGui::GetColorU32(IM_COL32(140, 140, 140, 255));
+
+        {
+            const float yinc = (3. / 3.3) * size.y / 12.f;
+            const float center = p0.y + size.y / 2;
+            drawList->AddLine({p0.x, center}, {p0.x + size.x, center}, ImGui::GetColorU32(IM_COL32_WHITE));
+            for (int i = 1; i < 7; ++i) {
+                drawList->AddLine({p0.x, center + i * yinc}, {p0.x + size.x, center + i * yinc}, (i % 2) ? lcMinor : lcMajor);
+                drawList->AddLine({p0.x, center - i * yinc}, {p0.x + size.x, center - i * yinc}, (i % 2) ? lcMinor : lcMajor);
+            }
+        }
+        {
+            const float xinc = size.x / 16.f;
+            const float center = p0.x + size.x / 2;
+            drawList->AddLine({center, p0.y}, {center, p0.y + size.y}, ImGui::GetColorU32(IM_COL32_WHITE));
+            for (int i = 1; i < 8; ++i) {
+                drawList->AddLine({center + i * xinc, p0.y}, {center + i * xinc, p0.y + size.y}, (i % 2) ? lcMinor : lcMajor);
+                drawList->AddLine({center - i * xinc, p0.y}, {center - i * xinc, p0.y + size.y}, (i % 2) ? lcMinor : lcMajor);
+            }
+        }
 
         const float di = static_cast<float>(buffer.size()) / size.x;
         const float dx = std::ceil(size.x / static_cast<float>(buffer.size()));
@@ -334,6 +383,18 @@ void deviceRenderDraw()
             }
         }
 
+        const auto mouse = ImGui::GetMousePos();
+        if (mouse.x > p0.x && mouse.x < p0.x + size.x &&
+            mouse.y > p0.y && mouse.y < p0.y + size.y)
+        {
+            const std::size_t si = (mouse.x - p0.x) / size.x * buffer.size();
+            const float s = buffer[si] / 4095.f * 6.6f - 3.3f;
+            char buf[12];
+            snprintf(buf, 16, "   %1.3fV", s);
+            drawList->AddLine({mouse.x, p0.y}, {mouse.x, p0.y + size.y}, IM_COL32(255, 255, 0, 255));
+            drawList->AddText(ImGui::GetMousePos(), IM_COL32(210, 210, 0, 255), buf);
+        }
+
         ImGui::End();
     }
 }
index 02966f0bb0f8883812da4b2311ab1be6670d8bd8..b45a21ea75d7f13fc8a4bd99711a6eb9dd425fab 100644 (file)
@@ -50,7 +50,7 @@ TextEditor::TextEditor()
        , mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
 {
        SetPalette(GetDarkPalette());
-       SetLanguageDefinition(LanguageDefinition::HLSL());
+       SetLanguageDefinition(LanguageDefinition::CPlusPlus());
        mLines.push_back(Line());
 }
 
@@ -2804,357 +2804,3 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::CPlusPlus(
        return langDef;
 }
 
-const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::HLSL()
-{
-       static bool inited = false;
-       static LanguageDefinition langDef;
-       if (!inited)
-       {
-               static const char* const keywords[] = {
-                       "AppendStructuredBuffer", "asm", "asm_fragment", "BlendState", "bool", "break", "Buffer", "ByteAddressBuffer", "case", "cbuffer", "centroid", "class", "column_major", "compile", "compile_fragment",
-                       "CompileShader", "const", "continue", "ComputeShader", "ConsumeStructuredBuffer", "default", "DepthStencilState", "DepthStencilView", "discard", "do", "double", "DomainShader", "dword", "else",
-                       "export", "extern", "false", "float", "for", "fxgroup", "GeometryShader", "groupshared", "half", "Hullshader", "if", "in", "inline", "inout", "InputPatch", "int", "interface", "line", "lineadj",
-                       "linear", "LineStream", "matrix", "min16float", "min10float", "min16int", "min12int", "min16uint", "namespace", "nointerpolation", "noperspective", "NULL", "out", "OutputPatch", "packoffset",
-                       "pass", "pixelfragment", "PixelShader", "point", "PointStream", "precise", "RasterizerState", "RenderTargetView", "return", "register", "row_major", "RWBuffer", "RWByteAddressBuffer", "RWStructuredBuffer",
-                       "RWTexture1D", "RWTexture1DArray", "RWTexture2D", "RWTexture2DArray", "RWTexture3D", "sample", "sampler", "SamplerState", "SamplerComparisonState", "shared", "snorm", "stateblock", "stateblock_state",
-                       "static", "string", "struct", "switch", "StructuredBuffer", "tbuffer", "technique", "technique10", "technique11", "texture", "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray", "Texture2DMS",
-                       "Texture2DMSArray", "Texture3D", "TextureCube", "TextureCubeArray", "true", "typedef", "triangle", "triangleadj", "TriangleStream", "uint", "uniform", "unorm", "unsigned", "vector", "vertexfragment",
-                       "VertexShader", "void", "volatile", "while",
-                       "bool1","bool2","bool3","bool4","double1","double2","double3","double4", "float1", "float2", "float3", "float4", "int1", "int2", "int3", "int4", "in", "out", "inout",
-                       "uint1", "uint2", "uint3", "uint4", "dword1", "dword2", "dword3", "dword4", "half1", "half2", "half3", "half4",
-                       "float1x1","float2x1","float3x1","float4x1","float1x2","float2x2","float3x2","float4x2",
-                       "float1x3","float2x3","float3x3","float4x3","float1x4","float2x4","float3x4","float4x4",
-                       "half1x1","half2x1","half3x1","half4x1","half1x2","half2x2","half3x2","half4x2",
-                       "half1x3","half2x3","half3x3","half4x3","half1x4","half2x4","half3x4","half4x4",
-               };
-               for (auto& k : keywords)
-                       langDef.mKeywords.insert(k);
-
-               static const char* const identifiers[] = {
-                       "abort", "abs", "acos", "all", "AllMemoryBarrier", "AllMemoryBarrierWithGroupSync", "any", "asdouble", "asfloat", "asin", "asint", "asint", "asuint",
-                       "asuint", "atan", "atan2", "ceil", "CheckAccessFullyMapped", "clamp", "clip", "cos", "cosh", "countbits", "cross", "D3DCOLORtoUBYTE4", "ddx",
-                       "ddx_coarse", "ddx_fine", "ddy", "ddy_coarse", "ddy_fine", "degrees", "determinant", "DeviceMemoryBarrier", "DeviceMemoryBarrierWithGroupSync",
-                       "distance", "dot", "dst", "errorf", "EvaluateAttributeAtCentroid", "EvaluateAttributeAtSample", "EvaluateAttributeSnapped", "exp", "exp2",
-                       "f16tof32", "f32tof16", "faceforward", "firstbithigh", "firstbitlow", "floor", "fma", "fmod", "frac", "frexp", "fwidth", "GetRenderTargetSampleCount",
-                       "GetRenderTargetSamplePosition", "GroupMemoryBarrier", "GroupMemoryBarrierWithGroupSync", "InterlockedAdd", "InterlockedAnd", "InterlockedCompareExchange",
-                       "InterlockedCompareStore", "InterlockedExchange", "InterlockedMax", "InterlockedMin", "InterlockedOr", "InterlockedXor", "isfinite", "isinf", "isnan",
-                       "ldexp", "length", "lerp", "lit", "log", "log10", "log2", "mad", "max", "min", "modf", "msad4", "mul", "noise", "normalize", "pow", "printf",
-                       "Process2DQuadTessFactorsAvg", "Process2DQuadTessFactorsMax", "Process2DQuadTessFactorsMin", "ProcessIsolineTessFactors", "ProcessQuadTessFactorsAvg",
-                       "ProcessQuadTessFactorsMax", "ProcessQuadTessFactorsMin", "ProcessTriTessFactorsAvg", "ProcessTriTessFactorsMax", "ProcessTriTessFactorsMin",
-                       "radians", "rcp", "reflect", "refract", "reversebits", "round", "rsqrt", "saturate", "sign", "sin", "sincos", "sinh", "smoothstep", "sqrt", "step",
-                       "tan", "tanh", "tex1D", "tex1D", "tex1Dbias", "tex1Dgrad", "tex1Dlod", "tex1Dproj", "tex2D", "tex2D", "tex2Dbias", "tex2Dgrad", "tex2Dlod", "tex2Dproj",
-                       "tex3D", "tex3D", "tex3Dbias", "tex3Dgrad", "tex3Dlod", "tex3Dproj", "texCUBE", "texCUBE", "texCUBEbias", "texCUBEgrad", "texCUBElod", "texCUBEproj", "transpose", "trunc"
-               };
-               for (auto& k : identifiers)
-               {
-                       Identifier id;
-                       id.mDeclaration = "Built-in function";
-                       langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
-               }
-
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
-
-               langDef.mCommentStart = "/*";
-               langDef.mCommentEnd = "*/";
-               langDef.mSingleLineComment = "//";
-
-               langDef.mCaseSensitive = true;
-               langDef.mAutoIndentation = true;
-
-               langDef.mName = "HLSL";
-
-               inited = true;
-       }
-       return langDef;
-}
-
-const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL()
-{
-       static bool inited = false;
-       static LanguageDefinition langDef;
-       if (!inited)
-       {
-               static const char* const keywords[] = {
-                       "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short",
-                       "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary",
-                       "_Noreturn", "_Static_assert", "_Thread_local"
-               };
-               for (auto& k : keywords)
-                       langDef.mKeywords.insert(k);
-
-               static const char* const identifiers[] = {
-                       "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
-                       "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper"
-               };
-               for (auto& k : identifiers)
-               {
-                       Identifier id;
-                       id.mDeclaration = "Built-in function";
-                       langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
-               }
-
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
-
-               langDef.mCommentStart = "/*";
-               langDef.mCommentEnd = "*/";
-               langDef.mSingleLineComment = "//";
-
-               langDef.mCaseSensitive = true;
-               langDef.mAutoIndentation = true;
-
-               langDef.mName = "GLSL";
-
-               inited = true;
-       }
-       return langDef;
-}
-
-const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::C()
-{
-       static bool inited = false;
-       static LanguageDefinition langDef;
-       if (!inited)
-       {
-               static const char* const keywords[] = {
-                       "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short",
-                       "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary",
-                       "_Noreturn", "_Static_assert", "_Thread_local"
-               };
-               for (auto& k : keywords)
-                       langDef.mKeywords.insert(k);
-
-               static const char* const identifiers[] = {
-                       "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph",
-                       "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper"
-               };
-               for (auto& k : identifiers)
-               {
-                       Identifier id;
-                       id.mDeclaration = "Built-in function";
-                       langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
-               }
-
-               langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex) -> bool
-               {
-                       paletteIndex = PaletteIndex::Max;
-
-                       while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin))
-                               in_begin++;
-
-                       if (in_begin == in_end)
-                       {
-                               out_begin = in_end;
-                               out_end = in_end;
-                               paletteIndex = PaletteIndex::Default;
-                       }
-                       else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end))
-                               paletteIndex = PaletteIndex::String;
-                       else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end))
-                               paletteIndex = PaletteIndex::CharLiteral;
-                       else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end))
-                               paletteIndex = PaletteIndex::Identifier;
-                       else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end))
-                               paletteIndex = PaletteIndex::Number;
-                       else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end))
-                               paletteIndex = PaletteIndex::Punctuation;
-
-                       return paletteIndex != PaletteIndex::Max;
-               };
-
-               langDef.mCommentStart = "/*";
-               langDef.mCommentEnd = "*/";
-               langDef.mSingleLineComment = "//";
-
-               langDef.mCaseSensitive = true;
-               langDef.mAutoIndentation = true;
-
-               langDef.mName = "C";
-
-               inited = true;
-       }
-       return langDef;
-}
-
-const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SQL()
-{
-       static bool inited = false;
-       static LanguageDefinition langDef;
-       if (!inited)
-       {
-               static const char* const keywords[] = {
-                       "ADD", "EXCEPT", "PERCENT", "ALL", "EXEC", "PLAN", "ALTER", "EXECUTE", "PRECISION", "AND", "EXISTS", "PRIMARY", "ANY", "EXIT", "PRINT", "AS", "FETCH", "PROC", "ASC", "FILE", "PROCEDURE",
-                       "AUTHORIZATION", "FILLFACTOR", "PUBLIC", "BACKUP", "FOR", "RAISERROR", "BEGIN", "FOREIGN", "READ", "BETWEEN", "FREETEXT", "READTEXT", "BREAK", "FREETEXTTABLE", "RECONFIGURE",
-                       "BROWSE", "FROM", "REFERENCES", "BULK", "FULL", "REPLICATION", "BY", "FUNCTION", "RESTORE", "CASCADE", "GOTO", "RESTRICT", "CASE", "GRANT", "RETURN", "CHECK", "GROUP", "REVOKE",
-                       "CHECKPOINT", "HAVING", "RIGHT", "CLOSE", "HOLDLOCK", "ROLLBACK", "CLUSTERED", "IDENTITY", "ROWCOUNT", "COALESCE", "IDENTITY_INSERT", "ROWGUIDCOL", "COLLATE", "IDENTITYCOL", "RULE",
-                       "COLUMN", "IF", "SAVE", "COMMIT", "IN", "SCHEMA", "COMPUTE", "INDEX", "SELECT", "CONSTRAINT", "INNER", "SESSION_USER", "CONTAINS", "INSERT", "SET", "CONTAINSTABLE", "INTERSECT", "SETUSER",
-                       "CONTINUE", "INTO", "SHUTDOWN", "CONVERT", "IS", "SOME", "CREATE", "JOIN", "STATISTICS", "CROSS", "KEY", "SYSTEM_USER", "CURRENT", "KILL", "TABLE", "CURRENT_DATE", "LEFT", "TEXTSIZE",
-                       "CURRENT_TIME", "LIKE", "THEN", "CURRENT_TIMESTAMP", "LINENO", "TO", "CURRENT_USER", "LOAD", "TOP", "CURSOR", "NATIONAL", "TRAN", "DATABASE", "NOCHECK", "TRANSACTION",
-                       "DBCC", "NONCLUSTERED", "TRIGGER", "DEALLOCATE", "NOT", "TRUNCATE", "DECLARE", "NULL", "TSEQUAL", "DEFAULT", "NULLIF", "UNION", "DELETE", "OF", "UNIQUE", "DENY", "OFF", "UPDATE",
-                       "DESC", "OFFSETS", "UPDATETEXT", "DISK", "ON", "USE", "DISTINCT", "OPEN", "USER", "DISTRIBUTED", "OPENDATASOURCE", "VALUES", "DOUBLE", "OPENQUERY", "VARYING","DROP", "OPENROWSET", "VIEW",
-                       "DUMMY", "OPENXML", "WAITFOR", "DUMP", "OPTION", "WHEN", "ELSE", "OR", "WHERE", "END", "ORDER", "WHILE", "ERRLVL", "OUTER", "WITH", "ESCAPE", "OVER", "WRITETEXT"
-               };
-
-               for (auto& k : keywords)
-                       langDef.mKeywords.insert(k);
-
-               static const char* const identifiers[] = {
-                       "ABS",  "ACOS",  "ADD_MONTHS",  "ASCII",  "ASCIISTR",  "ASIN",  "ATAN",  "ATAN2",  "AVG",  "BFILENAME",  "BIN_TO_NUM",  "BITAND",  "CARDINALITY",  "CASE",  "CAST",  "CEIL",
-                       "CHARTOROWID",  "CHR",  "COALESCE",  "COMPOSE",  "CONCAT",  "CONVERT",  "CORR",  "COS",  "COSH",  "COUNT",  "COVAR_POP",  "COVAR_SAMP",  "CUME_DIST",  "CURRENT_DATE",
-                       "CURRENT_TIMESTAMP",  "DBTIMEZONE",  "DECODE",  "DECOMPOSE",  "DENSE_RANK",  "DUMP",  "EMPTY_BLOB",  "EMPTY_CLOB",  "EXP",  "EXTRACT",  "FIRST_VALUE",  "FLOOR",  "FROM_TZ",  "GREATEST",
-                       "GROUP_ID",  "HEXTORAW",  "INITCAP",  "INSTR",  "INSTR2",  "INSTR4",  "INSTRB",  "INSTRC",  "LAG",  "LAST_DAY",  "LAST_VALUE",  "LEAD",  "LEAST",  "LENGTH",  "LENGTH2",  "LENGTH4",
-                       "LENGTHB",  "LENGTHC",  "LISTAGG",  "LN",  "LNNVL",  "LOCALTIMESTAMP",  "LOG",  "LOWER",  "LPAD",  "LTRIM",  "MAX",  "MEDIAN",  "MIN",  "MOD",  "MONTHS_BETWEEN",  "NANVL",  "NCHR",
-                       "NEW_TIME",  "NEXT_DAY",  "NTH_VALUE",  "NULLIF",  "NUMTODSINTERVAL",  "NUMTOYMINTERVAL",  "NVL",  "NVL2",  "POWER",  "RANK",  "RAWTOHEX",  "REGEXP_COUNT",  "REGEXP_INSTR",
-                       "REGEXP_REPLACE",  "REGEXP_SUBSTR",  "REMAINDER",  "REPLACE",  "ROUND",  "ROWNUM",  "RPAD",  "RTRIM",  "SESSIONTIMEZONE",  "SIGN",  "SIN",  "SINH",
-                       "SOUNDEX",  "SQRT",  "STDDEV",  "SUBSTR",  "SUM",  "SYS_CONTEXT",  "SYSDATE",  "SYSTIMESTAMP",  "TAN",  "TANH",  "TO_CHAR",  "TO_CLOB",  "TO_DATE",  "TO_DSINTERVAL",  "TO_LOB",
-                       "TO_MULTI_BYTE",  "TO_NCLOB",  "TO_NUMBER",  "TO_SINGLE_BYTE",  "TO_TIMESTAMP",  "TO_TIMESTAMP_TZ",  "TO_YMINTERVAL",  "TRANSLATE",  "TRIM",  "TRUNC", "TZ_OFFSET",  "UID",  "UPPER",
-                       "USER",  "USERENV",  "VAR_POP",  "VAR_SAMP",  "VARIANCE",  "VSIZE "
-               };
-               for (auto& k : identifiers)
-               {
-                       Identifier id;
-                       id.mDeclaration = "Built-in function";
-                       langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
-               }
-
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
-
-               langDef.mCommentStart = "/*";
-               langDef.mCommentEnd = "*/";
-               langDef.mSingleLineComment = "//";
-
-               langDef.mCaseSensitive = false;
-               langDef.mAutoIndentation = false;
-
-               langDef.mName = "SQL";
-
-               inited = true;
-       }
-       return langDef;
-}
-
-const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::AngelScript()
-{
-       static bool inited = false;
-       static LanguageDefinition langDef;
-       if (!inited)
-       {
-               static const char* const keywords[] = {
-                       "and", "abstract", "auto", "bool", "break", "case", "cast", "class", "const", "continue", "default", "do", "double", "else", "enum", "false", "final", "float", "for",
-                       "from", "funcdef", "function", "get", "if", "import", "in", "inout", "int", "interface", "int8", "int16", "int32", "int64", "is", "mixin", "namespace", "not",
-                       "null", "or", "out", "override", "private", "protected", "return", "set", "shared", "super", "switch", "this ", "true", "typedef", "uint", "uint8", "uint16", "uint32",
-                       "uint64", "void", "while", "xor"
-               };
-
-               for (auto& k : keywords)
-                       langDef.mKeywords.insert(k);
-
-               static const char* const identifiers[] = {
-                       "cos", "sin", "tab", "acos", "asin", "atan", "atan2", "cosh", "sinh", "tanh", "log", "log10", "pow", "sqrt", "abs", "ceil", "floor", "fraction", "closeTo", "fpFromIEEE", "fpToIEEE",
-                       "complex", "opEquals", "opAddAssign", "opSubAssign", "opMulAssign", "opDivAssign", "opAdd", "opSub", "opMul", "opDiv"
-               };
-               for (auto& k : identifiers)
-               {
-                       Identifier id;
-                       id.mDeclaration = "Built-in function";
-                       langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
-               }
-
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::String));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
-
-               langDef.mCommentStart = "/*";
-               langDef.mCommentEnd = "*/";
-               langDef.mSingleLineComment = "//";
-
-               langDef.mCaseSensitive = true;
-               langDef.mAutoIndentation = true;
-
-               langDef.mName = "AngelScript";
-
-               inited = true;
-       }
-       return langDef;
-}
-
-const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lua()
-{
-       static bool inited = false;
-       static LanguageDefinition langDef;
-       if (!inited)
-       {
-               static const char* const keywords[] = {
-                       "and", "break", "do", "", "else", "elseif", "end", "false", "for", "function", "if", "in", "", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while"
-               };
-
-               for (auto& k : keywords)
-                       langDef.mKeywords.insert(k);
-
-               static const char* const identifiers[] = {
-                       "assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "loadfile", "load", "loadstring",  "next",  "pairs",  "pcall",  "print",  "rawequal",  "rawlen",  "rawget",  "rawset",
-                       "select",  "setmetatable",  "tonumber",  "tostring",  "type",  "xpcall",  "_G",  "_VERSION","arshift", "band", "bnot", "bor", "bxor", "btest", "extract", "lrotate", "lshift", "replace",
-                       "rrotate", "rshift", "create", "resume", "running", "status", "wrap", "yield", "isyieldable", "debug","getuservalue", "gethook", "getinfo", "getlocal", "getregistry", "getmetatable",
-                       "getupvalue", "upvaluejoin", "upvalueid", "setuservalue", "sethook", "setlocal", "setmetatable", "setupvalue", "traceback", "close", "flush", "input", "lines", "open", "output", "popen",
-                       "read", "tmpfile", "type", "write", "close", "flush", "lines", "read", "seek", "setvbuf", "write", "__gc", "__tostring", "abs", "acos", "asin", "atan", "ceil", "cos", "deg", "exp", "tointeger",
-                       "floor", "fmod", "ult", "log", "max", "min", "modf", "rad", "random", "randomseed", "sin", "sqrt", "string", "tan", "type", "atan2", "cosh", "sinh", "tanh",
-                       "pow", "frexp", "ldexp", "log10", "pi", "huge", "maxinteger", "mininteger", "loadlib", "searchpath", "seeall", "preload", "cpath", "path", "searchers", "loaded", "module", "require", "clock",
-                       "date", "difftime", "execute", "exit", "getenv", "remove", "rename", "setlocale", "time", "tmpname", "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", "lower", "match", "rep",
-                       "reverse", "sub", "upper", "pack", "packsize", "unpack", "concat", "maxn", "insert", "pack", "unpack", "remove", "move", "sort", "offset", "codepoint", "char", "len", "codes", "charpattern",
-                       "coroutine", "table", "io", "os", "string", "utf8", "bit32", "math", "debug", "package"
-               };
-               for (auto& k : identifiers)
-               {
-                       Identifier id;
-                       id.mDeclaration = "Built-in function";
-                       langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
-               }
-
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
-               langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
-
-               langDef.mCommentStart = "--[[";
-               langDef.mCommentEnd = "]]";
-               langDef.mSingleLineComment = "--";
-
-               langDef.mCaseSensitive = true;
-               langDef.mAutoIndentation = false;
-
-               langDef.mName = "Lua";
-
-               inited = true;
-       }
-       return langDef;
-}
index bd52e13114a88eed46a4fa38de5883d2e59aa99e..e0e86cdde7202415a360e4efaa62b1978e592476 100644 (file)
@@ -174,12 +174,6 @@ public:
                }\r
 \r
                static const LanguageDefinition& CPlusPlus();\r
-               static const LanguageDefinition& HLSL();\r
-               static const LanguageDefinition& GLSL();\r
-               static const LanguageDefinition& C();\r
-               static const LanguageDefinition& SQL();\r
-               static const LanguageDefinition& AngelScript();\r
-               static const LanguageDefinition& Lua();\r
        };\r
 \r
        TextEditor();\r
index 182c46a556eaf83dcd34731ca3b4c2ff7934b12c..8d2b376fb4f106de4297a716276597d0320be6cd 100644 (file)
@@ -243,9 +243,9 @@ typedef unsigned long long  ImU64;  // 64-bit unsigned integer (post C++11)
 IM_MSVC_RUNTIME_CHECKS_OFF
 struct ImVec2
 {
-    float                                   x, y;
-    ImVec2()                                { x = y = 0.0f; }
-    ImVec2(float _x, float _y)              { x = _x; y = _y; }
+    float                                   x = 0.f, y = 0.f;
+    constexpr ImVec2()                      = default;
+    constexpr ImVec2(float _x, float _y)    : x(_x), y(_y) {}
     float  operator[] (size_t idx) const    { IM_ASSERT(idx <= 1); return (&x)[idx]; }    // We very rarely use this [] operator, the assert overhead is fine.
     float& operator[] (size_t idx)          { IM_ASSERT(idx <= 1); return (&x)[idx]; }    // We very rarely use this [] operator, the assert overhead is fine.
 #ifdef IM_VEC2_CLASS_EXTRA
diff --git a/source/logview.cpp b/source/logview.cpp
new file mode 100644 (file)
index 0000000..267cecb
--- /dev/null
@@ -0,0 +1,82 @@
+#include "logview.h"
+
+LogView::LogView()
+{
+    Clear();
+}
+
+void LogView::Clear()
+{
+    Buf.clear();
+    LineOffsets.clear();
+    LineOffsets.push_back(0);
+    updated = false;
+}
+
+void LogView::AddLog(const std::string& str)
+{
+    AddLog(str.c_str());
+}
+
+void LogView::AddLog(const char* fmt, ...)
+{
+    int old_size = Buf.size();
+    va_list args;
+    va_start(args, fmt);
+    Buf.appendfv(fmt, args);
+    Buf.appendfv("\n", args);
+    va_end(args);
+    for (int new_size = Buf.size(); old_size < new_size; old_size++)
+        if (Buf[old_size] == '\n')
+            LineOffsets.push_back(old_size + 1);
+    updated = true;
+}
+
+void LogView::Draw(const char* title, bool* p_open, ImGuiWindowFlags flags)
+{
+    if (!ImGui::Begin(title, p_open, flags))
+    {
+        ImGui::End();
+        return;
+    }
+
+    ImGui::Text("Log ");
+    ImGui::SameLine();
+    if (ImGui::Button("Clear"))
+        Clear();
+    ImGui::SameLine();
+    if (ImGui::Button("Copy"))
+        ImGui::LogToClipboard();
+    ImGui::Separator();
+    ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
+
+
+    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
+    const char* buf = Buf.begin();
+    const char* buf_end = Buf.end();
+
+    ImGuiListClipper clipper;
+    clipper.Begin(LineOffsets.Size);
+
+    while (clipper.Step())
+    {
+        for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
+        {
+            const char* line_start = buf + LineOffsets[line_no];
+            const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
+            ImGui::TextUnformatted(line_start, line_end);
+        }
+    }
+    clipper.End();
+
+    ImGui::PopStyleVar();
+
+    if (updated) {
+        ImGui::SetScrollHereY();
+        updated = false;
+    }
+
+    ImGui::EndChild();
+    ImGui::End();
+}
+
index 4ab2c94bb8552b9fb525325336e0acb367d32307..3c6acf1c315ebb7aaaea13ee272ef9858f398a0a 100644 (file)
@@ -3,87 +3,23 @@
 
 #include <string>
 
+#include "imgui.h"
+
 // Adapted from ExampleAppLog from imgui_demo.cpp
 class LogView
 {
 public:
-    LogView()
-    {
-        Clear();
-    }
-
-    void Clear()
-    {
-        Buf.clear();
-        LineOffsets.clear();
-        LineOffsets.push_back(0);
-    }
-
-    void AddLog(const std::string& str)
-    {
-        AddLog(str.c_str());
-    }
-
-    void AddLog(const char* fmt, ...) IM_FMTARGS(2)
-    {
-        int old_size = Buf.size();
-        va_list args;
-        va_start(args, fmt);
-        Buf.appendfv(fmt, args);
-        Buf.appendfv("\n", args);
-        va_end(args);
-        for (int new_size = Buf.size(); old_size < new_size; old_size++)
-            if (Buf[old_size] == '\n')
-                LineOffsets.push_back(old_size + 1);
-    }
-
-    void Draw(const char* title, bool* p_open = NULL, ImGuiWindowFlags flags = 0)
-    {
-        if (!ImGui::Begin(title, p_open, flags))
-        {
-            ImGui::End();
-            return;
-        }
-
-        ImGui::Text("Log ");
-        ImGui::SameLine();
-        if (ImGui::Button("Clear"))
-            Clear();
-        ImGui::SameLine();
-        if (ImGui::Button("Copy"))
-            ImGui::LogToClipboard();
-        ImGui::Separator();
-        ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
-
-
-        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
-        const char* buf = Buf.begin();
-        const char* buf_end = Buf.end();
-        ImGuiListClipper clipper;
-        clipper.Begin(LineOffsets.Size);
-        while (clipper.Step())
-        {
-            for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
-            {
-                const char* line_start = buf + LineOffsets[line_no];
-                const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
-                ImGui::TextUnformatted(line_start, line_end);
-            }
-        }
-        clipper.End();
-
-        ImGui::PopStyleVar();
-
-        if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
-            ImGui::SetScrollHereY(1.0f);
+    LogView();
 
-        ImGui::EndChild();
-        ImGui::End();
-    }
+    void Clear();
+    void AddLog(const std::string& str);
+    void AddLog(const char* fmt, ...) IM_FMTARGS(2);
+    void Draw(const char* title, bool* p_open = NULL, ImGuiWindowFlags flags = 0);
 
 private:
     ImGuiTextBuffer Buf;
     ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
+    bool updated;
 };
 
 #endif // LOGVIEW_H
index 20c8ea1e60b3ebccb136643837ecb98887ef286b..5eac3389106b68c689ead8437208de0023bf98e5 100644 (file)
 extern ImFont *fontSans;
 extern ImFont *fontMono;
 
-extern bool guiInitialize();
-extern void guiHandleEvents(bool& done);
-extern void guiShutdown();
-extern void guiRender(void (*func)());
+bool guiInitialize();
+bool guiHandleEvents();
+void guiShutdown();
+void guiRender(void (*func)());
 
-extern void fileRenderMenu();
-extern void fileRenderDialog();
-extern void fileInit();
+void fileRenderMenu();
+void fileRenderDialog();
+void fileInit();
 
-extern void codeEditorInit();
-extern void codeRenderMenu();
-extern void codeRenderToolbar();
-extern void codeRenderWidgets();
+void codeEditorInit();
+void codeRenderMenu();
+void codeRenderToolbar();
+void codeRenderWidgets();
 
-extern void deviceRenderDraw();
-extern void deviceRenderMenu();
-extern void deviceRenderToolbar();
-extern void deviceRenderWidgets();
-
-// Globals that live here
-bool done = false;
+void deviceRenderDraw();
+void deviceRenderMenu();
+void deviceRenderToolbar();
+void deviceRenderWidgets();
 
 static LogView logView;
 
@@ -54,6 +51,8 @@ void log(const std::string& str)
     logView.AddLog(str);
 }
 
+static void renderWindow();
+
 int main(int, char **)
 {
     if (!guiInitialize())
@@ -62,62 +61,72 @@ int main(int, char **)
     codeEditorInit();
     fileInit();
 
-    while (!done) {
-        auto endTime = std::chrono::steady_clock::now() +
-            std::chrono::milliseconds(static_cast<unsigned int>(std::floor(1000. / 60.)));
-
-        guiHandleEvents(done);
-
-        // Start the new window frame and render the menu bar.
-        ImGui_ImplOpenGL2_NewFrame();
-        ImGui_ImplSDL2_NewFrame();
-        ImGui::NewFrame();
+    while (1) {
+        constexpr std::chrono::duration<double> fpsDelay (1. / 60.);
+        const auto endTime = std::chrono::steady_clock::now() + fpsDelay;
 
-        if (ImGui::BeginMainMenuBar()) {
-            fileRenderMenu();
-            deviceRenderMenu();
-            codeRenderMenu();
-            ImGui::EndMainMenuBar();
+        const bool isDone = guiHandleEvents();
+        if (!isDone) {
+            renderWindow();
+            std::this_thread::sleep_until(endTime);
+        } else {
+            break;
         }
+    }
 
-        // Begin the main view which the controls will be drawn onto.
-        ImGui::SetNextWindowPos({0, 22});
-        ImGui::SetNextWindowSize({WINDOW_WIDTH, WINDOW_HEIGHT - 22 - 200});
-        ImGui::Begin("main", nullptr,
-                     ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration |
-                         ImGuiWindowFlags_NoBringToFrontOnFocus);
-
-        // Render main controls (order is important).
-        {
-            ImGui::PushFont(fontSans);
-            codeRenderToolbar();
-            deviceRenderToolbar();
-            fileRenderDialog();
-            deviceRenderWidgets();
-            ImGui::PopFont();
-
-            ImGui::PushFont(fontMono);
-            codeRenderWidgets();
-            ImGui::SetNextWindowPos({0, WINDOW_HEIGHT - 200});
-            ImGui::SetNextWindowSize({WINDOW_WIDTH, 200});
-            logView.Draw("log", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
-            ImGui::PopFont();
-        }
+    guiShutdown();
+    return 0;
+}
 
-        ImGui::End();
+void renderWindow()
+{
+    // Start the new window frame and render the menu bar.
+    ImGui_ImplOpenGL2_NewFrame();
+    ImGui_ImplSDL2_NewFrame();
+    ImGui::NewFrame();
+
+    if (ImGui::BeginMainMenuBar()) {
+        fileRenderMenu();
+        deviceRenderMenu();
+        codeRenderMenu();
+        ImGui::EndMainMenuBar();
+    }
 
-        deviceRenderDraw();
+    // Begin the main view which the controls will be drawn onto.
+    constexpr float LOGVIEW_HEIGHT = 200;
+    constexpr ImVec2 WINDOW_POS (0, 22);
+    constexpr ImVec2 WINDOW_SIZE (WINDOW_WIDTH, WINDOW_HEIGHT - 22 - LOGVIEW_HEIGHT);
+    ImGui::SetNextWindowPos(WINDOW_POS);
+    ImGui::SetNextWindowSize(WINDOW_SIZE);
+    ImGui::Begin("main", nullptr,
+                 ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration |
+                     ImGuiWindowFlags_NoBringToFrontOnFocus);
+
+    // Render main controls (order is important).
+    {
+        ImGui::PushFont(fontSans);
+        codeRenderToolbar();
+        deviceRenderToolbar();
+        fileRenderDialog();
+        deviceRenderWidgets();
+        ImGui::PopFont();
+
+        ImGui::PushFont(fontMono);
+        codeRenderWidgets();
+        ImGui::SetNextWindowPos({0, WINDOW_HEIGHT - LOGVIEW_HEIGHT});
+        ImGui::SetNextWindowSize({WINDOW_WIDTH, LOGVIEW_HEIGHT});
+        logView.Draw("log", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
+        ImGui::PopFont();
+    }
 
-        // Draw everything to the screen.
-        ImGui::Render();
-        guiRender([] {
-            ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
-        });
+    ImGui::End();
 
-        std::this_thread::sleep_until(endTime);
-    }
+    deviceRenderDraw();
 
-    guiShutdown();
-    return 0;
+    // Draw everything to the screen.
+    ImGui::Render();
+    guiRender([] {
+        ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
+    });
 }