refactor example + adding imgui and imtui submodules

This commit is contained in:
Georgi Gerganov
2020-11-29 17:45:20 +02:00
parent ba1742d733
commit 0bbb11fffb
19 changed files with 1839 additions and 305 deletions

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "examples/third-party/imtui"]
path = examples/third-party/imtui
url = https://github.com/ggerganov/imtui
[submodule "examples/third-party/imgui/imgui"]
path = examples/third-party/imgui/imgui
url = https://github.com/ocornut/imgui

View File

@@ -1,5 +1,11 @@
find_package(Threads REQUIRED)
# third-party
add_subdirectory(third-party)
# helper libraries
add_library(ggwave-common ${GGWAVE_LIBRARY_TYPE}
ggwave-common.cpp
)
@@ -48,6 +54,8 @@ if (GGWAVE_SUPPORT_SDL2)
)
endif()
# examples
if (GGWAVE_SUPPORT_SDL2)
if (EMSCRIPTEN)
# emscripten sdl2 examples
@@ -57,5 +65,6 @@ if (GGWAVE_SUPPORT_SDL2)
# non-emscripten sdl2 examples
add_subdirectory(ggwave-cli)
add_subdirectory(ggwave-gui)
endif()
endif()

View File

@@ -1,94 +1,15 @@
/*! \file main.cpp
* \brief Send/Receive data through sound in the terminal
* \author Georgi Gerganov
*/
#include "ggwave/ggwave.h"
#include "ggwave-common.h"
#include "ggwave-common-sdl2.h"
#include <SDL.h>
#include <SDL_audio.h>
#include <cstdio>
#include <string>
#include <chrono>
#include <mutex>
#include <thread>
#include <iostream>
static char *g_defaultCaptureDeviceName = nullptr;
static int g_captureId = -1;
static int g_playbackId = -1;
static bool g_isInitialized = false;
static SDL_AudioDeviceID g_devIdIn = 0;
static SDL_AudioDeviceID g_devIdOut = 0;
static GGWave *g_ggWave = nullptr;
// main loop
void update() {
if (g_isInitialized == false) return;
SDL_Event e;
SDL_bool shouldTerminate = SDL_FALSE;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
shouldTerminate = SDL_TRUE;
}
}
static GGWave::CBQueueAudio cbQueueAudio = [&](const void * data, uint32_t nBytes) {
SDL_QueueAudio(g_devIdOut, data, nBytes);
};
static GGWave::CBDequeueAudio CBDequeueAudio = [&](void * data, uint32_t nMaxBytes) {
return SDL_DequeueAudio(g_devIdIn, data, nMaxBytes);
};
if (g_ggWave->getHasData() == false) {
SDL_PauseAudioDevice(g_devIdOut, SDL_FALSE);
static auto tLastNoData = std::chrono::high_resolution_clock::now();
auto tNow = std::chrono::high_resolution_clock::now();
if ((int) SDL_GetQueuedAudioSize(g_devIdOut) < g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesOut()) {
SDL_PauseAudioDevice(g_devIdIn, SDL_FALSE);
if (::getTime_ms(tLastNoData, tNow) > 500.0f) {
g_ggWave->receive(CBDequeueAudio);
if ((int) SDL_GetQueuedAudioSize(g_devIdIn) > 32*g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesIn()) {
SDL_ClearQueuedAudio(g_devIdIn);
}
} else {
SDL_ClearQueuedAudio(g_devIdIn);
}
} else {
tLastNoData = tNow;
//SDL_ClearQueuedAudio(g_devIdIn);
//SDL_Delay(10);
}
} else {
SDL_PauseAudioDevice(g_devIdOut, SDL_TRUE);
SDL_PauseAudioDevice(g_devIdIn, SDL_TRUE);
g_ggWave->send(cbQueueAudio);
}
if (shouldTerminate) {
SDL_PauseAudioDevice(g_devIdIn, 1);
SDL_CloseAudioDevice(g_devIdIn);
SDL_PauseAudioDevice(g_devIdOut, 1);
SDL_CloseAudioDevice(g_devIdOut);
SDL_CloseAudio();
SDL_Quit();
}
}
int main(int argc, char** argv) {
printf("Usage: %s [-cN] [-pN] [-tN]\n", argv[0]);
printf(" -cN - select capture device N\n");
@@ -100,61 +21,58 @@ int main(int argc, char** argv) {
printf(" -t3 : Ultrasonic\n");
printf("\n");
g_defaultCaptureDeviceName = nullptr;
auto argm = parseCmdArguments(argc, argv);
g_captureId = argm["c"].empty() ? 0 : std::stoi(argm["c"]);
g_playbackId = argm["p"].empty() ? 0 : std::stoi(argm["p"]);
int captureId = argm["c"].empty() ? 0 : std::stoi(argm["c"]);
int playbackId = argm["p"].empty() ? 0 : std::stoi(argm["p"]);
int txProtocol = argm["t"].empty() ? 1 : std::stoi(argm["t"]);
initSDL2ForGGWave(
g_isInitialized,
g_playbackId,
g_devIdIn,
g_captureId,
g_devIdOut,
g_ggWave);
if (GGWave_init(playbackId, captureId) == false) {
fprintf(stderr, "Failed to initialize GGWave\n");
return -1;
}
g_ggWave->setTxMode(GGWave::TxMode::VariableLength);
auto ggWave = GGWave_instance();
ggWave->setTxMode(GGWave::TxMode::VariableLength);
printf("Selecting Tx protocol %d\n", txProtocol);
switch (txProtocol) {
case 0:
{
printf("Using 'Normal' Tx Protocol\n");
g_ggWave->setParameters(1, 40, 9, 3, 50);
ggWave->setParameters(1, 40, 9, 3, 50);
}
break;
case 1:
{
printf("Using 'Fast' Tx Protocol\n");
g_ggWave->setParameters(1, 40, 6, 3, 50);
ggWave->setParameters(1, 40, 6, 3, 50);
}
break;
case 2:
{
printf("Using 'Fastest' Tx Protocol\n");
g_ggWave->setParameters(1, 40, 3, 3, 50);
ggWave->setParameters(1, 40, 3, 3, 50);
}
break;
case 3:
{
printf("Using 'Ultrasonic' Tx Protocol\n");
g_ggWave->setParameters(1, 320, 9, 3, 50);
ggWave->setParameters(1, 320, 9, 3, 50);
}
break;
default:
{
printf("Using 'Fast' Tx Protocol\n");
g_ggWave->setParameters(1, 40, 6, 3, 50);
ggWave->setParameters(1, 40, 6, 3, 50);
}
};
printf("\n");
g_ggWave->init(0, "");
ggWave->init(0, "");
std::mutex mutex;
std::thread inputThread([&mutex]() {
std::thread inputThread([&]() {
std::string inputOld = "";
while (true) {
std::string input;
@@ -168,30 +86,23 @@ int main(int argc, char** argv) {
}
{
std::lock_guard<std::mutex> lock(mutex);
g_ggWave->init(input.size(), input.data());
ggWave->init(input.size(), input.data());
}
inputOld = input;
}
});
while (true) {
SDL_Delay(1);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
{
std::lock_guard<std::mutex> lock(mutex);
update();
GGWave_mainLoop();
}
}
inputThread.join();
delete g_ggWave;
SDL_PauseAudioDevice(g_devIdIn, 1);
SDL_CloseAudioDevice(g_devIdIn);
SDL_PauseAudioDevice(g_devIdOut, 1);
SDL_CloseAudioDevice(g_devIdOut);
SDL_CloseAudio();
SDL_Quit();
GGWave_deinit();
return 0;
}

View File

@@ -2,17 +2,112 @@
#include "ggwave/ggwave.h"
#include "ggwave-common.h"
#include <chrono>
#ifdef __EMSCRIPTEN__
#include "emscripten/emscripten.h"
#else
#define EMSCRIPTEN_KEEPALIVE
#endif
constexpr double kBaseSampleRate = 48000.0;
bool initSDL2ForGGWave(
bool & isInitialized,
namespace {
std::string g_defaultCaptureDeviceName = "";
bool g_isInitialized = false;
SDL_AudioDeviceID g_devIdIn = 0;
SDL_AudioDeviceID g_devIdOut = 0;
GGWave *g_ggWave = nullptr;
}
// JS interface
extern "C" {
EMSCRIPTEN_KEEPALIVE
int setText(int textLength, const char * text) {
g_ggWave->init(textLength, text);
return 0;
}
EMSCRIPTEN_KEEPALIVE
int getText(char * text) {
std::copy(g_ggWave->getRxData().begin(), g_ggWave->getRxData().end(), text);
return 0;
}
EMSCRIPTEN_KEEPALIVE
int getSampleRate() { return g_ggWave->getSampleRateIn(); }
EMSCRIPTEN_KEEPALIVE
float getAverageRxTime_ms() { return g_ggWave->getAverageRxTime_ms(); }
EMSCRIPTEN_KEEPALIVE
int getFramesToRecord() { return g_ggWave->getFramesToRecord(); }
EMSCRIPTEN_KEEPALIVE
int getFramesLeftToRecord() { return g_ggWave->getFramesLeftToRecord(); }
EMSCRIPTEN_KEEPALIVE
int getFramesToAnalyze() { return g_ggWave->getFramesToAnalyze(); }
EMSCRIPTEN_KEEPALIVE
int getFramesLeftToAnalyze() { return g_ggWave->getFramesLeftToAnalyze(); }
EMSCRIPTEN_KEEPALIVE
int hasDeviceOutput() { return g_devIdOut; }
EMSCRIPTEN_KEEPALIVE
int hasDeviceCapture() { return (g_ggWave->getTotalBytesCaptured() > 0) ? g_devIdIn : 0; }
EMSCRIPTEN_KEEPALIVE
int doInit() {
return GGWave_init(-1, -1);
}
EMSCRIPTEN_KEEPALIVE
int setTxMode(int txMode) {
g_ggWave->setTxMode((GGWave::TxMode)(txMode));
g_ggWave->init(0, "");
return 0;
}
EMSCRIPTEN_KEEPALIVE
void setParameters(
int paramFreqDelta,
int paramFreqStart,
int paramFramesPerTx,
int paramBytesPerTx,
int /*paramECCBytesPerTx*/,
int paramVolume) {
if (g_ggWave == nullptr) return;
g_ggWave->setParameters(
paramFreqDelta,
paramFreqStart,
paramFramesPerTx,
paramBytesPerTx,
paramVolume);
g_ggWave->init(0, "");
}
}
void GGWave_setDefaultCaptureDeviceName(std::string name) {
g_defaultCaptureDeviceName = std::move(name);
}
bool GGWave_init(
const int playbackId,
SDL_AudioDeviceID & devIdIn,
const int captureId,
SDL_AudioDeviceID & devIdOut,
GGWave *& ggWave,
const char * defaultCaptureDeviceName) {
if (isInitialized) return 0;
const int captureId) {
if (g_isInitialized) {
return false;
}
printf("Initializing ...\n");
@@ -60,17 +155,17 @@ bool initSDL2ForGGWave(
if (playbackId >= 0) {
printf("Attempt to open playback device %d : '%s' ...\n", playbackId, SDL_GetAudioDeviceName(playbackId, SDL_FALSE));
devIdOut = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(playbackId, SDL_FALSE), SDL_FALSE, &playbackSpec, &obtainedSpecOut, 0);
g_devIdOut = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(playbackId, SDL_FALSE), SDL_FALSE, &playbackSpec, &obtainedSpecOut, 0);
} else {
printf("Attempt to open default playback device ...\n");
devIdOut = SDL_OpenAudioDevice(NULL, SDL_FALSE, &playbackSpec, &obtainedSpecOut, 0);
g_devIdOut = SDL_OpenAudioDevice(NULL, SDL_FALSE, &playbackSpec, &obtainedSpecOut, 0);
}
if (!devIdOut) {
if (!g_devIdOut) {
printf("Couldn't open an audio device for playback: %s!\n", SDL_GetError());
devIdOut = 0;
g_devIdOut = 0;
} else {
printf("Obtained spec for output device (SDL Id = %d):\n", devIdOut);
printf("Obtained spec for output device (SDL Id = %d):\n", g_devIdOut);
printf(" - Sample rate: %d (required: %d)\n", obtainedSpecOut.freq, playbackSpec.freq);
printf(" - Format: %d (required: %d)\n", obtainedSpecOut.format, playbackSpec.format);
printf(" - Channels: %d (required: %d)\n", obtainedSpecOut.channels, playbackSpec.channels);
@@ -108,16 +203,17 @@ bool initSDL2ForGGWave(
if (captureId >= 0) {
printf("Attempt to open capture device %d : '%s' ...\n", captureId, SDL_GetAudioDeviceName(captureId, SDL_FALSE));
devIdIn = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(captureId, SDL_TRUE), SDL_TRUE, &captureSpec, &obtainedSpecIn, 0);
g_devIdIn = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(captureId, SDL_TRUE), SDL_TRUE, &captureSpec, &obtainedSpecIn, 0);
} else {
printf("Attempt to open default capture device ...\n");
devIdIn = SDL_OpenAudioDevice(defaultCaptureDeviceName, SDL_TRUE, &captureSpec, &obtainedSpecIn, 0);
g_devIdIn = SDL_OpenAudioDevice(g_defaultCaptureDeviceName.empty() ? nullptr : g_defaultCaptureDeviceName.c_str(),
SDL_TRUE, &captureSpec, &obtainedSpecIn, 0);
}
if (!devIdIn) {
if (!g_devIdIn) {
printf("Couldn't open an audio device for capture: %s!\n", SDL_GetError());
devIdIn = 0;
g_devIdIn = 0;
} else {
printf("Obtained spec for input device (SDL Id = %d):\n", devIdIn);
printf("Obtained spec for input device (SDL Id = %d):\n", g_devIdIn);
printf(" - Sample rate: %d\n", obtainedSpecIn.freq);
printf(" - Format: %d (required: %d)\n", obtainedSpecIn.format, captureSpec.format);
printf(" - Channels: %d (required: %d)\n", obtainedSpecIn.channels, captureSpec.channels);
@@ -139,14 +235,79 @@ bool initSDL2ForGGWave(
break;
}
ggWave = new GGWave(
g_ggWave = new GGWave(
obtainedSpecIn.freq,
obtainedSpecOut.freq,
1024,
sampleSizeBytesIn,
sampleSizeBytesOut);
isInitialized = true;
return 0;
g_isInitialized = true;
return true;
}
GGWave * GGWave_instance() { return g_ggWave; }
bool GGWave_mainLoop() {
if (g_isInitialized == false) {
return false;
}
static GGWave::CBQueueAudio cbQueueAudio = [&](const void * data, uint32_t nBytes) {
SDL_QueueAudio(g_devIdOut, data, nBytes);
};
static GGWave::CBDequeueAudio CBDequeueAudio = [&](void * data, uint32_t nMaxBytes) {
return SDL_DequeueAudio(g_devIdIn, data, nMaxBytes);
};
if (g_ggWave->getHasData() == false) {
SDL_PauseAudioDevice(g_devIdOut, SDL_FALSE);
static auto tLastNoData = std::chrono::high_resolution_clock::now();
auto tNow = std::chrono::high_resolution_clock::now();
if ((int) SDL_GetQueuedAudioSize(g_devIdOut) < g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesOut()) {
SDL_PauseAudioDevice(g_devIdIn, SDL_FALSE);
if (::getTime_ms(tLastNoData, tNow) > 500.0f) {
g_ggWave->receive(CBDequeueAudio);
if ((int) SDL_GetQueuedAudioSize(g_devIdIn) > 32*g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesIn()) {
SDL_ClearQueuedAudio(g_devIdIn);
}
} else {
SDL_ClearQueuedAudio(g_devIdIn);
}
} else {
tLastNoData = tNow;
}
} else {
SDL_PauseAudioDevice(g_devIdOut, SDL_TRUE);
SDL_PauseAudioDevice(g_devIdIn, SDL_TRUE);
g_ggWave->send(cbQueueAudio);
}
return true;
}
bool GGWave_deinit() {
if (g_isInitialized == false) {
return false;
}
delete g_ggWave;
g_ggWave = nullptr;
SDL_PauseAudioDevice(g_devIdIn, 1);
SDL_CloseAudioDevice(g_devIdIn);
SDL_PauseAudioDevice(g_devIdOut, 1);
SDL_CloseAudioDevice(g_devIdOut);
SDL_CloseAudio();
SDL_Quit();
#ifdef __EMSCRIPTEN__
emscripten_cancel_main_loop();
#endif
return true;
}

View File

@@ -2,13 +2,17 @@
#include <SDL.h>
#include <string>
class GGWave;
bool initSDL2ForGGWave(
bool & isInitialized,
const int playbackId,
SDL_AudioDeviceID & devIdIn,
const int captureId,
SDL_AudioDeviceID & devIdOut,
GGWave *& ggWave,
const char * defaultCaptureDeviceName = nullptr);
// GGWave helpers
void GGWave_setDefaultCaptureDeviceName(std::string name);
bool GGWave_init(const int playbackId, const int captureId);
GGWave * GGWave_instance();
bool GGWave_mainLoop();
bool GGWave_deinit();
// ImGui helpers

View File

@@ -0,0 +1,14 @@
add_executable(ggwave-gui main.cpp)
target_include_directories(ggwave-gui PRIVATE
..
${SDL2_INCLUDE_DIRS}
)
target_link_libraries(ggwave-gui PRIVATE
ggwave
ggwave-common
ggwave-common-sdl2
imgui-sdl2
${CMAKE_THREAD_LIBS_INIT}
)

View File

@@ -0,0 +1,162 @@
#include "ggwave/ggwave.h"
#include "ggwave-common.h"
#include "ggwave-common-sdl2.h"
#include <imgui/imgui.h>
#include <imgui-extra/imgui_impl.h>
#include <SDL.h>
#include <SDL_audio.h>
#include <SDL_opengl.h>
#include <cstdio>
#include <string>
#include <mutex>
#include <thread>
#include <iostream>
static SDL_Window * g_window;
static void * g_gl_context;
int main(int argc, char** argv) {
printf("Usage: %s [-cN] [-pN] [-tN]\n", argv[0]);
printf(" -cN - select capture device N\n");
printf(" -pN - select playback device N\n");
printf(" -tN - transmission protocol:\n");
printf(" -t0 : Normal\n");
printf(" -t1 : Fast (default)\n");
printf(" -t2 : Fastest\n");
printf(" -t3 : Ultrasonic\n");
printf("\n");
auto argm = parseCmdArguments(argc, argv);
int captureId = argm["c"].empty() ? 0 : std::stoi(argm["c"]);
int playbackId = argm["p"].empty() ? 0 : std::stoi(argm["p"]);
int txProtocol = argm["t"].empty() ? 1 : std::stoi(argm["t"]);
if (GGWave_init(playbackId, captureId) == false) {
fprintf(stderr, "Failed to initialize GGWave\n");
return -1;
}
auto ggWave = GGWave_instance();
ggWave->setTxMode(GGWave::TxMode::VariableLength);
printf("Selecting Tx protocol %d\n", txProtocol);
switch (txProtocol) {
case 0:
{
printf("Using 'Normal' Tx Protocol\n");
ggWave->setParameters(1, 40, 9, 3, 50);
}
break;
case 1:
{
printf("Using 'Fast' Tx Protocol\n");
ggWave->setParameters(1, 40, 6, 3, 50);
}
break;
case 2:
{
printf("Using 'Fastest' Tx Protocol\n");
ggWave->setParameters(1, 40, 3, 3, 50);
}
break;
case 3:
{
printf("Using 'Ultrasonic' Tx Protocol\n");
ggWave->setParameters(1, 320, 9, 3, 50);
}
break;
default:
{
printf("Using 'Fast' Tx Protocol\n");
ggWave->setParameters(1, 40, 6, 3, 50);
}
};
printf("\n");
ggWave->init(0, "");
std::mutex mutex;
std::thread inputThread([&]() {
std::string inputOld = "";
while (true) {
std::string input;
std::cout << "Enter text: ";
getline(std::cin, input);
if (input.empty()) {
std::cout << "Re-sending ... " << std::endl;
input = inputOld;
} else {
std::cout << "Sending ... " << std::endl;
}
{
std::lock_guard<std::mutex> lock(mutex);
ggWave->init(input.size(), input.data());
}
inputOld = input;
}
});
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) {
fprintf(stderr, "Error: %s\n", SDL_GetError());
return -1;
}
int windowX = 400;
int windowY = 600;
const char * windowTitle = "ggwave-gui";
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
g_window = SDL_CreateWindow(windowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowX, windowY, window_flags);
g_gl_context = SDL_GL_CreateContext(g_window);
SDL_GL_MakeCurrent(g_window, g_gl_context);
SDL_GL_SetSwapInterval(0); // Enable vsync
ImGui_Init(g_window, g_gl_context);
ImGui_NewFrame(g_window);
ImGui::Render();
while (true) {
{
std::lock_guard<std::mutex> lock(mutex);
GGWave_mainLoop();
}
SDL_Event event;
while (SDL_PollEvent(&event))
{
ImGui_ProcessEvent(&event);
if (event.type == SDL_QUIT) {}
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(g_window)) {}
}
ImGui_NewFrame(g_window);
ImGui::ShowDemoWindow();
// Rendering
int display_w, display_h;
SDL_GetWindowSize(g_window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(0.0f, 0.0f, 0.0f, 0.4f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui::Render();
ImGui_RenderDrawData(ImGui::GetDrawData());
SDL_GL_SwapWindow(g_window);
}
inputThread.join();
GGWave_deinit();
return 0;
}

View File

@@ -1,179 +1,19 @@
/*! \file main.cpp
* \brief Send/Receive - WebAssembly port
* \author Georgi Gerganov
*/
#include "ggwave/ggwave.h"
#include "ggwave-common.h"
#include "ggwave-common-sdl2.h"
#include <SDL.h>
#include <SDL_audio.h>
#include <cstdio>
#include <string>
#include <chrono>
#include "build_timestamp.h"
#include "emscripten/emscripten.h"
static char *g_defaultCaptureDeviceName = nullptr;
static int g_captureId = -1;
static int g_playbackId = -1;
static bool g_isInitialized = false;
static SDL_AudioDeviceID g_devIdIn = 0;
static SDL_AudioDeviceID g_devIdOut = 0;
static GGWave *g_ggWave = nullptr;
// JS interface
extern "C" {
EMSCRIPTEN_KEEPALIVE
int setText(int textLength, const char * text) {
g_ggWave->init(textLength, text);
return 0;
}
EMSCRIPTEN_KEEPALIVE
int getText(char * text) {
std::copy(g_ggWave->getRxData().begin(), g_ggWave->getRxData().end(), text);
return 0;
}
EMSCRIPTEN_KEEPALIVE
int getSampleRate() { return g_ggWave->getSampleRateIn(); }
EMSCRIPTEN_KEEPALIVE
float getAverageRxTime_ms() { return g_ggWave->getAverageRxTime_ms(); }
EMSCRIPTEN_KEEPALIVE
int getFramesToRecord() { return g_ggWave->getFramesToRecord(); }
EMSCRIPTEN_KEEPALIVE
int getFramesLeftToRecord() { return g_ggWave->getFramesLeftToRecord(); }
EMSCRIPTEN_KEEPALIVE
int getFramesToAnalyze() { return g_ggWave->getFramesToAnalyze(); }
EMSCRIPTEN_KEEPALIVE
int getFramesLeftToAnalyze() { return g_ggWave->getFramesLeftToAnalyze(); }
EMSCRIPTEN_KEEPALIVE
int hasDeviceOutput() { return g_devIdOut; }
EMSCRIPTEN_KEEPALIVE
int hasDeviceCapture() { return (g_ggWave->getTotalBytesCaptured() > 0) ? g_devIdIn : 0; }
EMSCRIPTEN_KEEPALIVE
int doInit() {
return initSDL2ForGGWave(
g_isInitialized,
g_playbackId,
g_devIdIn,
g_captureId,
g_devIdOut,
g_ggWave,
g_defaultCaptureDeviceName);
}
EMSCRIPTEN_KEEPALIVE
int setTxMode(int txMode) {
g_ggWave->setTxMode((GGWave::TxMode)(txMode));
g_ggWave->init(0, "");
return 0;
}
EMSCRIPTEN_KEEPALIVE
void setParameters(
int paramFreqDelta,
int paramFreqStart,
int paramFramesPerTx,
int paramBytesPerTx,
int /*paramECCBytesPerTx*/,
int paramVolume) {
if (g_ggWave == nullptr) return;
g_ggWave->setParameters(
paramFreqDelta,
paramFreqStart,
paramFramesPerTx,
paramBytesPerTx,
paramVolume);
g_ggWave->init(0, "");
}
}
// main loop
void update() {
if (g_isInitialized == false) return;
SDL_Event e;
SDL_bool shouldTerminate = SDL_FALSE;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
shouldTerminate = SDL_TRUE;
}
}
static GGWave::CBQueueAudio cbQueueAudio = [&](const void * data, uint32_t nBytes) {
SDL_QueueAudio(g_devIdOut, data, nBytes);
};
static GGWave::CBDequeueAudio CBDequeueAudio = [&](void * data, uint32_t nMaxBytes) {
return SDL_DequeueAudio(g_devIdIn, data, nMaxBytes);
};
if (g_ggWave->getHasData() == false) {
SDL_PauseAudioDevice(g_devIdOut, SDL_FALSE);
static auto tLastNoData = std::chrono::high_resolution_clock::now();
auto tNow = std::chrono::high_resolution_clock::now();
if ((int) SDL_GetQueuedAudioSize(g_devIdOut) < g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesOut()) {
SDL_PauseAudioDevice(g_devIdIn, SDL_FALSE);
if (::getTime_ms(tLastNoData, tNow) > 500.0f) {
g_ggWave->receive(CBDequeueAudio);
if ((int) SDL_GetQueuedAudioSize(g_devIdIn) > 32*g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesIn()) {
SDL_ClearQueuedAudio(g_devIdIn);
}
} else {
SDL_ClearQueuedAudio(g_devIdIn);
}
} else {
tLastNoData = tNow;
//SDL_ClearQueuedAudio(g_devIdIn);
//SDL_Delay(10);
}
} else {
SDL_PauseAudioDevice(g_devIdOut, SDL_TRUE);
SDL_PauseAudioDevice(g_devIdIn, SDL_TRUE);
g_ggWave->send(cbQueueAudio);
}
if (shouldTerminate) {
SDL_PauseAudioDevice(g_devIdIn, 1);
SDL_CloseAudioDevice(g_devIdIn);
SDL_PauseAudioDevice(g_devIdOut, 1);
SDL_CloseAudioDevice(g_devIdOut);
SDL_CloseAudio();
SDL_Quit();
#ifdef __EMSCRIPTEN__
emscripten_cancel_main_loop();
#endif
}
GGWave_mainLoop();
}
int main(int , char** argv) {
printf("Build time: %s\n", BUILD_TIMESTAMP);
printf("Press the Init button to start\n");
g_defaultCaptureDeviceName = argv[1];
GGWave_setDefaultCaptureDeviceName(argv[1]);
emscripten_set_main_loop(update, 60, 1);

View File

@@ -1,8 +1,3 @@
/*! \file main.js
* \brief Text transfer over sound
* \author Georgi Gerganov
*/
function transmitText(sText) {
var r = new Uint8Array(256);
for (var i = 0; i < sText.length; ++i) {

4
examples/third-party/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,4 @@
if (NOT EMSCRIPTEN)
add_subdirectory(imtui)
add_subdirectory(imgui)
endif()

View File

@@ -0,0 +1,67 @@
if (GGWAVE_ALL_WARNINGS_3RD_PARTY)
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
else()
# todo : windows
endif()
endif()
if (APPLE)
set(ADDITIONAL_LIBRARIES "-framework Cocoa")
else (APPLE)
unset(ADDITIONAL_LIBRARIES)
endif (APPLE)
add_library(imgui SHARED
imgui/imgui.cpp
imgui/imgui_draw.cpp
imgui/imgui_demo.cpp
imgui/imgui_widgets.cpp
)
target_include_directories(imgui INTERFACE
.
)
target_include_directories(imgui PRIVATE
imgui
)
target_link_libraries(imgui PRIVATE
${ADDITIONAL_LIBRARIES}
)
if (GGWAVE_SUPPORT_SDL2)
find_package(OpenGL REQUIRED)
find_package(SDL2 REQUIRED)
string(STRIP "${SDL2_LIBRARIES}" SDL2_LIBRARIES)
add_library(imgui-sdl2 SHARED
imgui/examples/libs/gl3w/GL/gl3w.c
imgui-extra/imgui_impl.cpp
imgui-extra/imgui_impl_sdl.cpp
imgui-extra/imgui_impl_opengl3.cpp
)
target_include_directories(imgui-sdl2 PUBLIC
imgui/examples/libs/gl3w
${SDL2_INCLUDE_DIRS}
)
target_include_directories(imgui-sdl2 PRIVATE
imgui
imgui-extra
)
target_link_libraries(imgui-sdl2 PUBLIC
imgui
${OPENGL_LIBRARIES}
${SDL2_LIBRARIES}
)
target_link_libraries(imgui-sdl2 PRIVATE
${CMAKE_DL_LIBS}
${CMAKE_THREAD_LIBS_INIT}
${ADDITIONAL_LIBRARIES}
)
endif()

View File

@@ -0,0 +1,95 @@
#include "imgui-extra/imgui_impl.h"
#include "imgui-extra/imgui_impl_sdl.h"
#include "imgui-extra/imgui_impl_opengl3.h"
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
#include <GL/gl3w.h> // Initialize with gl3wInit()
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
#include <GL/glew.h> // Initialize with glewInit()
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Initialize with gladLoadGL()
#else
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif
#include <SDL.h>
ImGuiContext* ImGui_Init(SDL_Window* window, SDL_GLContext gl_context) {
// Decide GL+GLSL versions
#if __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);
static bool isInitialized = false;
if (!isInitialized) {
// Initialize OpenGL loader
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
bool err = gl3wInit() != 0;
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
bool err = glewInit() != GLEW_OK;
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
bool err = gladLoadGL() == 0;
#else
bool err = false; // If you use IMGUI_IMPL_OPENGL_LOADER_CUSTOM, your loader is likely to requires some form of initialization.
#endif
if (err) {
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
return nullptr;
}
isInitialized = true;
}
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
auto ctx = ImGui::CreateContext();
ImGui::SetCurrentContext(ctx);
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Platform/Renderer bindings
bool res = true;
res &= ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
#ifdef __EMSCRIPTEN__
res &= ImGui_ImplOpenGL3_Init("#version 300 es");
#else
res &= ImGui_ImplOpenGL3_Init(glsl_version);
#endif
return res ? ctx : nullptr;
}
void ImGui_Shutdown() { ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL2_Shutdown(); }
void ImGui_NewFrame(SDL_Window* window) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(window); ImGui::NewFrame(); }
bool ImGui_ProcessEvent(const SDL_Event* event) { return ImGui_ImplSDL2_ProcessEvent(event); }
void ImGui_RenderDrawData(ImDrawData* draw_data) { ImGui_ImplOpenGL3_RenderDrawData(draw_data); }
bool ImGui_CreateFontsTexture() { return ImGui_ImplOpenGL3_CreateFontsTexture(); }
void ImGui_DestroyFontsTexture() { ImGui_ImplOpenGL3_DestroyFontsTexture(); }
bool ImGui_CreateDeviceObjects() { return ImGui_ImplOpenGL3_CreateDeviceObjects(); }
void ImGui_DestroyDeviceObjects() { ImGui_ImplOpenGL3_DestroyDeviceObjects(); }
void ImGui_SaveState(int id) { ImGui_ImplSDL2_SaveState(id); ImGui_ImplOpenGL3_SaveState(id); }
void ImGui_LoadState(int id) { ImGui_ImplSDL2_LoadState(id); ImGui_ImplOpenGL3_LoadState(id); }

View File

@@ -0,0 +1,31 @@
/*! \file imgui_impl.h
* \brief Enter description here.
*/
#pragma once
#if (defined(__EMSCRIPTEN__))
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
#endif
#include "imgui/imgui.h"
struct SDL_Window;
typedef void * SDL_GLContext;
typedef union SDL_Event SDL_Event;
IMGUI_API ImGuiContext* ImGui_Init(SDL_Window* window, SDL_GLContext gl_context);
void IMGUI_API ImGui_Shutdown();
void IMGUI_API ImGui_NewFrame(SDL_Window* window);
bool IMGUI_API ImGui_ProcessEvent(const SDL_Event* event);
void IMGUI_API ImGui_RenderDrawData(ImDrawData* draw_data);
bool IMGUI_API ImGui_CreateFontsTexture();
void IMGUI_API ImGui_DestroyFontsTexture();
bool IMGUI_API ImGui_CreateDeviceObjects();
void IMGUI_API ImGui_DestroyDeviceObjects();
void IMGUI_API ImGui_SaveState(int id);
void IMGUI_API ImGui_LoadState(int id);

View File

@@ -0,0 +1,722 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2020-01-07: OpenGL: Added support for glbindings OpenGL loader.
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
// 2019-03-15: OpenGL: Added a dummy GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
//----------------------------------------
// OpenGL GLSL GLSL
// version version string
//----------------------------------------
// 2.0 110 "#version 110"
// 2.1 120 "#version 120"
// 3.0 130 "#version 130"
// 3.1 140 "#version 140"
// 3.2 150 "#version 150"
// 3.3 330 "#version 330 core"
// 4.0 400 "#version 400 core"
// 4.1 410 "#version 410 core"
// 4.2 420 "#version 410 core"
// 4.3 430 "#version 430 core"
// ES 2.0 100 "#version 100" = WebGL 1.0
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif
// Auto-enable GLES on matching platforms
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
#undef IMGUI_IMPL_OPENGL_LOADER_GL3W
#undef IMGUI_IMPL_OPENGL_LOADER_GLEW
#undef IMGUI_IMPL_OPENGL_LOADER_GLAD
#undef IMGUI_IMPL_OPENGL_LOADER_GLBINDING
#undef IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#elif defined(__EMSCRIPTEN__)
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
#undef IMGUI_IMPL_OPENGL_LOADER_GL3W
#undef IMGUI_IMPL_OPENGL_LOADER_GLEW
#undef IMGUI_IMPL_OPENGL_LOADER_GLAD
#undef IMGUI_IMPL_OPENGL_LOADER_GLBINDING
#undef IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif
#endif
// GL includes
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h>
#elif defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
#else
#include <GLES3/gl3.h> // Use GL ES 3
#endif
#else
// About Desktop OpenGL function loaders:
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING)
#include <glbinding/gl/gl.h> // Initialize with glbinding::initialize()
#include <glbinding/glbinding.h>
using namespace gl;
#else
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif
#endif
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) || !defined(GL_VERSION_3_2)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 0
#else
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 1
#endif
// OpenGL Data
static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries.
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
static GLuint g_FontTexture = 0;
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
#include <map>
struct State {
GLuint FontTexture = 0;
GLuint ShaderHandle = 0, VertHandle = 0, FragHandle = 0;
int AttribLocationTex = 0, AttribLocationProjMtx = 0;
int AttribLocationVtxPos = 0, AttribLocationVtxUV = 0, AttribLocationVtxColor = 0;
unsigned int VboHandle = 0, ElementsHandle = 0;
};
static std::map<int, State> g_state;
void ImGui_ImplOpenGL3_SaveState(int id) {
auto & dst = g_state[id];
dst.FontTexture = g_FontTexture;
dst.FontTexture = g_FontTexture;
dst.ShaderHandle = g_ShaderHandle;
dst.VertHandle = g_VertHandle;
dst.FragHandle = g_FragHandle;
dst.AttribLocationTex = g_AttribLocationTex;
dst.AttribLocationProjMtx = g_AttribLocationProjMtx;
dst.AttribLocationVtxPos = g_AttribLocationVtxPos;
dst.AttribLocationVtxUV = g_AttribLocationVtxUV;
dst.AttribLocationVtxColor = g_AttribLocationVtxColor;
dst.VboHandle = g_VboHandle;
dst.ElementsHandle = g_ElementsHandle;
}
void ImGui_ImplOpenGL3_LoadState(int id) {
const auto & src = g_state[id];
g_FontTexture = src.FontTexture;
g_ShaderHandle = src.ShaderHandle;
g_VertHandle = src.VertHandle;
g_FragHandle = src.FragHandle;
g_AttribLocationTex = src.AttribLocationTex;
g_AttribLocationProjMtx = src.AttribLocationProjMtx;
g_AttribLocationVtxPos = src.AttribLocationVtxPos;
g_AttribLocationVtxUV = src.AttribLocationVtxUV;
g_AttribLocationVtxColor = src.AttribLocationVtxColor;
g_VboHandle = src.VboHandle;
g_ElementsHandle = src.ElementsHandle;
}
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
// Query for GL version
#if !defined(IMGUI_IMPL_OPENGL_ES2)
GLint major, minor;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
g_GlVersion = major * 1000 + minor;
#else
g_GlVersion = 2000; // GLES 2
#endif
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 3200)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
#if defined(IMGUI_IMPL_OPENGL_ES2)
if (glsl_version == NULL)
glsl_version = "#version 100";
#elif defined(IMGUI_IMPL_OPENGL_ES3)
if (glsl_version == NULL)
glsl_version = "#version 300 es";
#else
if (glsl_version == NULL)
glsl_version = "#version 130";
#endif
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n");
// Dummy construct to make it easily visible in the IDE and debugger which GL loader has been selected.
// The code actually never uses the 'gl_loader' variable! It is only here so you can read it!
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash below.
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
const char* gl_loader = "Unknown";
IM_UNUSED(gl_loader);
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
gl_loader = "GL3W";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
gl_loader = "GLEW";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
gl_loader = "GLAD";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING)
gl_loader = "glbinding";
#else // IMGUI_IMPL_OPENGL_LOADER_CUSTOM
gl_loader = "Custom";
#endif
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
(void)vertex_array_object;
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
#ifdef GL_SAMPLER_BINDING
GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
#endif
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object);
#endif
#ifdef GL_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
bool clip_origin_lower_left = true;
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
if (last_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
#endif
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
#ifndef IMGUI_IMPL_OPENGL_ES2
glGenVertexArrays(1, &vertex_array_object);
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
if (clip_origin_lower_left)
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
else
glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 3200)
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
else
#endif
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
}
}
// Destroy the temporary VAO
#ifndef IMGUI_IMPL_OPENGL_ES2
glDeleteVertexArrays(1, &vertex_array_object);
#endif
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, last_sampler);
#endif
glActiveTexture(last_active_texture);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_UNPACK_ROW_LENGTH
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO& io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->TexID = 0;
g_FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
// Parse GLSL version string
int glsl_version = 130;
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision mediump float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = NULL;
const GLchar* fragment_shader = NULL;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
g_ShaderHandle = glCreateProgram();
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
CheckProgram(g_ShaderHandle, "shader program");
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array);
#endif
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; }
if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; }
if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); }
if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); }
if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; }
if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; }
if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
}

