imgui
imgui copied to clipboard
Initial working X11 implementation with Vulkan
This is a basic X11 impl file and example. In it's current state it successfully creates an X11 window using the xcb library, connects the window to Vulkan and then uses the existing Vulkan impl files unmodified.
Since this isn't in complete feature parity with win32 and others, I'm going by this suggestion
In doubt, don't hesitate to push a PR because that is always the first step toward finding the mergeable solution!
This is also my first foray into X11 programming. It's a wild undocumented world, and the X11 protocol is too forgiving. Things will fail silently. I am by no means any sort of expert, so I hope the start of this PR attracts experts commenting on how to proceed with the rest of the features and/or tell me everything I did wrong.
What's left for feature parity, using the win32 impl as a baseline.
-
[x] Keyboard input
-
[x] Mouse cursor visibility and shape
-
[ ] Gamepad support
-
[x] Cipboard support
-
[ ] OpenGL renderer
-
~Mouse cursor~ ~It looks like
xcb_xfixes_show_cursorandxcb_xfixes_hide_cursordoes everything we need. I don't have that fully working currently. I think that's more me misunderstanding the boolean imgui flags and what they mean. Changing the X11 mouse cursor to represent the state of the mouse hovering over imgui widgets looks... like not a thing that usually happens with raw X11? That looks like it's a thing the window manager takes care of.~ -
Gamepad I'm wondering if we should put this in the impl file or leave it up to the library user instead. Gamepad support is either in the main library proper, like win32, or also implemented in the impl file. However, with linux being linux there are two different routes for controller support. One being an older joystick API, and a newer one using
libevdev. This would add some decent weight to the X11 impl file. -
~Clipboard~ ~This is a wild thing in X11 land. From what I can tell the common practice is creating a second unexposed window for clipboard handling. Then, set that window up for receiving clipboard events. When you want the clipboard contents, send a
xcb_convert_selectionmessage to the X server. After sending a request, use the second window to poll for theXCB_SELECTION_NOTIFYevent. On receiving the event extract the data from the event message and store it, in this case in something likechar * g_ClipboardContents. I'm not sure what the second window achieves, potentially better for multithreaded apps. It should be possible to do the same in the event processor in the X11 impl file. Sending clipboard contents to the X server looks more straightforward, if verbose. Example~ -
OpenGL This is more on me not understanding OpenGL as much as I do Vulkan. Yes, I know that's a weird state for a person's knowledge, yet here I am. Not only that but OpenGL on linux is, as linux being linux, confusing and potentially incompatible. Lots of chatter on how xcb doesn't work with OpenGL straight up, though I see xcb glx libraries available? :man_shrugging: Vulkan worked totally fine without issue, they abstracted the idea of a window surface perfectly and it worked first try.
I'll open this up as ready for review as it works in a basic case without the extra platform features.
Thanks for working on this.
Attached a bunch of cumulative fixes to make the code follow the dear imgui coding style.
I also:
- Renamed
ImGui_ImplX11_Event()toImGui_ImplX11_ProcessEvent() - Removed unnecessary indentation and branches from the main loop, making it matches other examples
- Noted that display size change should be done in _ProcessEvent and not in the main function.
- Various style fixes.
This has NOT been compiled, I don't have X11.
To add to your branch (possibly flatten into).
That diff applied cleanly and compiled without issue. Apologies for adding more work.
Bit more progress, hopefully less problematic with style issues. Added hiding the OS cursor. Using xcb-cursors it looks possible changing the OS mouse cursor reflecting the state of what widget it's over like with Windows. I'll do that next.
Example on what's working so far: responding to window resize, keyboard and mouse events, hiding the OS mouse cursor. Unfortunately I can't find a screen recorder which shows the actual cursor state, it does hide the cursor when entering/leaving the window and when the option changes.

There are a few references on WantSetMousePos rarely used. Is it worthwhile implementing that feature?
Turns out you can control the X windows cursor!

Whoops, didn't set the HasMouseCursors config flag, now it works properly

