If you work with C++ code then it will be no problem and you won't probably see any issue to solve. But in this case, I mainly use C thus we have something to solve. This post will reflect that.
imgui is written in C++. It provides platform implementation sample files to hook up with various platforms/API of choice not just SDL2 but also allegro, native Windows API, metal on macOS etc.
What we need is hooking up with SDL2 + OpenGL3 with the loader either gl3w or glew. Thus the following files are what we will be looking at
The approach I use is to compile both sdl, and opengl3 implementation source files against correct SDL2 and OpenGL version as installed on the system, the same version you intend to be using with your main program. This will produce object files to be linking with our main program. We need to make sure our main program also uses the same version of SDL2 and OpenGL as used by those implementation files; to minimize the conflicts and issues.
Let's drill down what's to look for.
imgui_impl_sdl.h, we see
IMGUI_IMPL_API. This is clever move from author and contributors of imgui.
IMGUI_IMPL_API in front of each declaration of function in header file like this allows us to inject a compilation flag to make this C++ based implementation able to support C linkage so we can use it in our main program which is implemented in C. So in our
Makefile script, we must add
• struct SDL_Window
In C, whenever we want to refer to struct we need to have keyword
struct in front. But in C++, it's optional, and isn't required.
We need to put
struct in front of
SDL_Window occurrences both in its header and implementation files.
I don't want to use
typedef struct SDL_Window SDL_Window as it's not exposed for public usage by SDL2 although internally it makes use of it.
Thus from the original source code of
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 void ImGui_ImplSDL2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window);
we change it to
struct SDL_Window; typedef union SDL_Event SDL_Event; IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(struct SDL_Window* window, void* sdl_gl_context); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(struct SDL_Window* window); IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(struct SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(SDL_Event* event);
PS. Notice additional of
struct at function's parameter.
Another one to notice
typedef union SDL_Event SDL_Event. This redefines what is already done by SDL2, thus there's a warning.
In file included from src/main.c:6: In file included from /usr/local/include/SDL2/SDL.h:41: /usr/local/include/SDL2/SDL_events.h:595:3: warning: redefinition of typedef 'SDL_Event' is a C11 feature [-Wtypedef-redefinition] } SDL_Event; ^ imgui/impl/imgui_impl_sdl.h:18:25: note: previous definition is here typedef union SDL_Event SDL_Event; ^ 1 warning generated.
Although it said the definition is first found in imgui's implementation header, but that's because in
main.c, it included such header first before
SDL.h. I leave this as a notice as I still cannot think about the way to resolve this cleanly. I tend to think this is a trick as
union struct is treated as anonymous struct that we just cannot forward declare like we did previously. If you have any idea, feel free to comment below.
The same as we did for
imgui_impl_sdl.h. In our
Makefile to compile its relevant source files, add
There's one more thing to do. We need to remove default parameter value in function as seen in
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
we changed it to
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version);
• OpenGL Function Loader
There are 4 options presented in the code to handle loading OpenGL functions.
It depends on which one you want to use, just make sure you have its header and source file on your system. In this case, I have
glew installed, as well as provided
gl3w alongside the sample project. So at compile time, if we want to use
glew as a loader then specify
gl3w, no need to specify anything as implementation source code will default to it when no specific loader is supplied.
The following code as seen in
... // Set default OpenGL loader to be gl3w #if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) #define IMGUI_IMPL_OPENGL_LOADER_GL3W #endif ...
But for our main source file, we do specify
-DIMGUI_IMPL_OPENGL_LOADER_GL3W. You can modify it to be in similar way to above, anyway in this case I just go along with what is already there in what imgui provided for us.
As we use cimgui, we need to call its functions instead of directly use from imgui one, fortunately it's very simple to port. This better demonstrated via examples.
- for type, it's still the same, but just the initialization that you might have to set value one by one for each of its attribute of structure.
One gotcha we need to do is to define
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS at the top of the source file, before
#include "cimgui.h". This is important to let cimgui exposes C interface of functions and strcuts for us.
That's pretty much it!
Link against cimgui.dylib
Building cimgui will produce
lib... in front. Check its Makefile, you will see similar pattern of output names applied for other platforms, so please take good care of hanlding this. Thus if we use
-l... linking flag, it won't find such file.
We have 2 options here.
- Create relative path symlink as
ln -s cimgui.dylib libcimgui.dylib)
- Directly use
-lspecified in front) in linking phase (I used this approach)
The fact that those platform implementation files are based in C++, thus we need to link against C++ library via
gcc. You can see this reflected in Makefile.
There are 3 build targets
make all- to build both variant of
It will produce executable
main-glew at the root directory.
Run such file, and play around with it.
If click on
Menu->... on the right popup to show additional windows; you will see something similar to the following result.
The sample project has cimgui as dependency, and in turn cimgui has imgui as dependency as well. Dependencies done through git submodule.
Thus we directly refer to relevant headers and some supporting implementation source files in imgui project directory (nested inside cimgui) except for just a few source files that we copied and modified to make it compatible with C as seen in the previous section.
When build the project, it will build cimgui, then copy the result dynamic library file into its root directory in order to implicitly makes
rpath towards library correct without hassle.
At last, check inside
Makefile for full detail.
All above setup can be found on Github at haxpor/sdl2-cimgui-demo.
I tested with SDL2 2.0.9 (commit:
12569:05aff4771d9a as of Jan, 21, 2019), cimgui (check commit hash in git repo via
git submodule status), glew v.2.1.0.