View File

@@ -0,0 +1,70 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// About Desktop OpenGL function loaders:
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
// About GLSL version:
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_SaveState(int id);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_LoadState(int id);
// Specific OpenGL versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
// Desktop OpenGL: attempt to detect default GL loader based on available header files.
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash in ImGui_ImplOpenGL3_Init().
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
#if defined(__has_include)
#if __has_include(<GL/glew.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
#elif __has_include(<glad/glad.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#elif __has_include(<GL/gl3w.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#elif __has_include(<glbinding/gl/gl.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING
#else
#error "Cannot detect OpenGL loader!"
#endif
#else
#define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W
#endif
#endif

View File

@@ -0,0 +1,410 @@
// dear imgui: Platform Binding for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// (Requires: SDL 2.0. Prefer SDL 2.0.4+ for full feature support.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2).
// 2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state).
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
// 2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.
// 2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.
// 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).
// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS.
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#include "imgui_impl_sdl.h"
// SDL
#include <SDL.h>
#include <SDL_syswm.h>
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4)
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
// Data
static SDL_Window* g_Window = NULL;
static Uint64 g_Time = 0;
static bool g_MousePressed[3] = { false, false, false };
static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
static char* g_ClipboardTextData = NULL;
static bool g_MouseCanUseGlobalState = true;
#include <map>
struct State {
SDL_Window* Window = NULL;
Uint64 Time = 0;
bool MousePressed[3] = { false, false, false };
SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT] = {};
char* ClipboardTextData = NULL;
bool MouseCanUseGlobalState = true;
};
static std::map<int, State> g_state;
void ImGui_ImplSDL2_SaveState(int id) {
auto & dst = g_state[id];
dst.Window = g_Window;
dst.Time = g_Time;
dst.MousePressed[0] = g_MousePressed[0];
dst.MousePressed[1] = g_MousePressed[1];
dst.MousePressed[2] = g_MousePressed[2];
for (int i = 0; i < ImGuiMouseCursor_COUNT; ++i) {
dst.MouseCursors[i] = g_MouseCursors[i];
}
dst.ClipboardTextData = g_ClipboardTextData;
dst.MouseCanUseGlobalState = g_MouseCanUseGlobalState;
}
void ImGui_ImplSDL2_LoadState(int id) {
const auto & src = g_state[id];
g_Window = src.Window;
g_Time = src.Time;
g_MousePressed[0] = src.MousePressed[0];
g_MousePressed[1] = src.MousePressed[1];
g_MousePressed[2] = src.MousePressed[2];
for (int i = 0; i < ImGuiMouseCursor_COUNT; ++i) {
g_MouseCursors[i] = src.MouseCursors[i];
}
g_ClipboardTextData = src.ClipboardTextData;
g_MouseCanUseGlobalState = src.MouseCanUseGlobalState;
}
static const char* ImGui_ImplSDL2_GetClipboardText(void*)
{
if (g_ClipboardTextData)
SDL_free(g_ClipboardTextData);
g_ClipboardTextData = SDL_GetClipboardText();
return g_ClipboardTextData;
}
static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
{
SDL_SetClipboardText(text);
}
// 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.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
{
ImGuiIO& io = ImGui::GetIO();
switch (event->type)
{
case SDL_MOUSEWHEEL:
{
if (event->wheel.x > 0) io.MouseWheelH += 1;
if (event->wheel.x < 0) io.MouseWheelH -= 1;
if (event->wheel.y > 0) io.MouseWheel += 1;
if (event->wheel.y < 0) io.MouseWheel -= 1;
return true;
}
case SDL_MOUSEBUTTONDOWN:
{
if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true;
if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true;
if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true;
return true;
}
case SDL_TEXTINPUT:
{
io.AddInputCharactersUTF8(event->text.text);
return true;
}
case SDL_KEYDOWN:
case SDL_KEYUP:
{
int key = event->key.keysym.scancode;
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
#ifdef _WIN32
io.KeySuper = false;
#else
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
#endif
return true;
}
}
return false;
}
static bool ImGui_ImplSDL2_Init(SDL_Window* window)
{
g_Window = window;
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_sdl";
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
io.KeyMap[ImGuiKey_KeyPadEnter] = SDL_SCANCODE_KP_ENTER;
io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
io.ClipboardUserData = NULL;
// Load mouse cursors
g_MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
g_MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
g_MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
g_MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
// Check and store if we are on Wayland
g_MouseCanUseGlobalState = strncmp(SDL_GetCurrentVideoDriver(), "wayland", 7) != 0;
#ifdef _WIN32
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(window, &wmInfo);
io.ImeWindowHandle = wmInfo.info.win.window;
#else
(void)window;
#endif
return true;
}
bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
{
(void)sdl_gl_context; // Viewport branch will need this.
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
{
#if !SDL_HAS_VULKAN
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
{
#if !defined(_WIN32)
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
{
return ImGui_ImplSDL2_Init(window);
}
void ImGui_ImplSDL2_Shutdown()
{
g_Window = NULL;
// Destroy last known clipboard data
if (g_ClipboardTextData)
SDL_free(g_ClipboardTextData);
g_ClipboardTextData = NULL;
// Destroy SDL mouse cursors
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
SDL_FreeCursor(g_MouseCursors[cursor_n]);
memset(g_MouseCursors, 0, sizeof(g_MouseCursors));
}
static void ImGui_ImplSDL2_UpdateMousePosAndButtons()
{
ImGuiIO& io = ImGui::GetIO();
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
SDL_WarpMouseInWindow(g_Window, (int)io.MousePos.x, (int)io.MousePos.y);
else
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
int mx, my;
Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my);
io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false;
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS)
SDL_Window* focused_window = SDL_GetKeyboardFocus();
if (g_Window == focused_window)
{
if (g_MouseCanUseGlobalState)
{
// SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?)
// The creation of a new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally.
// Won't use this workaround when on Wayland, as there is no global mouse position.
int wx, wy;
SDL_GetWindowPosition(focused_window, &wx, &wy);
SDL_GetGlobalMouseState(&mx, &my);
mx -= wx;
my -= wy;
}
io.MousePos = ImVec2((float)mx, (float)my);
}
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger the OS window resize cursor.
// The function is only supported from SDL 2.0.4 (released Jan 2016)
bool any_mouse_button_down = ImGui::IsAnyMouseDown();
SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE);
#else
if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS)
io.MousePos = ImVec2((float)mx, (float)my);
#endif
}
static void ImGui_ImplSDL2_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
SDL_ShowCursor(SDL_FALSE);
}
else
{
// Show OS mouse cursor
SDL_SetCursor(g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]);
SDL_ShowCursor(SDL_TRUE);
}
}
static void ImGui_ImplSDL2_UpdateGamepads()
{
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Get gamepad
SDL_GameController* game_controller = SDL_GameControllerOpen(0);
if (!game_controller)
{
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
return;
}
// Update gamepad inputs
#define MAP_BUTTON(NAV_NO, BUTTON_NO) { io.NavInputs[NAV_NO] = (SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0) ? 1.0f : 0.0f; }
#define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
MAP_BUTTON(ImGuiNavInput_Activate, SDL_CONTROLLER_BUTTON_A); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, SDL_CONTROLLER_BUTTON_B); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, SDL_CONTROLLER_BUTTON_X); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, SDL_CONTROLLER_BUTTON_Y); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768);
MAP_ANALOG(ImGuiNavInput_LStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767);
MAP_ANALOG(ImGuiNavInput_LStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32767);
MAP_ANALOG(ImGuiNavInput_LStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767);
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
#undef MAP_BUTTON
#undef MAP_ANALOG
}
void ImGui_ImplSDL2_NewFrame(SDL_Window* window)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
// Setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
SDL_GetWindowSize(window, &w, &h);
SDL_GL_GetDrawableSize(window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
static Uint64 frequency = SDL_GetPerformanceFrequency();
Uint64 current_time = SDL_GetPerformanceCounter();
io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f);
g_Time = current_time;
ImGui_ImplSDL2_UpdateMousePosAndButtons();
ImGui_ImplSDL2_UpdateMouseCursor();
// Update game controllers (if enabled and available)
ImGui_ImplSDL2_UpdateGamepads();
}

View File

@@ -0,0 +1,31 @@
// dear imgui: Platform Binding for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
struct SDL_Window;
typedef union SDL_Event SDL_Event;
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window);
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
IMGUI_IMPL_API void ImGui_ImplSDL2_SaveState(int id);
IMGUI_IMPL_API void ImGui_ImplSDL2_LoadState(int id);

1
examples/third-party/imtui vendored Submodule