Next up, clipboard support is going to get interesting, it looks intensely involved on the X side. Interestingly this is turning out to be a complete reference example on creating an X window and participating in the X system using xcb. I couldn't find anything like that through this journey!
I was wondering why do you need a raw X11 backend for dear imgui, instead if using GLFW/SDL why allows for portable application?
I don't have such dependencies on libraries like that. Before I started working on this I discovered this comment, so I thought I'd contribute back the work I'm doing based on that. If you don't think this should go into this project because of the maintenance burden I completely understand and will close this PR.
don't have such dependencies on libraries like that.
Why?
I admit the maintenance burden is a little bit worrying for me (since i'd have no way of testing/fixing that myself). Would you be willing to maintain this down the line? Realistically if this is your first venture into X11 i am expecting people to show up with issues.
The main incoming complication should be support for multi-viewports. This is also where there's potential for a raw x11 back-end to potentially be better than SDL and GLFW backends: there are many subtleties of multi-viewports support (popups/tooltip positioning, pass through inputs, focus when creating new viewports) which are tricky to do perfectly with SDL and GLFW, if the raw X11 backends was working better it would be a great alternative for those seeking to use multi-viewports.
Why?
This is going off on a philosophical tangent now. I think you answered your own question. Introducing such libraries means you're married to them forever, better or worse. When you're trying to do things they don't support you're in a hard place. We've been conditioned as programmers that the underlying systems (Win32, Cocoa, X protocol, and such) are difficult and should be avoided through abstractions. Microsoft is especially guilty of this trying to promote their framework of the decade, MFC, WTL/ATL, and now both C++/WinRT and Windows UI this decade. Turns out when you cut through the abstractions the underlying systems aren't difficult at all. The most problematic thing about is we have at best tribal knowledge on how to work with them.
Perhaps a more relevant answer is because I can and I prefer it. It looked like sharing my work may be useful to others instead of probably dying alone in obscurity. Again, if you feel this is too much for this project I close it and move on, no hard feelings I absolutely understand.
Would you be willing to maintain this down the line?
In the immediate term absolutely. If it isn't already it should reach stability quickly, there isn't anything arcane here. In the longer term I have no idea what may happen in say six months; if I'm employed in some way that drains my resources or, being American, dying from a horrible plague. I think with anything Linux it should come with a 'best effort' caveat because of the infinite amount of things the end user can do. Though again this is all so simple and standard it shouldn't be an issue.
multi-viewports
I know that's where the future of this project is, it looked like it's been constantly in flux so I haven't adopted it. Perhaps if you feel this is useful retargeting this for the docking branch instead is more appropriate?
I have an alternate idea. I can finish and make this up to standards, then instead pulling it into master we can leave it as a branch or put it up as a gist for reference?
Thank you for your detailed answer :)
Viewports features are a little in flux on the library side but it's definitely working and used by people, and its a good time to implement that stuff in a backend.
I agree that either way we should make it up to the standards first. Retargetting on docking should be ok in order to do the work on multi-viewports, and once its fully done we'd merge this in two steps (first a stripped out commit on master, then merge in docking and add the viewports code to it).
If you are willing to make it up to the standards we can merge it in.
Copying to the 'clipboard' works! What a strange concept. It makes sense, it's just alien.

Clipboard works in both directions. What a journey. I tried to leave lots of descriptive comments for anyone using this as a reference for X11 & xcb in general.

I'm going to take a break from this for a couple days and then look at the current state of multiple windows. I'm also not terribly happy with the code, I want to take a look a multiple windows before trying to go after code cleanup.
@RoryO, I tried building your X11 / vulkan code but ran into trouble:
hinxx@obzen ~/Code/x11imgui/examples/example_x11_vulkan $ bash build.sh
In file included from ../imgui_impl_x11.cpp:23:0:
/usr/include/xcb/xkb.h:727:19: error: declaration does not declare anything [-fpermissive]
uint8_t explicit;
^~~~~~~~
/usr/include/xcb/xkb.h:2005:30: error: expected unqualified-id before ‘explicit’
xcb_xkb_set_explicit_t *explicit;
^~~~~~~~
/usr/include/xcb/xkb.h:2005:29: error: expected ‘;’ at end of member declaration
xcb_xkb_set_explicit_t *explicit;
^
/usr/include/xcb/xkb.h:2005:30: error: declaration does not declare anything [-fpermissive]
xcb_xkb_set_explicit_t *explicit;
^~~~~~~~
In file included from ../imgui_impl_x11.cpp:23:0:
/usr/include/xcb/xkb.h:727:19: error: declaration does not declare anything [-fpermissive]
uint8_t explicit;
^~~~~~~~
/usr/include/xcb/xkb.h:2005:30: error: expected unqualified-id before ‘explicit’
xcb_xkb_set_explicit_t *explicit;
^~~~~~~~
/usr/include/xcb/xkb.h:2005:29: error: expected ‘;’ at end of member declaration
xcb_xkb_set_explicit_t *explicit;
^
/usr/include/xcb/xkb.h:2005:30: error: declaration does not declare anything [-fpermissive]
xcb_xkb_set_explicit_t *explicit;
^~~~~~~~
This is on Linux Mint 19.3, if it matters.
Oh right. I hacked that header locally because ‘explicit’ is a reserved word in c++, and that header is C. I didn’t look into how to work around it yet. Thanks for the reminder, I’ll come up with a proper fix.
Sounds tricky.. I wonder if you could #define explicit c_explicit around the xcb.h include...
Additionally, you shouldn't include xcb.h from imgui_impl_x11.h as you can forward declare those structure?
It only need an include from imgui_impl_x11.cpp.
The define trick worked, thanks! @hinxx please give it another go and let me know how it is. Also, I retargeted this to multiple viewports. I think it's going to be too much of a mess trying to do this with both non-viewport and viewport code, and viewport looks close to release? So lets make this only available for multi-viewport.
I can't forward declare all the structures in the impl file. GLFW for example just typedefs them in their header file
typedef struct GLFWwindow GLFWwindow;
In the xcb header file they have both typedef struct and full typedef struct {} definitions. If I try and forward declare xcb_generic_event_t them I get a conflicting type error when including both the impl header and xcb header.
/**
* @brief Generic event.
*
* A generic event structure.
*/
typedef struct {
uint8_t response_type; /**< Type of the response */
uint8_t pad0; /**< Padding */
uint16_t sequence; /**< Sequence number */
uint32_t pad[7]; /**< Padding */
uint32_t full_sequence; /**< full sequence */
} xcb_generic_event_t;
I dunno how to solve not including the xcb.h without making the impl header parameter void *. I'm sorry, my C and C++ tricks are at a limit here.
@RoryO, I can compile it now, thanks! Additionally, after fixing some issues on my system (solved by installing missing packages regarding vulkan for Intel HD 630 card I'm using and configuring Xorg for DRI3 support) I could actually run the example.
Regarding the window resize, is it supposed to be possible now on this PR?
Resize crashes the app for me at the moment. I did notice that the windows resize issue is being tackled in https://github.com/ocornut/imgui/compare/features/vulkan_resize_fix and https://github.com/ocornut/imgui/pull/3390. More info from side there..
You could rebase your branch over master and forcepush and remove all the cruft/duplicate commits in the list.
Yeah, basic viewports working! There's some crash bugs relating to resizing and another I get occasionally with imgui.cpp:11130: void ImGui::UpdateViewportsNewFrame(): Assertion '0' failed. that I haven't looked at yet. Still, big milestone.
I found that the speed of creating a new window depends entirely on the window manager. With KDE it frustratingly lags for a second or two. Other smaller window managers like e or awesomewm are instant in creating the window, exact same code. I don't think there's anything we can do about that.

Merged in #3390 as I'm at the limit on what I can do with viewports without it.
Otherwise have removing window chrome working. Dragging a window around without a frame is pretty choppy. SDL and glfw both are as well, glfw seems a bit less choppy. Different window managers doesn't help much. I wonder what window managers do to get smoother window motions. The framerate is still 60fps internally when moving a window so it should be sending an update for the window position 60 time a second.
Interestingly the glfw and sdl impl doesn't respect changing the window decoration after window creation.

Interestingly the glfw and sdl impl doesn't respect changing the window decoration after window creation.
That’s right, i suppose we could add support for that in a separate PR. Win32 does support it.
@RoryO about your xcb/GLX/xlib in OpenGL bullet
GLX protocol extension API is defined in terms of libX11 objects on API/ABI level; libxcb is successor of libX11 and libX11 was rewritten (ie. libX11-xcb) in terms of libxcb to support legacy clients linking libX11.
In other words back in 1992 Khronos standardized that <GL/glx.h> includes <X11/Xlib.h> so we must use libX11 with GLX and no way around it.
Since after around two decades almost everyone uses libX11-xcb flavor of libX11 one can use <X11/Xlib-xcb.h> to obtain xcb_connection_t* and thus xcb_screen_t* form Display.
However contrary is not true, there is no way to obtain libX11 Display using just libxcb.
That is one of few reasons for more recent uptake in adaptation of EGL API as an alternative to straight GLX It has by extension a concept of platform EGL_EXT_platform_base that can be added and advertised as derived extensions
- X11 libX11
- X11 xcb,
- wayland,
- device ie. headless),
- also headless but gpu!=nvidia gbm
- others like android (ANativeWindow) and stuff, that is used on android anything... surfaceless for compute, etc.
- native display (X11
Display,xcb_screen_t*,wl_display*, DRM fd -open("/dev/dri/card%d", ...),gbm_device) - native window surface - (X11
Window*,xcb_window_t*,wl_surface*, drm buffer attached to KMS plane,gbm_surface *) etc..
You can think about it as WSI in Vulkan is alternative to:
AGL - ( on mac osx - that died in favour of metal) WGL - (on windows - that no one really cared about until WebGL came) GLX - (X11 protocol extension that let you add OpenGL server support to X11 server, so X11 OpenGL client can use it) EGL - initialy targeted embedded where there were no X11 or any other windowing system.
Bit of mess, but maybe in a decade or two, we will have new shiny mess and will forget about this one. If you have additional questions, please let me know, maybe I'll know the anwser.
Wow thank you for that excellent information @piotrrak ! I've been on another project for a bit as this is at the point where it's usable for myself. When I get back to this I'll use that information on creating an opengl x11 impl.
I haven't tried wayland yet as for some reason systemd does stupid systemd things and crashes when I try a wayland session; though I think Xwayland should work out fine.