mirror of
https://github.com/Bloodysharp/Cheat-imgui-menu-RAGNAREK-v2.git
synced 2024-12-23 00:17:24 +08:00
Add files via upload
This commit is contained in:
parent
cd6160d4a0
commit
4890aca9f9
812
backends/imgui_impl_osx.mm
Normal file
812
backends/imgui_impl_osx.mm
Normal file
@ -0,0 +1,812 @@
|
|||||||
|
// dear imgui: Platform Backend for OSX / Cocoa
|
||||||
|
// This needs to be used along with a Renderer (e.g. OpenGL2, OpenGL3, Vulkan, Metal..)
|
||||||
|
// - Not well tested. If you want a portable application, prefer using the GLFW or SDL platform Backends on Mac.
|
||||||
|
// - Requires linking with the GameController framework ("-framework GameController").
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/Pen.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
|
||||||
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: IME support.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#import "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#import "imgui_impl_osx.h"
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <Carbon/Carbon.h>
|
||||||
|
#import <GameController/GameController.h>
|
||||||
|
#import <time.h>
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F20 function keys. Stopped mapping F13 into PrintScreen.
|
||||||
|
// 2023-04-09: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_Pen.
|
||||||
|
// 2023-02-01: Fixed scroll wheel scaling for devices emitting events with hasPreciseScrollingDeltas==false (e.g. non-Apple mices).
|
||||||
|
// 2022-11-02: Fixed mouse coordinates before clicking the host window.
|
||||||
|
// 2022-10-06: Fixed mouse inputs on flipped views.
|
||||||
|
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
|
||||||
|
// 2022-05-03: Inputs: Removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture.
|
||||||
|
// 2022-04-27: Misc: Store backend data in a per-context struct, allowing to use this backend with multiple contexts.
|
||||||
|
// 2022-03-22: Inputs: Monitor NSKeyUp events to catch missing keyUp for key when user press Cmd + key
|
||||||
|
// 2022-02-07: Inputs: Forward keyDown/keyUp events to OS when unused by dear imgui.
|
||||||
|
// 2022-01-31: Fixed building with old Xcode versions that are missing gamepad features.
|
||||||
|
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
|
||||||
|
// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
|
||||||
|
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
|
||||||
|
// 2022-01-12: Inputs: Added basic Platform IME support, hooking the io.SetPlatformImeDataFn() function.
|
||||||
|
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
|
||||||
|
// 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys.
|
||||||
|
// 2021-12-13: Add game controller support.
|
||||||
|
// 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards.
|
||||||
|
// 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events.
|
||||||
|
// 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key.
|
||||||
|
// 2021-04-19: Inputs: Added a fix for keys remaining stuck in pressed state when CMD-tabbing into different application.
|
||||||
|
// 2021-01-27: Inputs: Added a fix for mouse position not being reported when mouse buttons other than left one are down.
|
||||||
|
// 2020-10-28: Inputs: Added a fix for handling keypad-enter key.
|
||||||
|
// 2020-05-25: Inputs: Added a fix for missing trackpad clicks when done with "soft tap".
|
||||||
|
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
|
||||||
|
// 2019-10-11: Inputs: Fix using Backspace key.
|
||||||
|
// 2019-07-21: Re-added clipboard handlers as they are not enabled by default in core imgui.cpp (reverted 2019-05-18 change).
|
||||||
|
// 2019-05-28: Inputs: Added mouse cursor shape and visibility support.
|
||||||
|
// 2019-05-18: Misc: Removed clipboard handlers as they are now supported by core imgui.cpp.
|
||||||
|
// 2019-05-11: Inputs: Don't filter character values before calling AddInputCharacter() apart from 0xF700..0xFFFF range.
|
||||||
|
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
|
||||||
|
// 2018-07-07: Initial version.
|
||||||
|
|
||||||
|
#define APPLE_HAS_BUTTON_OPTIONS (__IPHONE_OS_VERSION_MIN_REQUIRED >= 130000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500 || __TV_OS_VERSION_MIN_REQUIRED >= 130000)
|
||||||
|
#define APPLE_HAS_CONTROLLER (__IPHONE_OS_VERSION_MIN_REQUIRED >= 140000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 110000 || __TV_OS_VERSION_MIN_REQUIRED >= 140000)
|
||||||
|
#define APPLE_HAS_THUMBSTICKS (__IPHONE_OS_VERSION_MIN_REQUIRED >= 120100 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101401 || __TV_OS_VERSION_MIN_REQUIRED >= 120100)
|
||||||
|
|
||||||
|
@class ImGuiObserver;
|
||||||
|
@class KeyEventResponder;
|
||||||
|
|
||||||
|
// Data
|
||||||
|
struct ImGui_ImplOSX_Data
|
||||||
|
{
|
||||||
|
CFTimeInterval Time;
|
||||||
|
NSCursor* MouseCursors[ImGuiMouseCursor_COUNT];
|
||||||
|
bool MouseCursorHidden;
|
||||||
|
ImGuiObserver* Observer;
|
||||||
|
KeyEventResponder* KeyEventResponder;
|
||||||
|
NSTextInputContext* InputContext;
|
||||||
|
id Monitor;
|
||||||
|
|
||||||
|
ImGui_ImplOSX_Data() { memset(this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static ImGui_ImplOSX_Data* ImGui_ImplOSX_CreateBackendData() { return IM_NEW(ImGui_ImplOSX_Data)(); }
|
||||||
|
static ImGui_ImplOSX_Data* ImGui_ImplOSX_GetBackendData() { return (ImGui_ImplOSX_Data*)ImGui::GetIO().BackendPlatformUserData; }
|
||||||
|
static void ImGui_ImplOSX_DestroyBackendData() { IM_DELETE(ImGui_ImplOSX_GetBackendData()); }
|
||||||
|
|
||||||
|
static inline CFTimeInterval GetMachAbsoluteTimeInSeconds() { return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9); }
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
static void ImGui_ImplOSX_AddTrackingArea(NSView* _Nonnull view);
|
||||||
|
static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view);
|
||||||
|
|
||||||
|
// Undocumented methods for creating cursors.
|
||||||
|
@interface NSCursor()
|
||||||
|
+ (id)_windowResizeNorthWestSouthEastCursor;
|
||||||
|
+ (id)_windowResizeNorthEastSouthWestCursor;
|
||||||
|
+ (id)_windowResizeNorthSouthCursor;
|
||||||
|
+ (id)_windowResizeEastWestCursor;
|
||||||
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
KeyEventResponder implements the NSTextInputClient protocol as is required by the macOS text input manager.
|
||||||
|
|
||||||
|
The macOS text input manager is invoked by calling the interpretKeyEvents method from the keyDown method.
|
||||||
|
Keyboard events are then evaluated by the macOS input manager and valid text input is passed back via the
|
||||||
|
insertText:replacementRange method.
|
||||||
|
|
||||||
|
This is the same approach employed by other cross-platform libraries such as SDL2:
|
||||||
|
https://github.com/spurious/SDL-mirror/blob/e17aacbd09e65a4fd1e166621e011e581fb017a8/src/video/cocoa/SDL_cocoakeyboard.m#L53
|
||||||
|
and GLFW:
|
||||||
|
https://github.com/glfw/glfw/blob/b55a517ae0c7b5127dffa79a64f5406021bf9076/src/cocoa_window.m#L722-L723
|
||||||
|
*/
|
||||||
|
@interface KeyEventResponder: NSView<NSTextInputClient>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation KeyEventResponder
|
||||||
|
{
|
||||||
|
float _posX;
|
||||||
|
float _posY;
|
||||||
|
NSRect _imeRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public
|
||||||
|
|
||||||
|
- (void)setImePosX:(float)posX imePosY:(float)posY
|
||||||
|
{
|
||||||
|
_posX = posX;
|
||||||
|
_posY = posY;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateImePosWithView:(NSView *)view
|
||||||
|
{
|
||||||
|
NSWindow *window = view.window;
|
||||||
|
if (!window)
|
||||||
|
return;
|
||||||
|
NSRect contentRect = [window contentRectForFrameRect:window.frame];
|
||||||
|
NSRect rect = NSMakeRect(_posX, contentRect.size.height - _posY, 0, 0);
|
||||||
|
_imeRect = [window convertRectToScreen:rect];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewDidMoveToWindow
|
||||||
|
{
|
||||||
|
// Ensure self is a first responder to receive the input events.
|
||||||
|
[self.window makeFirstResponder:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)keyDown:(NSEvent*)event
|
||||||
|
{
|
||||||
|
if (!ImGui_ImplOSX_HandleEvent(event, self))
|
||||||
|
[super keyDown:event];
|
||||||
|
|
||||||
|
// Call to the macOS input manager system.
|
||||||
|
[self interpretKeyEvents:@[event]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)keyUp:(NSEvent*)event
|
||||||
|
{
|
||||||
|
if (!ImGui_ImplOSX_HandleEvent(event, self))
|
||||||
|
[super keyUp:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
NSString* characters;
|
||||||
|
if ([aString isKindOfClass:[NSAttributedString class]])
|
||||||
|
characters = [aString string];
|
||||||
|
else
|
||||||
|
characters = (NSString*)aString;
|
||||||
|
|
||||||
|
io.AddInputCharactersUTF8(characters.UTF8String);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)acceptsFirstResponder
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doCommandBySelector:(SEL)myselector
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)characterIndexForPoint:(NSPoint)point
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange
|
||||||
|
{
|
||||||
|
return _imeRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)hasMarkedText
|
||||||
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSRange)markedRange
|
||||||
|
{
|
||||||
|
return NSMakeRange(NSNotFound, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSRange)selectedRange
|
||||||
|
{
|
||||||
|
return NSMakeRange(NSNotFound, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setMarkedText:(nonnull id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)unmarkText
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nonnull NSArray<NSAttributedStringKey>*)validAttributesForMarkedText
|
||||||
|
{
|
||||||
|
return @[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface ImGuiObserver : NSObject
|
||||||
|
|
||||||
|
- (void)onApplicationBecomeActive:(NSNotification*)aNotification;
|
||||||
|
- (void)onApplicationBecomeInactive:(NSNotification*)aNotification;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ImGuiObserver
|
||||||
|
|
||||||
|
- (void)onApplicationBecomeActive:(NSNotification*)aNotification
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddFocusEvent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)onApplicationBecomeInactive:(NSNotification*)aNotification
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddFocusEvent(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
static ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code)
|
||||||
|
{
|
||||||
|
switch (key_code)
|
||||||
|
{
|
||||||
|
case kVK_ANSI_A: return ImGuiKey_A;
|
||||||
|
case kVK_ANSI_S: return ImGuiKey_S;
|
||||||
|
case kVK_ANSI_D: return ImGuiKey_D;
|
||||||
|
case kVK_ANSI_F: return ImGuiKey_F;
|
||||||
|
case kVK_ANSI_H: return ImGuiKey_H;
|
||||||
|
case kVK_ANSI_G: return ImGuiKey_G;
|
||||||
|
case kVK_ANSI_Z: return ImGuiKey_Z;
|
||||||
|
case kVK_ANSI_X: return ImGuiKey_X;
|
||||||
|
case kVK_ANSI_C: return ImGuiKey_C;
|
||||||
|
case kVK_ANSI_V: return ImGuiKey_V;
|
||||||
|
case kVK_ANSI_B: return ImGuiKey_B;
|
||||||
|
case kVK_ANSI_Q: return ImGuiKey_Q;
|
||||||
|
case kVK_ANSI_W: return ImGuiKey_W;
|
||||||
|
case kVK_ANSI_E: return ImGuiKey_E;
|
||||||
|
case kVK_ANSI_R: return ImGuiKey_R;
|
||||||
|
case kVK_ANSI_Y: return ImGuiKey_Y;
|
||||||
|
case kVK_ANSI_T: return ImGuiKey_T;
|
||||||
|
case kVK_ANSI_1: return ImGuiKey_1;
|
||||||
|
case kVK_ANSI_2: return ImGuiKey_2;
|
||||||
|
case kVK_ANSI_3: return ImGuiKey_3;
|
||||||
|
case kVK_ANSI_4: return ImGuiKey_4;
|
||||||
|
case kVK_ANSI_6: return ImGuiKey_6;
|
||||||
|
case kVK_ANSI_5: return ImGuiKey_5;
|
||||||
|
case kVK_ANSI_Equal: return ImGuiKey_Equal;
|
||||||
|
case kVK_ANSI_9: return ImGuiKey_9;
|
||||||
|
case kVK_ANSI_7: return ImGuiKey_7;
|
||||||
|
case kVK_ANSI_Minus: return ImGuiKey_Minus;
|
||||||
|
case kVK_ANSI_8: return ImGuiKey_8;
|
||||||
|
case kVK_ANSI_0: return ImGuiKey_0;
|
||||||
|
case kVK_ANSI_RightBracket: return ImGuiKey_RightBracket;
|
||||||
|
case kVK_ANSI_O: return ImGuiKey_O;
|
||||||
|
case kVK_ANSI_U: return ImGuiKey_U;
|
||||||
|
case kVK_ANSI_LeftBracket: return ImGuiKey_LeftBracket;
|
||||||
|
case kVK_ANSI_I: return ImGuiKey_I;
|
||||||
|
case kVK_ANSI_P: return ImGuiKey_P;
|
||||||
|
case kVK_ANSI_L: return ImGuiKey_L;
|
||||||
|
case kVK_ANSI_J: return ImGuiKey_J;
|
||||||
|
case kVK_ANSI_Quote: return ImGuiKey_Apostrophe;
|
||||||
|
case kVK_ANSI_K: return ImGuiKey_K;
|
||||||
|
case kVK_ANSI_Semicolon: return ImGuiKey_Semicolon;
|
||||||
|
case kVK_ANSI_Backslash: return ImGuiKey_Backslash;
|
||||||
|
case kVK_ANSI_Comma: return ImGuiKey_Comma;
|
||||||
|
case kVK_ANSI_Slash: return ImGuiKey_Slash;
|
||||||
|
case kVK_ANSI_N: return ImGuiKey_N;
|
||||||
|
case kVK_ANSI_M: return ImGuiKey_M;
|
||||||
|
case kVK_ANSI_Period: return ImGuiKey_Period;
|
||||||
|
case kVK_ANSI_Grave: return ImGuiKey_GraveAccent;
|
||||||
|
case kVK_ANSI_KeypadDecimal: return ImGuiKey_KeypadDecimal;
|
||||||
|
case kVK_ANSI_KeypadMultiply: return ImGuiKey_KeypadMultiply;
|
||||||
|
case kVK_ANSI_KeypadPlus: return ImGuiKey_KeypadAdd;
|
||||||
|
case kVK_ANSI_KeypadClear: return ImGuiKey_NumLock;
|
||||||
|
case kVK_ANSI_KeypadDivide: return ImGuiKey_KeypadDivide;
|
||||||
|
case kVK_ANSI_KeypadEnter: return ImGuiKey_KeypadEnter;
|
||||||
|
case kVK_ANSI_KeypadMinus: return ImGuiKey_KeypadSubtract;
|
||||||
|
case kVK_ANSI_KeypadEquals: return ImGuiKey_KeypadEqual;
|
||||||
|
case kVK_ANSI_Keypad0: return ImGuiKey_Keypad0;
|
||||||
|
case kVK_ANSI_Keypad1: return ImGuiKey_Keypad1;
|
||||||
|
case kVK_ANSI_Keypad2: return ImGuiKey_Keypad2;
|
||||||
|
case kVK_ANSI_Keypad3: return ImGuiKey_Keypad3;
|
||||||
|
case kVK_ANSI_Keypad4: return ImGuiKey_Keypad4;
|
||||||
|
case kVK_ANSI_Keypad5: return ImGuiKey_Keypad5;
|
||||||
|
case kVK_ANSI_Keypad6: return ImGuiKey_Keypad6;
|
||||||
|
case kVK_ANSI_Keypad7: return ImGuiKey_Keypad7;
|
||||||
|
case kVK_ANSI_Keypad8: return ImGuiKey_Keypad8;
|
||||||
|
case kVK_ANSI_Keypad9: return ImGuiKey_Keypad9;
|
||||||
|
case kVK_Return: return ImGuiKey_Enter;
|
||||||
|
case kVK_Tab: return ImGuiKey_Tab;
|
||||||
|
case kVK_Space: return ImGuiKey_Space;
|
||||||
|
case kVK_Delete: return ImGuiKey_Backspace;
|
||||||
|
case kVK_Escape: return ImGuiKey_Escape;
|
||||||
|
case kVK_CapsLock: return ImGuiKey_CapsLock;
|
||||||
|
case kVK_Control: return ImGuiKey_LeftCtrl;
|
||||||
|
case kVK_Shift: return ImGuiKey_LeftShift;
|
||||||
|
case kVK_Option: return ImGuiKey_LeftAlt;
|
||||||
|
case kVK_Command: return ImGuiKey_LeftSuper;
|
||||||
|
case kVK_RightControl: return ImGuiKey_RightCtrl;
|
||||||
|
case kVK_RightShift: return ImGuiKey_RightShift;
|
||||||
|
case kVK_RightOption: return ImGuiKey_RightAlt;
|
||||||
|
case kVK_RightCommand: return ImGuiKey_RightSuper;
|
||||||
|
// case kVK_Function: return ImGuiKey_;
|
||||||
|
// case kVK_VolumeUp: return ImGuiKey_;
|
||||||
|
// case kVK_VolumeDown: return ImGuiKey_;
|
||||||
|
// case kVK_Mute: return ImGuiKey_;
|
||||||
|
case kVK_F1: return ImGuiKey_F1;
|
||||||
|
case kVK_F2: return ImGuiKey_F2;
|
||||||
|
case kVK_F3: return ImGuiKey_F3;
|
||||||
|
case kVK_F4: return ImGuiKey_F4;
|
||||||
|
case kVK_F5: return ImGuiKey_F5;
|
||||||
|
case kVK_F6: return ImGuiKey_F6;
|
||||||
|
case kVK_F7: return ImGuiKey_F7;
|
||||||
|
case kVK_F8: return ImGuiKey_F8;
|
||||||
|
case kVK_F9: return ImGuiKey_F9;
|
||||||
|
case kVK_F10: return ImGuiKey_F10;
|
||||||
|
case kVK_F11: return ImGuiKey_F11;
|
||||||
|
case kVK_F12: return ImGuiKey_F12;
|
||||||
|
case kVK_F13: return ImGuiKey_F13;
|
||||||
|
case kVK_F14: return ImGuiKey_F14;
|
||||||
|
case kVK_F15: return ImGuiKey_F15;
|
||||||
|
case kVK_F16: return ImGuiKey_F16;
|
||||||
|
case kVK_F17: return ImGuiKey_F17;
|
||||||
|
case kVK_F18: return ImGuiKey_F18;
|
||||||
|
case kVK_F19: return ImGuiKey_F19;
|
||||||
|
case kVK_F20: return ImGuiKey_F20;
|
||||||
|
case 0x6E: return ImGuiKey_Menu;
|
||||||
|
case kVK_Help: return ImGuiKey_Insert;
|
||||||
|
case kVK_Home: return ImGuiKey_Home;
|
||||||
|
case kVK_PageUp: return ImGuiKey_PageUp;
|
||||||
|
case kVK_ForwardDelete: return ImGuiKey_Delete;
|
||||||
|
case kVK_End: return ImGuiKey_End;
|
||||||
|
case kVK_PageDown: return ImGuiKey_PageDown;
|
||||||
|
case kVK_LeftArrow: return ImGuiKey_LeftArrow;
|
||||||
|
case kVK_RightArrow: return ImGuiKey_RightArrow;
|
||||||
|
case kVK_DownArrow: return ImGuiKey_DownArrow;
|
||||||
|
case kVK_UpArrow: return ImGuiKey_UpArrow;
|
||||||
|
default: return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef IMGUI_IMPL_METAL_CPP_EXTENSIONS
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplOSX_Init(void* _Nonnull view) {
|
||||||
|
return ImGui_ImplOSX_Init((__bridge NSView*)(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(void* _Nullable view) {
|
||||||
|
return ImGui_ImplOSX_NewFrame((__bridge NSView*)(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
bool ImGui_ImplOSX_Init(NSView* view)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_CreateBackendData();
|
||||||
|
io.BackendPlatformUserData = (void*)bd;
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
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_osx";
|
||||||
|
|
||||||
|
bd->Observer = [ImGuiObserver new];
|
||||||
|
|
||||||
|
// Load cursors. Some of them are undocumented.
|
||||||
|
bd->MouseCursorHidden = false;
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_Arrow] = [NSCursor arrowCursor];
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_TextInput] = [NSCursor IBeamCursor];
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = [NSCursor closedHandCursor];
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_Hand] = [NSCursor pointingHandCursor];
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = [NSCursor operationNotAllowedCursor];
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor];
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor];
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor];
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor];
|
||||||
|
|
||||||
|
// Note that imgui.cpp also include default OSX clipboard handlers which can be enabled
|
||||||
|
// by adding '#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS' in imconfig.h and adding '-framework ApplicationServices' to your linker command-line.
|
||||||
|
// Since we are already in ObjC land here, it is easy for us to add a clipboard handler using the NSPasteboard api.
|
||||||
|
io.SetClipboardTextFn = [](void*, const char* str) -> void
|
||||||
|
{
|
||||||
|
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
||||||
|
[pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil];
|
||||||
|
[pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString];
|
||||||
|
};
|
||||||
|
|
||||||
|
io.GetClipboardTextFn = [](void*) -> const char*
|
||||||
|
{
|
||||||
|
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
||||||
|
NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]];
|
||||||
|
if (![available isEqualToString:NSPasteboardTypeString])
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
NSString* string = [pasteboard stringForType:NSPasteboardTypeString];
|
||||||
|
if (string == nil)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const char* string_c = (const char*)[string UTF8String];
|
||||||
|
size_t string_len = strlen(string_c);
|
||||||
|
static ImVector<char> s_clipboard;
|
||||||
|
s_clipboard.resize((int)string_len + 1);
|
||||||
|
strcpy(s_clipboard.Data, string_c);
|
||||||
|
return s_clipboard.Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:bd->Observer
|
||||||
|
selector:@selector(onApplicationBecomeActive:)
|
||||||
|
name:NSApplicationDidBecomeActiveNotification
|
||||||
|
object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:bd->Observer
|
||||||
|
selector:@selector(onApplicationBecomeInactive:)
|
||||||
|
name:NSApplicationDidResignActiveNotification
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
// Add the NSTextInputClient to the view hierarchy,
|
||||||
|
// to receive keyboard events and translate them to input text.
|
||||||
|
bd->KeyEventResponder = [[KeyEventResponder alloc] initWithFrame:NSZeroRect];
|
||||||
|
bd->InputContext = [[NSTextInputContext alloc] initWithClient:bd->KeyEventResponder];
|
||||||
|
[view addSubview:bd->KeyEventResponder];
|
||||||
|
ImGui_ImplOSX_AddTrackingArea(view);
|
||||||
|
|
||||||
|
io.SetPlatformImeDataFn = [](ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void
|
||||||
|
{
|
||||||
|
ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData();
|
||||||
|
if (data->WantVisible)
|
||||||
|
{
|
||||||
|
[bd->InputContext activate];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[bd->InputContext discardMarkedText];
|
||||||
|
[bd->InputContext invalidateCharacterCoordinates];
|
||||||
|
[bd->InputContext deactivate];
|
||||||
|
}
|
||||||
|
[bd->KeyEventResponder setImePosX:data->InputPos.x imePosY:data->InputPos.y + data->InputLineHeight];
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplOSX_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||||
|
|
||||||
|
bd->Observer = nullptr;
|
||||||
|
if (bd->Monitor != nullptr)
|
||||||
|
{
|
||||||
|
[NSEvent removeMonitor:bd->Monitor];
|
||||||
|
bd->Monitor = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplOSX_DestroyBackendData();
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.BackendPlatformName = nullptr;
|
||||||
|
io.BackendPlatformUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasGamepad);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplOSX_UpdateMouseCursor()
|
||||||
|
{
|
||||||
|
ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData();
|
||||||
|
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
|
||||||
|
if (!bd->MouseCursorHidden)
|
||||||
|
{
|
||||||
|
bd->MouseCursorHidden = true;
|
||||||
|
[NSCursor hide];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSCursor* desired = bd->MouseCursors[imgui_cursor] ?: bd->MouseCursors[ImGuiMouseCursor_Arrow];
|
||||||
|
// -[NSCursor set] generates measureable overhead if called unconditionally.
|
||||||
|
if (desired != NSCursor.currentCursor)
|
||||||
|
{
|
||||||
|
[desired set];
|
||||||
|
}
|
||||||
|
if (bd->MouseCursorHidden)
|
||||||
|
{
|
||||||
|
bd->MouseCursorHidden = false;
|
||||||
|
[NSCursor unhide];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplOSX_UpdateGamepads()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
memset(io.NavInputs, 0, sizeof(io.NavInputs));
|
||||||
|
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if APPLE_HAS_CONTROLLER
|
||||||
|
GCController* controller = GCController.current;
|
||||||
|
#else
|
||||||
|
GCController* controller = GCController.controllers.firstObject;
|
||||||
|
#endif
|
||||||
|
if (controller == nil || controller.extendedGamepad == nil)
|
||||||
|
{
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GCExtendedGamepad* gp = controller.extendedGamepad;
|
||||||
|
|
||||||
|
// Update gamepad inputs
|
||||||
|
#define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)
|
||||||
|
#define MAP_BUTTON(KEY_NO, BUTTON_NAME) { io.AddKeyEvent(KEY_NO, gp.BUTTON_NAME.isPressed); }
|
||||||
|
#define MAP_ANALOG(KEY_NO, AXIS_NAME, V0, V1) { float vn = (float)(gp.AXIS_NAME.value - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); }
|
||||||
|
const float thumb_dead_zone = 0.0f;
|
||||||
|
|
||||||
|
#if APPLE_HAS_BUTTON_OPTIONS
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadBack, buttonOptions);
|
||||||
|
#endif
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceLeft, buttonX); // Xbox X, PS Square
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceRight, buttonB); // Xbox B, PS Circle
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceUp, buttonY); // Xbox Y, PS Triangle
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceDown, buttonA); // Xbox A, PS Cross
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadLeft, dpad.left);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadRight, dpad.right);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadUp, dpad.up);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadDown, dpad.down);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadL1, leftShoulder, 0.0f, 1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadR1, rightShoulder, 0.0f, 1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadL2, leftTrigger, 0.0f, 1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadR2, rightTrigger, 0.0f, 1.0f);
|
||||||
|
#if APPLE_HAS_THUMBSTICKS
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL3, leftThumbstickButton);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR3, rightThumbstickButton);
|
||||||
|
#endif
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickLeft, leftThumbstick.xAxis, -thumb_dead_zone, -1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickRight, leftThumbstick.xAxis, +thumb_dead_zone, +1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickUp, leftThumbstick.yAxis, +thumb_dead_zone, +1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickDown, leftThumbstick.yAxis, -thumb_dead_zone, -1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickLeft, rightThumbstick.xAxis, -thumb_dead_zone, -1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickRight, rightThumbstick.xAxis, +thumb_dead_zone, +1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickUp, rightThumbstick.yAxis, +thumb_dead_zone, +1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickDown, rightThumbstick.yAxis, -thumb_dead_zone, -1.0f);
|
||||||
|
#undef MAP_BUTTON
|
||||||
|
#undef MAP_ANALOG
|
||||||
|
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplOSX_UpdateImePosWithView(NSView* view)
|
||||||
|
{
|
||||||
|
ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (io.WantTextInput)
|
||||||
|
[bd->KeyEventResponder updateImePosWithView:view];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplOSX_NewFrame(NSView* view)
|
||||||
|
{
|
||||||
|
ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Setup display size
|
||||||
|
if (view)
|
||||||
|
{
|
||||||
|
const float dpi = (float)[view.window backingScaleFactor];
|
||||||
|
io.DisplaySize = ImVec2((float)view.bounds.size.width, (float)view.bounds.size.height);
|
||||||
|
io.DisplayFramebufferScale = ImVec2(dpi, dpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup time step
|
||||||
|
if (bd->Time == 0.0)
|
||||||
|
bd->Time = GetMachAbsoluteTimeInSeconds();
|
||||||
|
|
||||||
|
double current_time = GetMachAbsoluteTimeInSeconds();
|
||||||
|
io.DeltaTime = (float)(current_time - bd->Time);
|
||||||
|
bd->Time = current_time;
|
||||||
|
|
||||||
|
ImGui_ImplOSX_UpdateMouseCursor();
|
||||||
|
ImGui_ImplOSX_UpdateGamepads();
|
||||||
|
ImGui_ImplOSX_UpdateImePosWithView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must only be called for a mouse event, otherwise an exception occurs
|
||||||
|
// (Note that NSEventTypeScrollWheel is considered "other input". Oddly enough an exception does not occur with it, but the value will sometimes be wrong!)
|
||||||
|
static ImGuiMouseSource GetMouseSource(NSEvent* event)
|
||||||
|
{
|
||||||
|
switch (event.subtype)
|
||||||
|
{
|
||||||
|
case NSEventSubtypeTabletPoint:
|
||||||
|
return ImGuiMouseSource_Pen;
|
||||||
|
// macOS considers input from relative touch devices (like the trackpad or Apple Magic Mouse) to be touch input.
|
||||||
|
// This doesn't really make sense for Dear ImGui, which expects absolute touch devices only.
|
||||||
|
// There does not seem to be a simple way to disambiguate things here so we consider NSEventSubtypeTouch events to always come from mice.
|
||||||
|
// See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/HandlingTouchEvents/HandlingTouchEvents.html#//apple_ref/doc/uid/10000060i-CH13-SW24
|
||||||
|
//case NSEventSubtypeTouch:
|
||||||
|
// return ImGuiMouseSource_TouchScreen;
|
||||||
|
case NSEventSubtypeMouseEvent:
|
||||||
|
default:
|
||||||
|
return ImGuiMouseSource_Mouse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown)
|
||||||
|
{
|
||||||
|
int button = (int)[event buttonNumber];
|
||||||
|
if (button >= 0 && button < ImGuiMouseButton_COUNT)
|
||||||
|
{
|
||||||
|
io.AddMouseSourceEvent(GetMouseSource(event));
|
||||||
|
io.AddMouseButtonEvent(button, true);
|
||||||
|
}
|
||||||
|
return io.WantCaptureMouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == NSEventTypeLeftMouseUp || event.type == NSEventTypeRightMouseUp || event.type == NSEventTypeOtherMouseUp)
|
||||||
|
{
|
||||||
|
int button = (int)[event buttonNumber];
|
||||||
|
if (button >= 0 && button < ImGuiMouseButton_COUNT)
|
||||||
|
{
|
||||||
|
io.AddMouseSourceEvent(GetMouseSource(event));
|
||||||
|
io.AddMouseButtonEvent(button, false);
|
||||||
|
}
|
||||||
|
return io.WantCaptureMouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged || event.type == NSEventTypeRightMouseDragged || event.type == NSEventTypeOtherMouseDragged)
|
||||||
|
{
|
||||||
|
NSPoint mousePoint = event.locationInWindow;
|
||||||
|
if (event.window == nil)
|
||||||
|
mousePoint = [[view window] convertPointFromScreen:mousePoint];
|
||||||
|
mousePoint = [view convertPoint:mousePoint fromView:nil];
|
||||||
|
if ([view isFlipped])
|
||||||
|
mousePoint = NSMakePoint(mousePoint.x, mousePoint.y);
|
||||||
|
else
|
||||||
|
mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y);
|
||||||
|
io.AddMouseSourceEvent(GetMouseSource(event));
|
||||||
|
io.AddMousePosEvent((float)mousePoint.x, (float)mousePoint.y);
|
||||||
|
return io.WantCaptureMouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == NSEventTypeScrollWheel)
|
||||||
|
{
|
||||||
|
// Ignore canceled events.
|
||||||
|
//
|
||||||
|
// From macOS 12.1, scrolling with two fingers and then decelerating
|
||||||
|
// by tapping two fingers results in two events appearing:
|
||||||
|
//
|
||||||
|
// 1. A scroll wheel NSEvent, with a phase == NSEventPhaseMayBegin, when the user taps
|
||||||
|
// two fingers to decelerate or stop the scroll events.
|
||||||
|
//
|
||||||
|
// 2. A scroll wheel NSEvent, with a phase == NSEventPhaseCancelled, when the user releases the
|
||||||
|
// two-finger tap. It is this event that sometimes contains large values for scrollingDeltaX and
|
||||||
|
// scrollingDeltaY. When these are added to the current x and y positions of the scrolling view,
|
||||||
|
// it appears to jump up or down. It can be observed in Preview, various JetBrains IDEs and here.
|
||||||
|
if (event.phase == NSEventPhaseCancelled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double wheel_dx = 0.0;
|
||||||
|
double wheel_dy = 0.0;
|
||||||
|
|
||||||
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
|
||||||
|
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6)
|
||||||
|
{
|
||||||
|
wheel_dx = [event scrollingDeltaX];
|
||||||
|
wheel_dy = [event scrollingDeltaY];
|
||||||
|
if ([event hasPreciseScrollingDeltas])
|
||||||
|
{
|
||||||
|
wheel_dx *= 0.01;
|
||||||
|
wheel_dy *= 0.01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif // MAC_OS_X_VERSION_MAX_ALLOWED
|
||||||
|
{
|
||||||
|
wheel_dx = [event deltaX] * 0.1;
|
||||||
|
wheel_dy = [event deltaY] * 0.1;
|
||||||
|
}
|
||||||
|
if (wheel_dx != 0.0 || wheel_dy != 0.0)
|
||||||
|
io.AddMouseWheelEvent((float)wheel_dx, (float)wheel_dy);
|
||||||
|
|
||||||
|
return io.WantCaptureMouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp)
|
||||||
|
{
|
||||||
|
if ([event isARepeat])
|
||||||
|
return io.WantCaptureKeyboard;
|
||||||
|
|
||||||
|
int key_code = (int)[event keyCode];
|
||||||
|
ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code);
|
||||||
|
io.AddKeyEvent(key, event.type == NSEventTypeKeyDown);
|
||||||
|
io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code)
|
||||||
|
|
||||||
|
return io.WantCaptureKeyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == NSEventTypeFlagsChanged)
|
||||||
|
{
|
||||||
|
unsigned short key_code = [event keyCode];
|
||||||
|
NSEventModifierFlags modifier_flags = [event modifierFlags];
|
||||||
|
|
||||||
|
io.AddKeyEvent(ImGuiMod_Shift, (modifier_flags & NSEventModifierFlagShift) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Ctrl, (modifier_flags & NSEventModifierFlagControl) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Alt, (modifier_flags & NSEventModifierFlagOption) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Super, (modifier_flags & NSEventModifierFlagCommand) != 0);
|
||||||
|
|
||||||
|
ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code);
|
||||||
|
if (key != ImGuiKey_None)
|
||||||
|
{
|
||||||
|
// macOS does not generate down/up event for modifiers. We're trying
|
||||||
|
// to use hardware dependent masks to extract that information.
|
||||||
|
// 'imgui_mask' is left as a fallback.
|
||||||
|
NSEventModifierFlags mask = 0;
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case ImGuiKey_LeftCtrl: mask = 0x0001; break;
|
||||||
|
case ImGuiKey_RightCtrl: mask = 0x2000; break;
|
||||||
|
case ImGuiKey_LeftShift: mask = 0x0002; break;
|
||||||
|
case ImGuiKey_RightShift: mask = 0x0004; break;
|
||||||
|
case ImGuiKey_LeftSuper: mask = 0x0008; break;
|
||||||
|
case ImGuiKey_RightSuper: mask = 0x0010; break;
|
||||||
|
case ImGuiKey_LeftAlt: mask = 0x0020; break;
|
||||||
|
case ImGuiKey_RightAlt: mask = 0x0040; break;
|
||||||
|
default:
|
||||||
|
return io.WantCaptureKeyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSEventModifierFlags modifier_flags = [event modifierFlags];
|
||||||
|
io.AddKeyEvent(key, (modifier_flags & mask) != 0);
|
||||||
|
io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code)
|
||||||
|
}
|
||||||
|
|
||||||
|
return io.WantCaptureKeyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplOSX_AddTrackingArea(NSView* _Nonnull view)
|
||||||
|
{
|
||||||
|
// If we want to receive key events, we either need to be in the responder chain of the key view,
|
||||||
|
// or else we can install a local monitor. The consequence of this heavy-handed approach is that
|
||||||
|
// we receive events for all controls, not just Dear ImGui widgets. If we had native controls in our
|
||||||
|
// window, we'd want to be much more careful than just ingesting the complete event stream.
|
||||||
|
// To match the behavior of other backends, we pass every event down to the OS.
|
||||||
|
ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData();
|
||||||
|
if (bd->Monitor)
|
||||||
|
return;
|
||||||
|
NSEventMask eventMask = 0;
|
||||||
|
eventMask |= NSEventMaskMouseMoved | NSEventMaskScrollWheel;
|
||||||
|
eventMask |= NSEventMaskLeftMouseDown | NSEventMaskLeftMouseUp | NSEventMaskLeftMouseDragged;
|
||||||
|
eventMask |= NSEventMaskRightMouseDown | NSEventMaskRightMouseUp | NSEventMaskRightMouseDragged;
|
||||||
|
eventMask |= NSEventMaskOtherMouseDown | NSEventMaskOtherMouseUp | NSEventMaskOtherMouseDragged;
|
||||||
|
eventMask |= NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged;
|
||||||
|
bd->Monitor = [NSEvent addLocalMonitorForEventsMatchingMask:eventMask
|
||||||
|
handler:^NSEvent* _Nullable(NSEvent* event)
|
||||||
|
{
|
||||||
|
ImGui_ImplOSX_HandleEvent(event, view);
|
||||||
|
return event;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
674
backends/imgui_impl_sdl2.cpp
Normal file
674
backends/imgui_impl_sdl2.cpp
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
// dear imgui: Platform Backend 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.)
|
||||||
|
// (Prefer SDL 2.0.5+ for full feature support.)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support.
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
// [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
|
||||||
|
// 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306)
|
||||||
|
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702)
|
||||||
|
// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)
|
||||||
|
// 2023-02-07: Implement IME handler (io.SetPlatformImeDataFn will call SDL_SetTextInputRect()/SDL_StartTextInput()).
|
||||||
|
// 2023-02-07: *BREAKING CHANGE* Renamed this backend file from imgui_impl_sdl.cpp/.h to imgui_impl_sdl2.cpp/.h in prevision for the future release of SDL3.
|
||||||
|
// 2023-02-02: Avoid calling SDL_SetCursor() when cursor has not changed, as the function is surprisingly costly on Mac with latest SDL (may be fixed in next SDL version).
|
||||||
|
// 2023-02-02: Added support for SDL 2.0.18+ preciseX/preciseY mouse wheel data for smooth scrolling + Scaling X value on Emscripten (bug?). (#4019, #6096)
|
||||||
|
// 2023-02-02: Removed SDL_MOUSEWHEEL value clamping, as values seem correct in latest Emscripten. (#4019)
|
||||||
|
// 2023-02-01: Flipping SDL_MOUSEWHEEL 'wheel.x' value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
|
||||||
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||||
|
// 2022-09-26: Inputs: Disable SDL 2.0.22 new "auto capture" (SDL_HINT_MOUSE_AUTO_CAPTURE) which prevents drag and drop across windows for multi-viewport support + don't capture when drag and dropping. (#5710)
|
||||||
|
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
|
||||||
|
// 2022-03-22: Inputs: Fix mouse position issues when dragging outside of boundaries. SDL_CaptureMouse() erroneously still gives out LEAVE events when hovering OS decorations.
|
||||||
|
// 2022-03-22: Inputs: Added support for extra mouse buttons (SDL_BUTTON_X1/SDL_BUTTON_X2).
|
||||||
|
// 2022-02-04: Added SDL_Renderer* parameter to ImGui_ImplSDL2_InitForSDLRenderer(), so we can use SDL_GetRendererOutputSize() instead of SDL_GL_GetDrawableSize() when bound to a SDL_Renderer.
|
||||||
|
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
|
||||||
|
// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
|
||||||
|
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
|
||||||
|
// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates.
|
||||||
|
// 2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API.
|
||||||
|
// 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted.
|
||||||
|
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
|
||||||
|
// 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST.
|
||||||
|
// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+)
|
||||||
|
// 2021-06-29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary.
|
||||||
|
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
|
// 2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950)
|
||||||
|
// 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends.
|
||||||
|
// 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"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_sdl2.h"
|
||||||
|
|
||||||
|
// Clang warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SDL
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <SDL_syswm.h>
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)
|
||||||
|
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1
|
||||||
|
#else
|
||||||
|
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0
|
||||||
|
#endif
|
||||||
|
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
|
||||||
|
|
||||||
|
// SDL Data
|
||||||
|
struct ImGui_ImplSDL2_Data
|
||||||
|
{
|
||||||
|
SDL_Window* Window;
|
||||||
|
SDL_Renderer* Renderer;
|
||||||
|
Uint64 Time;
|
||||||
|
Uint32 MouseWindowID;
|
||||||
|
int MouseButtonsDown;
|
||||||
|
SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
|
||||||
|
SDL_Cursor* LastMouseCursor;
|
||||||
|
int PendingMouseLeaveFrame;
|
||||||
|
char* ClipboardTextData;
|
||||||
|
bool MouseCanUseGlobalState;
|
||||||
|
|
||||||
|
ImGui_ImplSDL2_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
|
||||||
|
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
|
||||||
|
static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplSDL2_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
static const char* ImGui_ImplSDL2_GetClipboardText(void*)
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
|
||||||
|
if (bd->ClipboardTextData)
|
||||||
|
SDL_free(bd->ClipboardTextData);
|
||||||
|
bd->ClipboardTextData = SDL_GetClipboardText();
|
||||||
|
return bd->ClipboardTextData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
|
||||||
|
{
|
||||||
|
SDL_SetClipboardText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: native IME will only display if user calls SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1") _before_ SDL_CreateWindow().
|
||||||
|
static void ImGui_ImplSDL2_SetPlatformImeData(ImGuiViewport*, ImGuiPlatformImeData* data)
|
||||||
|
{
|
||||||
|
if (data->WantVisible)
|
||||||
|
{
|
||||||
|
SDL_Rect r;
|
||||||
|
r.x = (int)data->InputPos.x;
|
||||||
|
r.y = (int)data->InputPos.y;
|
||||||
|
r.w = 1;
|
||||||
|
r.h = (int)data->InputLineHeight;
|
||||||
|
SDL_SetTextInputRect(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode)
|
||||||
|
{
|
||||||
|
switch (keycode)
|
||||||
|
{
|
||||||
|
case SDLK_TAB: return ImGuiKey_Tab;
|
||||||
|
case SDLK_LEFT: return ImGuiKey_LeftArrow;
|
||||||
|
case SDLK_RIGHT: return ImGuiKey_RightArrow;
|
||||||
|
case SDLK_UP: return ImGuiKey_UpArrow;
|
||||||
|
case SDLK_DOWN: return ImGuiKey_DownArrow;
|
||||||
|
case SDLK_PAGEUP: return ImGuiKey_PageUp;
|
||||||
|
case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
|
||||||
|
case SDLK_HOME: return ImGuiKey_Home;
|
||||||
|
case SDLK_END: return ImGuiKey_End;
|
||||||
|
case SDLK_INSERT: return ImGuiKey_Insert;
|
||||||
|
case SDLK_DELETE: return ImGuiKey_Delete;
|
||||||
|
case SDLK_BACKSPACE: return ImGuiKey_Backspace;
|
||||||
|
case SDLK_SPACE: return ImGuiKey_Space;
|
||||||
|
case SDLK_RETURN: return ImGuiKey_Enter;
|
||||||
|
case SDLK_ESCAPE: return ImGuiKey_Escape;
|
||||||
|
case SDLK_QUOTE: return ImGuiKey_Apostrophe;
|
||||||
|
case SDLK_COMMA: return ImGuiKey_Comma;
|
||||||
|
case SDLK_MINUS: return ImGuiKey_Minus;
|
||||||
|
case SDLK_PERIOD: return ImGuiKey_Period;
|
||||||
|
case SDLK_SLASH: return ImGuiKey_Slash;
|
||||||
|
case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
|
||||||
|
case SDLK_EQUALS: return ImGuiKey_Equal;
|
||||||
|
case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
|
||||||
|
case SDLK_BACKSLASH: return ImGuiKey_Backslash;
|
||||||
|
case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
|
||||||
|
case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent;
|
||||||
|
case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
|
||||||
|
case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
|
||||||
|
case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
|
||||||
|
case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
|
||||||
|
case SDLK_PAUSE: return ImGuiKey_Pause;
|
||||||
|
case SDLK_KP_0: return ImGuiKey_Keypad0;
|
||||||
|
case SDLK_KP_1: return ImGuiKey_Keypad1;
|
||||||
|
case SDLK_KP_2: return ImGuiKey_Keypad2;
|
||||||
|
case SDLK_KP_3: return ImGuiKey_Keypad3;
|
||||||
|
case SDLK_KP_4: return ImGuiKey_Keypad4;
|
||||||
|
case SDLK_KP_5: return ImGuiKey_Keypad5;
|
||||||
|
case SDLK_KP_6: return ImGuiKey_Keypad6;
|
||||||
|
case SDLK_KP_7: return ImGuiKey_Keypad7;
|
||||||
|
case SDLK_KP_8: return ImGuiKey_Keypad8;
|
||||||
|
case SDLK_KP_9: return ImGuiKey_Keypad9;
|
||||||
|
case SDLK_KP_PERIOD: return ImGuiKey_KeypadDecimal;
|
||||||
|
case SDLK_KP_DIVIDE: return ImGuiKey_KeypadDivide;
|
||||||
|
case SDLK_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
|
||||||
|
case SDLK_KP_MINUS: return ImGuiKey_KeypadSubtract;
|
||||||
|
case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd;
|
||||||
|
case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter;
|
||||||
|
case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual;
|
||||||
|
case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
|
||||||
|
case SDLK_LSHIFT: return ImGuiKey_LeftShift;
|
||||||
|
case SDLK_LALT: return ImGuiKey_LeftAlt;
|
||||||
|
case SDLK_LGUI: return ImGuiKey_LeftSuper;
|
||||||
|
case SDLK_RCTRL: return ImGuiKey_RightCtrl;
|
||||||
|
case SDLK_RSHIFT: return ImGuiKey_RightShift;
|
||||||
|
case SDLK_RALT: return ImGuiKey_RightAlt;
|
||||||
|
case SDLK_RGUI: return ImGuiKey_RightSuper;
|
||||||
|
case SDLK_APPLICATION: return ImGuiKey_Menu;
|
||||||
|
case SDLK_0: return ImGuiKey_0;
|
||||||
|
case SDLK_1: return ImGuiKey_1;
|
||||||
|
case SDLK_2: return ImGuiKey_2;
|
||||||
|
case SDLK_3: return ImGuiKey_3;
|
||||||
|
case SDLK_4: return ImGuiKey_4;
|
||||||
|
case SDLK_5: return ImGuiKey_5;
|
||||||
|
case SDLK_6: return ImGuiKey_6;
|
||||||
|
case SDLK_7: return ImGuiKey_7;
|
||||||
|
case SDLK_8: return ImGuiKey_8;
|
||||||
|
case SDLK_9: return ImGuiKey_9;
|
||||||
|
case SDLK_a: return ImGuiKey_A;
|
||||||
|
case SDLK_b: return ImGuiKey_B;
|
||||||
|
case SDLK_c: return ImGuiKey_C;
|
||||||
|
case SDLK_d: return ImGuiKey_D;
|
||||||
|
case SDLK_e: return ImGuiKey_E;
|
||||||
|
case SDLK_f: return ImGuiKey_F;
|
||||||
|
case SDLK_g: return ImGuiKey_G;
|
||||||
|
case SDLK_h: return ImGuiKey_H;
|
||||||
|
case SDLK_i: return ImGuiKey_I;
|
||||||
|
case SDLK_j: return ImGuiKey_J;
|
||||||
|
case SDLK_k: return ImGuiKey_K;
|
||||||
|
case SDLK_l: return ImGuiKey_L;
|
||||||
|
case SDLK_m: return ImGuiKey_M;
|
||||||
|
case SDLK_n: return ImGuiKey_N;
|
||||||
|
case SDLK_o: return ImGuiKey_O;
|
||||||
|
case SDLK_p: return ImGuiKey_P;
|
||||||
|
case SDLK_q: return ImGuiKey_Q;
|
||||||
|
case SDLK_r: return ImGuiKey_R;
|
||||||
|
case SDLK_s: return ImGuiKey_S;
|
||||||
|
case SDLK_t: return ImGuiKey_T;
|
||||||
|
case SDLK_u: return ImGuiKey_U;
|
||||||
|
case SDLK_v: return ImGuiKey_V;
|
||||||
|
case SDLK_w: return ImGuiKey_W;
|
||||||
|
case SDLK_x: return ImGuiKey_X;
|
||||||
|
case SDLK_y: return ImGuiKey_Y;
|
||||||
|
case SDLK_z: return ImGuiKey_Z;
|
||||||
|
case SDLK_F1: return ImGuiKey_F1;
|
||||||
|
case SDLK_F2: return ImGuiKey_F2;
|
||||||
|
case SDLK_F3: return ImGuiKey_F3;
|
||||||
|
case SDLK_F4: return ImGuiKey_F4;
|
||||||
|
case SDLK_F5: return ImGuiKey_F5;
|
||||||
|
case SDLK_F6: return ImGuiKey_F6;
|
||||||
|
case SDLK_F7: return ImGuiKey_F7;
|
||||||
|
case SDLK_F8: return ImGuiKey_F8;
|
||||||
|
case SDLK_F9: return ImGuiKey_F9;
|
||||||
|
case SDLK_F10: return ImGuiKey_F10;
|
||||||
|
case SDLK_F11: return ImGuiKey_F11;
|
||||||
|
case SDLK_F12: return ImGuiKey_F12;
|
||||||
|
case SDLK_F13: return ImGuiKey_F13;
|
||||||
|
case SDLK_F14: return ImGuiKey_F14;
|
||||||
|
case SDLK_F15: return ImGuiKey_F15;
|
||||||
|
case SDLK_F16: return ImGuiKey_F16;
|
||||||
|
case SDLK_F17: return ImGuiKey_F17;
|
||||||
|
case SDLK_F18: return ImGuiKey_F18;
|
||||||
|
case SDLK_F19: return ImGuiKey_F19;
|
||||||
|
case SDLK_F20: return ImGuiKey_F20;
|
||||||
|
case SDLK_F21: return ImGuiKey_F21;
|
||||||
|
case SDLK_F22: return ImGuiKey_F22;
|
||||||
|
case SDLK_F23: return ImGuiKey_F23;
|
||||||
|
case SDLK_F24: return ImGuiKey_F24;
|
||||||
|
case SDLK_AC_BACK: return ImGuiKey_AppBack;
|
||||||
|
case SDLK_AC_FORWARD: return ImGuiKey_AppForward;
|
||||||
|
}
|
||||||
|
return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & KMOD_CTRL) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & KMOD_SHIFT) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & KMOD_ALT) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||||
|
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
||||||
|
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
||||||
|
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||||
|
// 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();
|
||||||
|
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
|
||||||
|
|
||||||
|
switch (event->type)
|
||||||
|
{
|
||||||
|
case SDL_MOUSEMOTION:
|
||||||
|
{
|
||||||
|
ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
|
||||||
|
io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||||
|
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_MOUSEWHEEL:
|
||||||
|
{
|
||||||
|
//IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
|
||||||
|
#if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten!
|
||||||
|
float wheel_x = -event->wheel.preciseX;
|
||||||
|
float wheel_y = event->wheel.preciseY;
|
||||||
|
#else
|
||||||
|
float wheel_x = -(float)event->wheel.x;
|
||||||
|
float wheel_y = (float)event->wheel.y;
|
||||||
|
#endif
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
wheel_x /= 100.0f;
|
||||||
|
#endif
|
||||||
|
io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||||
|
io.AddMouseWheelEvent(wheel_x, wheel_y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
|
case SDL_MOUSEBUTTONUP:
|
||||||
|
{
|
||||||
|
int mouse_button = -1;
|
||||||
|
if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
|
||||||
|
if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
|
||||||
|
if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
|
||||||
|
if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
|
||||||
|
if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
|
||||||
|
if (mouse_button == -1)
|
||||||
|
break;
|
||||||
|
io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||||
|
io.AddMouseButtonEvent(mouse_button, (event->type == SDL_MOUSEBUTTONDOWN));
|
||||||
|
bd->MouseButtonsDown = (event->type == SDL_MOUSEBUTTONDOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_TEXTINPUT:
|
||||||
|
{
|
||||||
|
io.AddInputCharactersUTF8(event->text.text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
case SDL_KEYUP:
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);
|
||||||
|
ImGuiKey key = ImGui_ImplSDL2_KeycodeToImGuiKey(event->key.keysym.sym);
|
||||||
|
io.AddKeyEvent(key, (event->type == SDL_KEYDOWN));
|
||||||
|
io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_WINDOWEVENT:
|
||||||
|
{
|
||||||
|
// - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right.
|
||||||
|
// - However we won't get a correct LEAVE event for a captured window.
|
||||||
|
// - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
|
||||||
|
// causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
|
||||||
|
// we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
|
||||||
|
Uint8 window_event = event->window.event;
|
||||||
|
if (window_event == SDL_WINDOWEVENT_ENTER)
|
||||||
|
{
|
||||||
|
bd->MouseWindowID = event->window.windowID;
|
||||||
|
bd->PendingMouseLeaveFrame = 0;
|
||||||
|
}
|
||||||
|
if (window_event == SDL_WINDOWEVENT_LEAVE)
|
||||||
|
bd->PendingMouseLeaveFrame = ImGui::GetFrameCount() + 1;
|
||||||
|
if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED)
|
||||||
|
io.AddFocusEvent(true);
|
||||||
|
else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST)
|
||||||
|
io.AddFocusEvent(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||||
|
|
||||||
|
// Check and store if we are on a SDL backend that supports global mouse position
|
||||||
|
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
|
||||||
|
bool mouse_can_use_global_state = false;
|
||||||
|
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
|
||||||
|
const char* sdl_backend = SDL_GetCurrentVideoDriver();
|
||||||
|
const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
|
||||||
|
for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++)
|
||||||
|
if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)
|
||||||
|
mouse_can_use_global_state = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)();
|
||||||
|
io.BackendPlatformUserData = (void*)bd;
|
||||||
|
io.BackendPlatformName = "imgui_impl_sdl2";
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||||
|
|
||||||
|
bd->Window = window;
|
||||||
|
bd->Renderer = renderer;
|
||||||
|
bd->MouseCanUseGlobalState = mouse_can_use_global_state;
|
||||||
|
|
||||||
|
io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
|
||||||
|
io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
|
||||||
|
io.ClipboardUserData = nullptr;
|
||||||
|
io.SetPlatformImeDataFn = ImGui_ImplSDL2_SetPlatformImeData;
|
||||||
|
|
||||||
|
// Load mouse cursors
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
|
||||||
|
|
||||||
|
// Set platform dependent data in viewport
|
||||||
|
// Our mouse update function expect PlatformHandle to be filled for the main viewport
|
||||||
|
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||||
|
main_viewport->PlatformHandleRaw = nullptr;
|
||||||
|
SDL_SysWMinfo info;
|
||||||
|
SDL_VERSION(&info.version);
|
||||||
|
if (SDL_GetWindowWMInfo(window, &info))
|
||||||
|
{
|
||||||
|
#if defined(SDL_VIDEO_DRIVER_WINDOWS)
|
||||||
|
main_viewport->PlatformHandleRaw = (void*)info.info.win.window;
|
||||||
|
#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
|
||||||
|
main_viewport->PlatformHandleRaw = (void*)info.info.cocoa.window;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
|
||||||
|
// Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
|
||||||
|
// (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
|
||||||
|
// It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
|
||||||
|
// you can ignore SDL_MOUSEBUTTONDOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
|
||||||
|
#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
|
||||||
|
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// From 2.0.18: Enable native IME.
|
||||||
|
// IMPORTANT: This is used at the time of SDL_CreateWindow() so this will only affects secondary windows, if any.
|
||||||
|
// For the main window to be affected, your application needs to call this manually before calling SDL_CreateWindow().
|
||||||
|
#ifdef SDL_HINT_IME_SHOW_UI
|
||||||
|
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
|
||||||
|
#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
|
||||||
|
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
|
||||||
|
{
|
||||||
|
IM_UNUSED(sdl_gl_context); // Viewport branch will need this.
|
||||||
|
return ImGui_ImplSDL2_Init(window, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
|
||||||
|
{
|
||||||
|
#if !SDL_HAS_VULKAN
|
||||||
|
IM_ASSERT(0 && "Unsupported");
|
||||||
|
#endif
|
||||||
|
return ImGui_ImplSDL2_Init(window, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
|
||||||
|
{
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
IM_ASSERT(0 && "Unsupported");
|
||||||
|
#endif
|
||||||
|
return ImGui_ImplSDL2_Init(window, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
|
||||||
|
{
|
||||||
|
return ImGui_ImplSDL2_Init(window, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
|
||||||
|
{
|
||||||
|
return ImGui_ImplSDL2_Init(window, renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL2_InitForOther(SDL_Window* window)
|
||||||
|
{
|
||||||
|
return ImGui_ImplSDL2_Init(window, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDL2_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
if (bd->ClipboardTextData)
|
||||||
|
SDL_free(bd->ClipboardTextData);
|
||||||
|
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
|
||||||
|
SDL_FreeCursor(bd->MouseCursors[cursor_n]);
|
||||||
|
bd->LastMouseCursor = nullptr;
|
||||||
|
|
||||||
|
io.BackendPlatformName = nullptr;
|
||||||
|
io.BackendPlatformUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL2_UpdateMouseData()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// We forward mouse input when hovered or captured (via SDL_MOUSEMOTION) or when focused (below)
|
||||||
|
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
|
||||||
|
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside
|
||||||
|
SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE);
|
||||||
|
SDL_Window* focused_window = SDL_GetKeyboardFocus();
|
||||||
|
const bool is_app_focused = (bd->Window == focused_window);
|
||||||
|
#else
|
||||||
|
const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only
|
||||||
|
#endif
|
||||||
|
if (is_app_focused)
|
||||||
|
{
|
||||||
|
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||||
|
if (io.WantSetMousePos)
|
||||||
|
SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y);
|
||||||
|
|
||||||
|
// (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured)
|
||||||
|
if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0)
|
||||||
|
{
|
||||||
|
int window_x, window_y, mouse_x_global, mouse_y_global;
|
||||||
|
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
|
||||||
|
SDL_GetWindowPosition(bd->Window, &window_x, &window_y);
|
||||||
|
io.AddMousePosEvent((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL2_UpdateMouseCursor()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||||
|
return;
|
||||||
|
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
|
||||||
|
|
||||||
|
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_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];
|
||||||
|
if (bd->LastMouseCursor != expected_cursor)
|
||||||
|
{
|
||||||
|
SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
|
||||||
|
bd->LastMouseCursor = expected_cursor;
|
||||||
|
}
|
||||||
|
SDL_ShowCursor(SDL_TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL2_UpdateGamepads()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get gamepad
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||||
|
SDL_GameController* game_controller = SDL_GameControllerOpen(0);
|
||||||
|
if (!game_controller)
|
||||||
|
return;
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||||
|
|
||||||
|
// Update gamepad inputs
|
||||||
|
#define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)
|
||||||
|
#define MAP_BUTTON(KEY_NO, BUTTON_NO) { io.AddKeyEvent(KEY_NO, SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0); }
|
||||||
|
#define MAP_ANALOG(KEY_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); }
|
||||||
|
const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadStart, SDL_CONTROLLER_BUTTON_START);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadBack, SDL_CONTROLLER_BUTTON_BACK);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceLeft, SDL_CONTROLLER_BUTTON_X); // Xbox X, PS Square
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceRight, SDL_CONTROLLER_BUTTON_B); // Xbox B, PS Circle
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceUp, SDL_CONTROLLER_BUTTON_Y); // Xbox Y, PS Triangle
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceDown, SDL_CONTROLLER_BUTTON_A); // Xbox A, PS Cross
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL1, SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR1, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadL2, SDL_CONTROLLER_AXIS_TRIGGERLEFT, 0.0f, 32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadR2, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 0.0f, 32767);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL3, SDL_CONTROLLER_BUTTON_LEFTSTICK);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR3, SDL_CONTROLLER_BUTTON_RIGHTSTICK);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickLeft, SDL_CONTROLLER_AXIS_RIGHTX, -thumb_dead_zone, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickRight, SDL_CONTROLLER_AXIS_RIGHTX, +thumb_dead_zone, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickUp, SDL_CONTROLLER_AXIS_RIGHTY, -thumb_dead_zone, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickDown, SDL_CONTROLLER_AXIS_RIGHTY, +thumb_dead_zone, +32767);
|
||||||
|
#undef MAP_BUTTON
|
||||||
|
#undef MAP_ANALOG
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDL2_NewFrame()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplSDL2_Init()?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Setup display size (every frame to accommodate for window resizing)
|
||||||
|
int w, h;
|
||||||
|
int display_w, display_h;
|
||||||
|
SDL_GetWindowSize(bd->Window, &w, &h);
|
||||||
|
if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED)
|
||||||
|
w = h = 0;
|
||||||
|
if (bd->Renderer != nullptr)
|
||||||
|
SDL_GetRendererOutputSize(bd->Renderer, &display_w, &display_h);
|
||||||
|
else
|
||||||
|
SDL_GL_GetDrawableSize(bd->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)
|
||||||
|
// (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)
|
||||||
|
static Uint64 frequency = SDL_GetPerformanceFrequency();
|
||||||
|
Uint64 current_time = SDL_GetPerformanceCounter();
|
||||||
|
if (current_time <= bd->Time)
|
||||||
|
current_time = bd->Time + 1;
|
||||||
|
io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
|
||||||
|
bd->Time = current_time;
|
||||||
|
|
||||||
|
if (bd->PendingMouseLeaveFrame && bd->PendingMouseLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
|
||||||
|
{
|
||||||
|
bd->MouseWindowID = 0;
|
||||||
|
bd->PendingMouseLeaveFrame = 0;
|
||||||
|
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplSDL2_UpdateMouseData();
|
||||||
|
ImGui_ImplSDL2_UpdateMouseCursor();
|
||||||
|
|
||||||
|
// Update game controllers (if enabled and available)
|
||||||
|
ImGui_ImplSDL2_UpdateGamepads();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
43
backends/imgui_impl_sdl2.h
Normal file
43
backends/imgui_impl_sdl2.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// dear imgui: Platform Backend 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: Clipboard support.
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
// [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
struct SDL_Window;
|
||||||
|
struct SDL_Renderer;
|
||||||
|
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 bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOther(SDL_Window* window);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
|
||||||
|
|
||||||
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
static inline void ImGui_ImplSDL2_NewFrame(SDL_Window*) { ImGui_ImplSDL2_NewFrame(); } // 1.84: removed unnecessary parameter
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
617
backends/imgui_impl_sdl3.cpp
Normal file
617
backends/imgui_impl_sdl3.cpp
Normal file
@ -0,0 +1,617 @@
|
|||||||
|
// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*)
|
||||||
|
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||||
|
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||||
|
// (IMPORTANT: SDL 3.0.0 is NOT YET RELEASED. IT IS POSSIBLE THAT ITS SPECS/API WILL CHANGE BEFORE RELEASE)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support.
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
// Missing features:
|
||||||
|
// [x] Platform: Basic IME support. Position somehow broken in SDL3 + app needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2023-11-13: Updated for recent SDL3 API changes.
|
||||||
|
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
|
||||||
|
// 2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391)
|
||||||
|
// 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306)
|
||||||
|
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702)
|
||||||
|
// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)
|
||||||
|
// 2023-02-07: Forked "imgui_impl_sdl2" into "imgui_impl_sdl3". Removed version checks for old feature. Refer to imgui_impl_sdl2.cpp for older changelog.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_sdl3.h"
|
||||||
|
|
||||||
|
// Clang warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SDL
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)
|
||||||
|
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1
|
||||||
|
#else
|
||||||
|
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SDL Data
|
||||||
|
struct ImGui_ImplSDL3_Data
|
||||||
|
{
|
||||||
|
SDL_Window* Window;
|
||||||
|
SDL_Renderer* Renderer;
|
||||||
|
Uint64 Time;
|
||||||
|
Uint32 MouseWindowID;
|
||||||
|
int MouseButtonsDown;
|
||||||
|
SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
|
||||||
|
SDL_Cursor* LastMouseCursor;
|
||||||
|
int PendingMouseLeaveFrame;
|
||||||
|
char* ClipboardTextData;
|
||||||
|
bool MouseCanUseGlobalState;
|
||||||
|
|
||||||
|
ImGui_ImplSDL3_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
|
||||||
|
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
|
||||||
|
static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
static const char* ImGui_ImplSDL3_GetClipboardText(void*)
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||||
|
if (bd->ClipboardTextData)
|
||||||
|
SDL_free(bd->ClipboardTextData);
|
||||||
|
bd->ClipboardTextData = SDL_GetClipboardText();
|
||||||
|
return bd->ClipboardTextData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL3_SetClipboardText(void*, const char* text)
|
||||||
|
{
|
||||||
|
SDL_SetClipboardText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL3_SetPlatformImeData(ImGuiViewport*, ImGuiPlatformImeData* data)
|
||||||
|
{
|
||||||
|
if (data->WantVisible)
|
||||||
|
{
|
||||||
|
SDL_Rect r;
|
||||||
|
r.x = (int)data->InputPos.x;
|
||||||
|
r.y = (int)data->InputPos.y;
|
||||||
|
r.w = 1;
|
||||||
|
r.h = (int)data->InputLineHeight;
|
||||||
|
SDL_SetTextInputRect(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImGuiKey ImGui_ImplSDL3_KeycodeToImGuiKey(int keycode)
|
||||||
|
{
|
||||||
|
switch (keycode)
|
||||||
|
{
|
||||||
|
case SDLK_TAB: return ImGuiKey_Tab;
|
||||||
|
case SDLK_LEFT: return ImGuiKey_LeftArrow;
|
||||||
|
case SDLK_RIGHT: return ImGuiKey_RightArrow;
|
||||||
|
case SDLK_UP: return ImGuiKey_UpArrow;
|
||||||
|
case SDLK_DOWN: return ImGuiKey_DownArrow;
|
||||||
|
case SDLK_PAGEUP: return ImGuiKey_PageUp;
|
||||||
|
case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
|
||||||
|
case SDLK_HOME: return ImGuiKey_Home;
|
||||||
|
case SDLK_END: return ImGuiKey_End;
|
||||||
|
case SDLK_INSERT: return ImGuiKey_Insert;
|
||||||
|
case SDLK_DELETE: return ImGuiKey_Delete;
|
||||||
|
case SDLK_BACKSPACE: return ImGuiKey_Backspace;
|
||||||
|
case SDLK_SPACE: return ImGuiKey_Space;
|
||||||
|
case SDLK_RETURN: return ImGuiKey_Enter;
|
||||||
|
case SDLK_ESCAPE: return ImGuiKey_Escape;
|
||||||
|
case SDLK_QUOTE: return ImGuiKey_Apostrophe;
|
||||||
|
case SDLK_COMMA: return ImGuiKey_Comma;
|
||||||
|
case SDLK_MINUS: return ImGuiKey_Minus;
|
||||||
|
case SDLK_PERIOD: return ImGuiKey_Period;
|
||||||
|
case SDLK_SLASH: return ImGuiKey_Slash;
|
||||||
|
case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
|
||||||
|
case SDLK_EQUALS: return ImGuiKey_Equal;
|
||||||
|
case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
|
||||||
|
case SDLK_BACKSLASH: return ImGuiKey_Backslash;
|
||||||
|
case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
|
||||||
|
case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent;
|
||||||
|
case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
|
||||||
|
case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
|
||||||
|
case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
|
||||||
|
case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
|
||||||
|
case SDLK_PAUSE: return ImGuiKey_Pause;
|
||||||
|
case SDLK_KP_0: return ImGuiKey_Keypad0;
|
||||||
|
case SDLK_KP_1: return ImGuiKey_Keypad1;
|
||||||
|
case SDLK_KP_2: return ImGuiKey_Keypad2;
|
||||||
|
case SDLK_KP_3: return ImGuiKey_Keypad3;
|
||||||
|
case SDLK_KP_4: return ImGuiKey_Keypad4;
|
||||||
|
case SDLK_KP_5: return ImGuiKey_Keypad5;
|
||||||
|
case SDLK_KP_6: return ImGuiKey_Keypad6;
|
||||||
|
case SDLK_KP_7: return ImGuiKey_Keypad7;
|
||||||
|
case SDLK_KP_8: return ImGuiKey_Keypad8;
|
||||||
|
case SDLK_KP_9: return ImGuiKey_Keypad9;
|
||||||
|
case SDLK_KP_PERIOD: return ImGuiKey_KeypadDecimal;
|
||||||
|
case SDLK_KP_DIVIDE: return ImGuiKey_KeypadDivide;
|
||||||
|
case SDLK_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
|
||||||
|
case SDLK_KP_MINUS: return ImGuiKey_KeypadSubtract;
|
||||||
|
case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd;
|
||||||
|
case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter;
|
||||||
|
case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual;
|
||||||
|
case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
|
||||||
|
case SDLK_LSHIFT: return ImGuiKey_LeftShift;
|
||||||
|
case SDLK_LALT: return ImGuiKey_LeftAlt;
|
||||||
|
case SDLK_LGUI: return ImGuiKey_LeftSuper;
|
||||||
|
case SDLK_RCTRL: return ImGuiKey_RightCtrl;
|
||||||
|
case SDLK_RSHIFT: return ImGuiKey_RightShift;
|
||||||
|
case SDLK_RALT: return ImGuiKey_RightAlt;
|
||||||
|
case SDLK_RGUI: return ImGuiKey_RightSuper;
|
||||||
|
case SDLK_APPLICATION: return ImGuiKey_Menu;
|
||||||
|
case SDLK_0: return ImGuiKey_0;
|
||||||
|
case SDLK_1: return ImGuiKey_1;
|
||||||
|
case SDLK_2: return ImGuiKey_2;
|
||||||
|
case SDLK_3: return ImGuiKey_3;
|
||||||
|
case SDLK_4: return ImGuiKey_4;
|
||||||
|
case SDLK_5: return ImGuiKey_5;
|
||||||
|
case SDLK_6: return ImGuiKey_6;
|
||||||
|
case SDLK_7: return ImGuiKey_7;
|
||||||
|
case SDLK_8: return ImGuiKey_8;
|
||||||
|
case SDLK_9: return ImGuiKey_9;
|
||||||
|
case SDLK_a: return ImGuiKey_A;
|
||||||
|
case SDLK_b: return ImGuiKey_B;
|
||||||
|
case SDLK_c: return ImGuiKey_C;
|
||||||
|
case SDLK_d: return ImGuiKey_D;
|
||||||
|
case SDLK_e: return ImGuiKey_E;
|
||||||
|
case SDLK_f: return ImGuiKey_F;
|
||||||
|
case SDLK_g: return ImGuiKey_G;
|
||||||
|
case SDLK_h: return ImGuiKey_H;
|
||||||
|
case SDLK_i: return ImGuiKey_I;
|
||||||
|
case SDLK_j: return ImGuiKey_J;
|
||||||
|
case SDLK_k: return ImGuiKey_K;
|
||||||
|
case SDLK_l: return ImGuiKey_L;
|
||||||
|
case SDLK_m: return ImGuiKey_M;
|
||||||
|
case SDLK_n: return ImGuiKey_N;
|
||||||
|
case SDLK_o: return ImGuiKey_O;
|
||||||
|
case SDLK_p: return ImGuiKey_P;
|
||||||
|
case SDLK_q: return ImGuiKey_Q;
|
||||||
|
case SDLK_r: return ImGuiKey_R;
|
||||||
|
case SDLK_s: return ImGuiKey_S;
|
||||||
|
case SDLK_t: return ImGuiKey_T;
|
||||||
|
case SDLK_u: return ImGuiKey_U;
|
||||||
|
case SDLK_v: return ImGuiKey_V;
|
||||||
|
case SDLK_w: return ImGuiKey_W;
|
||||||
|
case SDLK_x: return ImGuiKey_X;
|
||||||
|
case SDLK_y: return ImGuiKey_Y;
|
||||||
|
case SDLK_z: return ImGuiKey_Z;
|
||||||
|
case SDLK_F1: return ImGuiKey_F1;
|
||||||
|
case SDLK_F2: return ImGuiKey_F2;
|
||||||
|
case SDLK_F3: return ImGuiKey_F3;
|
||||||
|
case SDLK_F4: return ImGuiKey_F4;
|
||||||
|
case SDLK_F5: return ImGuiKey_F5;
|
||||||
|
case SDLK_F6: return ImGuiKey_F6;
|
||||||
|
case SDLK_F7: return ImGuiKey_F7;
|
||||||
|
case SDLK_F8: return ImGuiKey_F8;
|
||||||
|
case SDLK_F9: return ImGuiKey_F9;
|
||||||
|
case SDLK_F10: return ImGuiKey_F10;
|
||||||
|
case SDLK_F11: return ImGuiKey_F11;
|
||||||
|
case SDLK_F12: return ImGuiKey_F12;
|
||||||
|
case SDLK_F13: return ImGuiKey_F13;
|
||||||
|
case SDLK_F14: return ImGuiKey_F14;
|
||||||
|
case SDLK_F15: return ImGuiKey_F15;
|
||||||
|
case SDLK_F16: return ImGuiKey_F16;
|
||||||
|
case SDLK_F17: return ImGuiKey_F17;
|
||||||
|
case SDLK_F18: return ImGuiKey_F18;
|
||||||
|
case SDLK_F19: return ImGuiKey_F19;
|
||||||
|
case SDLK_F20: return ImGuiKey_F20;
|
||||||
|
case SDLK_F21: return ImGuiKey_F21;
|
||||||
|
case SDLK_F22: return ImGuiKey_F22;
|
||||||
|
case SDLK_F23: return ImGuiKey_F23;
|
||||||
|
case SDLK_F24: return ImGuiKey_F24;
|
||||||
|
case SDLK_AC_BACK: return ImGuiKey_AppBack;
|
||||||
|
case SDLK_AC_FORWARD: return ImGuiKey_AppForward;
|
||||||
|
}
|
||||||
|
return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||||
|
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
||||||
|
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
||||||
|
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||||
|
// 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_ImplSDL3_ProcessEvent(const SDL_Event* event)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||||
|
|
||||||
|
switch (event->type)
|
||||||
|
{
|
||||||
|
case SDL_EVENT_MOUSE_MOTION:
|
||||||
|
{
|
||||||
|
ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
|
||||||
|
io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||||
|
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_EVENT_MOUSE_WHEEL:
|
||||||
|
{
|
||||||
|
//IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
|
||||||
|
float wheel_x = -event->wheel.x;
|
||||||
|
float wheel_y = event->wheel.y;
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
wheel_x /= 100.0f;
|
||||||
|
#endif
|
||||||
|
io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||||
|
io.AddMouseWheelEvent(wheel_x, wheel_y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
|
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||||
|
{
|
||||||
|
int mouse_button = -1;
|
||||||
|
if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
|
||||||
|
if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
|
||||||
|
if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
|
||||||
|
if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
|
||||||
|
if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
|
||||||
|
if (mouse_button == -1)
|
||||||
|
break;
|
||||||
|
io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||||
|
io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN));
|
||||||
|
bd->MouseButtonsDown = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_EVENT_TEXT_INPUT:
|
||||||
|
{
|
||||||
|
io.AddInputCharactersUTF8(event->text.text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_EVENT_KEY_DOWN:
|
||||||
|
case SDL_EVENT_KEY_UP:
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);
|
||||||
|
ImGuiKey key = ImGui_ImplSDL3_KeycodeToImGuiKey(event->key.keysym.sym);
|
||||||
|
io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN));
|
||||||
|
io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
||||||
|
{
|
||||||
|
bd->MouseWindowID = event->window.windowID;
|
||||||
|
bd->PendingMouseLeaveFrame = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
|
||||||
|
// causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
|
||||||
|
// we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
|
||||||
|
// FIXME: Unconfirmed whether this is still needed with SDL3.
|
||||||
|
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||||
|
{
|
||||||
|
bd->PendingMouseLeaveFrame = ImGui::GetFrameCount() + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||||
|
io.AddFocusEvent(true);
|
||||||
|
return true;
|
||||||
|
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||||
|
io.AddFocusEvent(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window)
|
||||||
|
{
|
||||||
|
viewport->PlatformHandleRaw = nullptr;
|
||||||
|
#if defined(__WIN32__) && !defined(__WINRT__)
|
||||||
|
viewport->PlatformHandleRaw = (HWND)SDL_GetProperty(SDL_GetWindowProperties(window), "SDL.window.win32.hwnd", NULL);
|
||||||
|
#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
|
||||||
|
viewport->PlatformHandleRaw = (void*)SDL_GetProperty(SDL_GetWindowProperties(window), "SDL.window.cocoa.window", NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||||
|
IM_UNUSED(sdl_gl_context); // Unused in this branch
|
||||||
|
|
||||||
|
// Check and store if we are on a SDL backend that supports global mouse position
|
||||||
|
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
|
||||||
|
bool mouse_can_use_global_state = false;
|
||||||
|
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
|
||||||
|
const char* sdl_backend = SDL_GetCurrentVideoDriver();
|
||||||
|
const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
|
||||||
|
for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++)
|
||||||
|
if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)
|
||||||
|
mouse_can_use_global_state = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)();
|
||||||
|
io.BackendPlatformUserData = (void*)bd;
|
||||||
|
io.BackendPlatformName = "imgui_impl_sdl3";
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||||
|
|
||||||
|
bd->Window = window;
|
||||||
|
bd->Renderer = renderer;
|
||||||
|
bd->MouseCanUseGlobalState = mouse_can_use_global_state;
|
||||||
|
|
||||||
|
io.SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText;
|
||||||
|
io.GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText;
|
||||||
|
io.ClipboardUserData = nullptr;
|
||||||
|
io.SetPlatformImeDataFn = ImGui_ImplSDL3_SetPlatformImeData;
|
||||||
|
|
||||||
|
// Load mouse cursors
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
|
||||||
|
|
||||||
|
// Set platform dependent data in viewport
|
||||||
|
// Our mouse update function expect PlatformHandle to be filled for the main viewport
|
||||||
|
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||||
|
ImGui_ImplSDL3_SetupPlatformHandles(main_viewport, window);
|
||||||
|
|
||||||
|
// From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
|
||||||
|
// Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
|
||||||
|
// (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
|
||||||
|
// It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
|
||||||
|
// you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
|
||||||
|
#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
|
||||||
|
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
|
||||||
|
#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
|
||||||
|
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
|
||||||
|
{
|
||||||
|
IM_UNUSED(sdl_gl_context); // Viewport branch will need this.
|
||||||
|
return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window)
|
||||||
|
{
|
||||||
|
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window)
|
||||||
|
{
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
IM_ASSERT(0 && "Unsupported");
|
||||||
|
#endif
|
||||||
|
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window)
|
||||||
|
{
|
||||||
|
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
|
||||||
|
{
|
||||||
|
return ImGui_ImplSDL3_Init(window, renderer, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDL3_InitForOther(SDL_Window* window)
|
||||||
|
{
|
||||||
|
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDL3_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
if (bd->ClipboardTextData)
|
||||||
|
SDL_free(bd->ClipboardTextData);
|
||||||
|
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
|
||||||
|
SDL_DestroyCursor(bd->MouseCursors[cursor_n]);
|
||||||
|
bd->LastMouseCursor = nullptr;
|
||||||
|
|
||||||
|
io.BackendPlatformName = nullptr;
|
||||||
|
io.BackendPlatformUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL3_UpdateMouseData()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below)
|
||||||
|
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
|
||||||
|
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside
|
||||||
|
SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE);
|
||||||
|
SDL_Window* focused_window = SDL_GetKeyboardFocus();
|
||||||
|
const bool is_app_focused = (bd->Window == focused_window);
|
||||||
|
#else
|
||||||
|
SDL_Window* focused_window = bd->Window;
|
||||||
|
const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only
|
||||||
|
#endif
|
||||||
|
if (is_app_focused)
|
||||||
|
{
|
||||||
|
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||||
|
if (io.WantSetMousePos)
|
||||||
|
SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y);
|
||||||
|
|
||||||
|
// (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured)
|
||||||
|
if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0)
|
||||||
|
{
|
||||||
|
// Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
|
||||||
|
float mouse_x_global, mouse_y_global;
|
||||||
|
int window_x, window_y;
|
||||||
|
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
|
||||||
|
SDL_GetWindowPosition(focused_window, &window_x, &window_y);
|
||||||
|
io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL3_UpdateMouseCursor()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||||
|
return;
|
||||||
|
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||||
|
|
||||||
|
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_HideCursor();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Show OS mouse cursor
|
||||||
|
SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];
|
||||||
|
if (bd->LastMouseCursor != expected_cursor)
|
||||||
|
{
|
||||||
|
SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
|
||||||
|
bd->LastMouseCursor = expected_cursor;
|
||||||
|
}
|
||||||
|
SDL_ShowCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDL3_UpdateGamepads()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get gamepad
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||||
|
SDL_Gamepad* gamepad = SDL_OpenGamepad(0);
|
||||||
|
if (!gamepad)
|
||||||
|
return;
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||||
|
|
||||||
|
// Update gamepad inputs
|
||||||
|
#define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)
|
||||||
|
#define MAP_BUTTON(KEY_NO, BUTTON_NO) { io.AddKeyEvent(KEY_NO, SDL_GetGamepadButton(gamepad, BUTTON_NO) != 0); }
|
||||||
|
#define MAP_ANALOG(KEY_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GetGamepadAxis(gamepad, AXIS_NO) - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); }
|
||||||
|
const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadStart, SDL_GAMEPAD_BUTTON_START);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadBack, SDL_GAMEPAD_BUTTON_BACK);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceLeft, SDL_GAMEPAD_BUTTON_WEST); // Xbox X, PS Square
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceRight, SDL_GAMEPAD_BUTTON_EAST); // Xbox B, PS Circle
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceUp, SDL_GAMEPAD_BUTTON_NORTH); // Xbox Y, PS Triangle
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceDown, SDL_GAMEPAD_BUTTON_SOUTH); // Xbox A, PS Cross
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadUp, SDL_GAMEPAD_BUTTON_DPAD_UP);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL1, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR1, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadL2, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0.0f, 32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadR2, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL3, SDL_GAMEPAD_BUTTON_LEFT_STICK);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR3, SDL_GAMEPAD_BUTTON_RIGHT_STICK);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickLeft, SDL_GAMEPAD_AXIS_LEFTX, -thumb_dead_zone, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX, +thumb_dead_zone, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickUp, SDL_GAMEPAD_AXIS_LEFTY, -thumb_dead_zone, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickDown, SDL_GAMEPAD_AXIS_LEFTY, +thumb_dead_zone, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickLeft, SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickUp, SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767);
|
||||||
|
#undef MAP_BUTTON
|
||||||
|
#undef MAP_ANALOG
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDL3_NewFrame()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplSDL3_Init()?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Setup display size (every frame to accommodate for window resizing)
|
||||||
|
int w, h;
|
||||||
|
int display_w, display_h;
|
||||||
|
SDL_GetWindowSize(bd->Window, &w, &h);
|
||||||
|
if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED)
|
||||||
|
w = h = 0;
|
||||||
|
SDL_GetWindowSizeInPixels(bd->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)
|
||||||
|
// (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)
|
||||||
|
static Uint64 frequency = SDL_GetPerformanceFrequency();
|
||||||
|
Uint64 current_time = SDL_GetPerformanceCounter();
|
||||||
|
if (current_time <= bd->Time)
|
||||||
|
current_time = bd->Time + 1;
|
||||||
|
io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
|
||||||
|
bd->Time = current_time;
|
||||||
|
|
||||||
|
if (bd->PendingMouseLeaveFrame && bd->PendingMouseLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
|
||||||
|
{
|
||||||
|
bd->MouseWindowID = 0;
|
||||||
|
bd->PendingMouseLeaveFrame = 0;
|
||||||
|
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplSDL3_UpdateMouseData();
|
||||||
|
ImGui_ImplSDL3_UpdateMouseCursor();
|
||||||
|
|
||||||
|
// Update game controllers (if enabled and available)
|
||||||
|
ImGui_ImplSDL3_UpdateGamepads();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
41
backends/imgui_impl_sdl3.h
Normal file
41
backends/imgui_impl_sdl3.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*)
|
||||||
|
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||||
|
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||||
|
// (IMPORTANT: SDL 3.0.0 is NOT YET RELEASED. IT IS POSSIBLE THAT ITS SPECS/API WILL CHANGE BEFORE RELEASE)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support.
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
// Missing features:
|
||||||
|
// [x] Platform: Basic IME support. Position somehow broken in SDL3 + app needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
struct SDL_Window;
|
||||||
|
struct SDL_Renderer;
|
||||||
|
typedef union SDL_Event SDL_Event;
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOther(SDL_Window* window);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDL3_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDL3_NewFrame();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event);
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
266
backends/imgui_impl_sdlrenderer2.cpp
Normal file
266
backends/imgui_impl_sdlrenderer2.cpp
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
// dear imgui: Renderer Backend for SDL_Renderer for SDL2
|
||||||
|
// (Requires: SDL 2.0.17+)
|
||||||
|
|
||||||
|
// Note how SDL_Renderer is an _optional_ component of SDL2.
|
||||||
|
// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.
|
||||||
|
// If your application will want to render any non trivial amount of graphics other than UI,
|
||||||
|
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and
|
||||||
|
// it might be difficult to step out of those boundaries.
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// 2023-05-30: Renamed imgui_impl_sdlrenderer.h/.cpp to imgui_impl_sdlrenderer2.h/.cpp to accommodate for upcoming SDL3.
|
||||||
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||||
|
// 2021-12-21: Update SDL_RenderGeometryRaw() format to work with SDL 2.0.19.
|
||||||
|
// 2021-12-03: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||||
|
// 2021-10-06: Backup and restore modified ClipRect/Viewport.
|
||||||
|
// 2021-09-21: Initial version.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_sdlrenderer2.h"
|
||||||
|
#include <stdint.h> // intptr_t
|
||||||
|
|
||||||
|
// Clang warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SDL
|
||||||
|
#include <SDL.h>
|
||||||
|
#if !SDL_VERSION_ATLEAST(2,0,17)
|
||||||
|
#error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SDL_Renderer data
|
||||||
|
struct ImGui_ImplSDLRenderer2_Data
|
||||||
|
{
|
||||||
|
SDL_Renderer* SDLRenderer;
|
||||||
|
SDL_Texture* FontTexture;
|
||||||
|
ImGui_ImplSDLRenderer2_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
static ImGui_ImplSDLRenderer2_Data* ImGui_ImplSDLRenderer2_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer2_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
bool ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||||
|
IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!");
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplSDLRenderer2_Data* bd = IM_NEW(ImGui_ImplSDLRenderer2_Data)();
|
||||||
|
io.BackendRendererUserData = (void*)bd;
|
||||||
|
io.BackendRendererName = "imgui_impl_sdlrenderer2";
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||||
|
|
||||||
|
bd->SDLRenderer = renderer;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer2_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
ImGui_ImplSDLRenderer2_DestroyDeviceObjects();
|
||||||
|
|
||||||
|
io.BackendRendererName = nullptr;
|
||||||
|
io.BackendRendererUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDLRenderer2_SetupRenderState()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
|
||||||
|
|
||||||
|
// Clear out any viewports and cliprect set by the user
|
||||||
|
// FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
|
||||||
|
SDL_RenderSetViewport(bd->SDLRenderer, nullptr);
|
||||||
|
SDL_RenderSetClipRect(bd->SDLRenderer, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer2_NewFrame()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplSDLRenderer2_Init()?");
|
||||||
|
|
||||||
|
if (!bd->FontTexture)
|
||||||
|
ImGui_ImplSDLRenderer2_CreateDeviceObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data)
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
|
||||||
|
|
||||||
|
// If there's a scale factor set by the user, use that instead
|
||||||
|
// If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass
|
||||||
|
// to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.
|
||||||
|
float rsx = 1.0f;
|
||||||
|
float rsy = 1.0f;
|
||||||
|
SDL_RenderGetScale(bd->SDLRenderer, &rsx, &rsy);
|
||||||
|
ImVec2 render_scale;
|
||||||
|
render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f;
|
||||||
|
render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f;
|
||||||
|
|
||||||
|
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||||
|
int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x);
|
||||||
|
int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y);
|
||||||
|
if (fb_width == 0 || fb_height == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Backup SDL_Renderer state that will be modified to restore it afterwards
|
||||||
|
struct BackupSDLRendererState
|
||||||
|
{
|
||||||
|
SDL_Rect Viewport;
|
||||||
|
bool ClipEnabled;
|
||||||
|
SDL_Rect ClipRect;
|
||||||
|
};
|
||||||
|
BackupSDLRendererState old = {};
|
||||||
|
old.ClipEnabled = SDL_RenderIsClipEnabled(bd->SDLRenderer) == SDL_TRUE;
|
||||||
|
SDL_RenderGetViewport(bd->SDLRenderer, &old.Viewport);
|
||||||
|
SDL_RenderGetClipRect(bd->SDLRenderer, &old.ClipRect);
|
||||||
|
|
||||||
|
// Will project scissor/clipping rectangles into framebuffer space
|
||||||
|
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||||
|
ImVec2 clip_scale = render_scale;
|
||||||
|
|
||||||
|
// Render command lists
|
||||||
|
ImGui_ImplSDLRenderer2_SetupRenderState();
|
||||||
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||||
|
const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
|
||||||
|
const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;
|
||||||
|
|
||||||
|
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||||
|
{
|
||||||
|
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||||
|
if (pcmd->UserCallback)
|
||||||
|
{
|
||||||
|
// 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_ImplSDLRenderer2_SetupRenderState();
|
||||||
|
else
|
||||||
|
pcmd->UserCallback(cmd_list, pcmd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Project scissor/clipping rectangles into framebuffer space
|
||||||
|
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||||
|
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||||
|
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
|
||||||
|
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
|
||||||
|
if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; }
|
||||||
|
if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; }
|
||||||
|
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) };
|
||||||
|
SDL_RenderSetClipRect(bd->SDLRenderer, &r);
|
||||||
|
|
||||||
|
const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos));
|
||||||
|
const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv));
|
||||||
|
#if SDL_VERSION_ATLEAST(2,0,19)
|
||||||
|
const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+
|
||||||
|
#else
|
||||||
|
const int* color = (const int*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.17 and 2.0.18
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Bind texture, Draw
|
||||||
|
SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID();
|
||||||
|
SDL_RenderGeometryRaw(bd->SDLRenderer, tex,
|
||||||
|
xy, (int)sizeof(ImDrawVert),
|
||||||
|
color, (int)sizeof(ImDrawVert),
|
||||||
|
uv, (int)sizeof(ImDrawVert),
|
||||||
|
cmd_list->VtxBuffer.Size - pcmd->VtxOffset,
|
||||||
|
idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore modified SDL_Renderer state
|
||||||
|
SDL_RenderSetViewport(bd->SDLRenderer, &old.Viewport);
|
||||||
|
SDL_RenderSetClipRect(bd->SDLRenderer, old.ClipEnabled ? &old.ClipRect : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by Init/NewFrame/Shutdown
|
||||||
|
bool ImGui_ImplSDLRenderer2_CreateFontsTexture()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
|
||||||
|
|
||||||
|
// Build texture atlas
|
||||||
|
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
|
||||||
|
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||||
|
bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height);
|
||||||
|
if (bd->FontTexture == nullptr)
|
||||||
|
{
|
||||||
|
SDL_Log("error creating texture");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SDL_UpdateTexture(bd->FontTexture, nullptr, pixels, 4 * width);
|
||||||
|
SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND);
|
||||||
|
SDL_SetTextureScaleMode(bd->FontTexture, SDL_ScaleModeLinear);
|
||||||
|
|
||||||
|
// Store our identifier
|
||||||
|
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer2_DestroyFontsTexture()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
|
||||||
|
if (bd->FontTexture)
|
||||||
|
{
|
||||||
|
io.Fonts->SetTexID(0);
|
||||||
|
SDL_DestroyTexture(bd->FontTexture);
|
||||||
|
bd->FontTexture = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDLRenderer2_CreateDeviceObjects()
|
||||||
|
{
|
||||||
|
return ImGui_ImplSDLRenderer2_CreateFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer2_DestroyDeviceObjects()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer2_DestroyFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
39
backends/imgui_impl_sdlrenderer2.h
Normal file
39
backends/imgui_impl_sdlrenderer2.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// dear imgui: Renderer Backend for SDL_Renderer for SDL2
|
||||||
|
// (Requires: SDL 2.0.17+)
|
||||||
|
|
||||||
|
// Note how SDL_Renderer is an _optional_ component of SDL2.
|
||||||
|
// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.
|
||||||
|
// If your application will want to render any non trivial amount of graphics other than UI,
|
||||||
|
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and
|
||||||
|
// it might be difficult to step out of those boundaries.
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
|
||||||
|
struct SDL_Renderer;
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_NewFrame();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data);
|
||||||
|
|
||||||
|
// Called by Init/NewFrame/Shutdown
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_CreateFontsTexture();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_DestroyFontsTexture();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_CreateDeviceObjects();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_DestroyDeviceObjects();
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
261
backends/imgui_impl_sdlrenderer3.cpp
Normal file
261
backends/imgui_impl_sdlrenderer3.cpp
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
// dear imgui: Renderer Backend for SDL_Renderer for SDL3
|
||||||
|
// (Requires: SDL 3.0.0+)
|
||||||
|
|
||||||
|
// Note how SDL_Renderer is an _optional_ component of SDL3.
|
||||||
|
// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.
|
||||||
|
// If your application will want to render any non trivial amount of graphics other than UI,
|
||||||
|
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and
|
||||||
|
// it might be difficult to step out of those boundaries.
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// 2023-05-30: Initial version.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_sdlrenderer3.h"
|
||||||
|
#include <stdint.h> // intptr_t
|
||||||
|
|
||||||
|
// Clang warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SDL
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#if !SDL_VERSION_ATLEAST(3,0,0)
|
||||||
|
#error This backend requires SDL 3.0.0+
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SDL_Renderer data
|
||||||
|
struct ImGui_ImplSDLRenderer3_Data
|
||||||
|
{
|
||||||
|
SDL_Renderer* SDLRenderer;
|
||||||
|
SDL_Texture* FontTexture;
|
||||||
|
ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||||
|
IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!");
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplSDLRenderer3_Data* bd = IM_NEW(ImGui_ImplSDLRenderer3_Data)();
|
||||||
|
io.BackendRendererUserData = (void*)bd;
|
||||||
|
io.BackendRendererName = "imgui_impl_sdlrenderer3";
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||||
|
|
||||||
|
bd->SDLRenderer = renderer;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer3_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
|
||||||
|
|
||||||
|
io.BackendRendererName = nullptr;
|
||||||
|
io.BackendRendererUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplSDLRenderer3_SetupRenderState()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||||
|
|
||||||
|
// Clear out any viewports and cliprect set by the user
|
||||||
|
// FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
|
||||||
|
SDL_SetRenderViewport(bd->SDLRenderer, nullptr);
|
||||||
|
SDL_SetRenderClipRect(bd->SDLRenderer, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer3_NewFrame()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplSDLRenderer3_Init()?");
|
||||||
|
|
||||||
|
if (!bd->FontTexture)
|
||||||
|
ImGui_ImplSDLRenderer3_CreateDeviceObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data)
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||||
|
|
||||||
|
// If there's a scale factor set by the user, use that instead
|
||||||
|
// If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass
|
||||||
|
// to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.
|
||||||
|
float rsx = 1.0f;
|
||||||
|
float rsy = 1.0f;
|
||||||
|
SDL_GetRenderScale(bd->SDLRenderer, &rsx, &rsy);
|
||||||
|
ImVec2 render_scale;
|
||||||
|
render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f;
|
||||||
|
render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f;
|
||||||
|
|
||||||
|
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||||
|
int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x);
|
||||||
|
int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y);
|
||||||
|
if (fb_width == 0 || fb_height == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Backup SDL_Renderer state that will be modified to restore it afterwards
|
||||||
|
struct BackupSDLRendererState
|
||||||
|
{
|
||||||
|
SDL_Rect Viewport;
|
||||||
|
bool ClipEnabled;
|
||||||
|
SDL_Rect ClipRect;
|
||||||
|
};
|
||||||
|
BackupSDLRendererState old = {};
|
||||||
|
old.ClipEnabled = SDL_RenderClipEnabled(bd->SDLRenderer) == SDL_TRUE;
|
||||||
|
SDL_GetRenderViewport(bd->SDLRenderer, &old.Viewport);
|
||||||
|
SDL_GetRenderClipRect(bd->SDLRenderer, &old.ClipRect);
|
||||||
|
|
||||||
|
// Will project scissor/clipping rectangles into framebuffer space
|
||||||
|
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||||
|
ImVec2 clip_scale = render_scale;
|
||||||
|
|
||||||
|
// Render command lists
|
||||||
|
ImGui_ImplSDLRenderer3_SetupRenderState();
|
||||||
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||||
|
const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
|
||||||
|
const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;
|
||||||
|
|
||||||
|
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||||
|
{
|
||||||
|
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||||
|
if (pcmd->UserCallback)
|
||||||
|
{
|
||||||
|
// 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_ImplSDLRenderer3_SetupRenderState();
|
||||||
|
else
|
||||||
|
pcmd->UserCallback(cmd_list, pcmd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Project scissor/clipping rectangles into framebuffer space
|
||||||
|
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||||
|
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||||
|
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
|
||||||
|
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
|
||||||
|
if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; }
|
||||||
|
if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; }
|
||||||
|
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) };
|
||||||
|
SDL_SetRenderClipRect(bd->SDLRenderer, &r);
|
||||||
|
|
||||||
|
const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos));
|
||||||
|
const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv));
|
||||||
|
#if SDL_VERSION_ATLEAST(2,0,19)
|
||||||
|
const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+
|
||||||
|
#else
|
||||||
|
const int* color = (const int*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.17 and 2.0.18
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Bind texture, Draw
|
||||||
|
SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID();
|
||||||
|
SDL_RenderGeometryRaw(bd->SDLRenderer, tex,
|
||||||
|
xy, (int)sizeof(ImDrawVert),
|
||||||
|
color, (int)sizeof(ImDrawVert),
|
||||||
|
uv, (int)sizeof(ImDrawVert),
|
||||||
|
cmd_list->VtxBuffer.Size - pcmd->VtxOffset,
|
||||||
|
idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore modified SDL_Renderer state
|
||||||
|
SDL_SetRenderViewport(bd->SDLRenderer, &old.Viewport);
|
||||||
|
SDL_SetRenderClipRect(bd->SDLRenderer, old.ClipEnabled ? &old.ClipRect : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by Init/NewFrame/Shutdown
|
||||||
|
bool ImGui_ImplSDLRenderer3_CreateFontsTexture()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||||
|
|
||||||
|
// Build texture atlas
|
||||||
|
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
|
||||||
|
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||||
|
bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height);
|
||||||
|
if (bd->FontTexture == nullptr)
|
||||||
|
{
|
||||||
|
SDL_Log("error creating texture");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SDL_UpdateTexture(bd->FontTexture, nullptr, pixels, 4 * width);
|
||||||
|
SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND);
|
||||||
|
SDL_SetTextureScaleMode(bd->FontTexture, SDL_SCALEMODE_LINEAR);
|
||||||
|
|
||||||
|
// Store our identifier
|
||||||
|
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer3_DestroyFontsTexture()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||||
|
if (bd->FontTexture)
|
||||||
|
{
|
||||||
|
io.Fonts->SetTexID(0);
|
||||||
|
SDL_DestroyTexture(bd->FontTexture);
|
||||||
|
bd->FontTexture = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSDLRenderer3_CreateDeviceObjects()
|
||||||
|
{
|
||||||
|
return ImGui_ImplSDLRenderer3_CreateFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSDLRenderer3_DestroyDeviceObjects()
|
||||||
|
{
|
||||||
|
ImGui_ImplSDLRenderer3_DestroyFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
39
backends/imgui_impl_sdlrenderer3.h
Normal file
39
backends/imgui_impl_sdlrenderer3.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// dear imgui: Renderer Backend for SDL_Renderer for SDL3
|
||||||
|
// (Requires: SDL 3.0.0+)
|
||||||
|
|
||||||
|
// Note how SDL_Renderer is an _optional_ component of SDL3.
|
||||||
|
// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.
|
||||||
|
// If your application will want to render any non trivial amount of graphics other than UI,
|
||||||
|
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and
|
||||||
|
// it might be difficult to step out of those boundaries.
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
struct SDL_Renderer;
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_NewFrame();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data);
|
||||||
|
|
||||||
|
// Called by Init/NewFrame/Shutdown
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateFontsTexture();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyFontsTexture();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateDeviceObjects();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
1571
backends/imgui_impl_vulkan.cpp
Normal file
1571
backends/imgui_impl_vulkan.cpp
Normal file
File diff suppressed because it is too large
Load Diff
169
backends/imgui_impl_vulkan.h
Normal file
169
backends/imgui_impl_vulkan.h
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// dear imgui: Renderer Backend for Vulkan
|
||||||
|
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
|
||||||
|
// See imgui_impl_vulkan.cpp file for details.
|
||||||
|
|
||||||
|
// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
|
||||||
|
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
|
||||||
|
// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
|
||||||
|
// You will use those if you want to use this rendering backend in your engine/app.
|
||||||
|
// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
|
||||||
|
// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
|
||||||
|
// Read comments in imgui_impl_vulkan.h.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
|
||||||
|
// [Configuration] in order to use a custom Vulkan function loader:
|
||||||
|
// (1) You'll need to disable default Vulkan function prototypes.
|
||||||
|
// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag.
|
||||||
|
// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit:
|
||||||
|
// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file
|
||||||
|
// - Or as a compilation flag in your build system
|
||||||
|
// - Or uncomment here (not recommended because you'd be modifying imgui sources!)
|
||||||
|
// - Do not simply add it in a .cpp file!
|
||||||
|
// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function.
|
||||||
|
// If you have no idea what this is, leave it alone!
|
||||||
|
//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES
|
||||||
|
|
||||||
|
// Vulkan includes
|
||||||
|
#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES)
|
||||||
|
#define VK_NO_PROTOTYPES
|
||||||
|
#endif
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
// Initialization data, for ImGui_ImplVulkan_Init()
|
||||||
|
// [Please zero-clear before use!]
|
||||||
|
struct ImGui_ImplVulkan_InitInfo
|
||||||
|
{
|
||||||
|
VkInstance Instance;
|
||||||
|
VkPhysicalDevice PhysicalDevice;
|
||||||
|
VkDevice Device;
|
||||||
|
uint32_t QueueFamily;
|
||||||
|
VkQueue Queue;
|
||||||
|
VkPipelineCache PipelineCache;
|
||||||
|
VkDescriptorPool DescriptorPool;
|
||||||
|
uint32_t Subpass;
|
||||||
|
uint32_t MinImageCount; // >= 2
|
||||||
|
uint32_t ImageCount; // >= MinImageCount
|
||||||
|
VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT (0 -> default to VK_SAMPLE_COUNT_1_BIT)
|
||||||
|
|
||||||
|
// Dynamic Rendering (Optional)
|
||||||
|
bool UseDynamicRendering; // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3.
|
||||||
|
VkFormat ColorAttachmentFormat; // Required for dynamic rendering
|
||||||
|
|
||||||
|
// Allocation, Debugging
|
||||||
|
const VkAllocationCallbacks* Allocator;
|
||||||
|
void (*CheckVkResultFn)(VkResult err);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Called by user code
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontsTexture();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated)
|
||||||
|
|
||||||
|
// Register a texture (VkDescriptorSet == ImTextureID)
|
||||||
|
// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem
|
||||||
|
// Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions.
|
||||||
|
IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);
|
||||||
|
|
||||||
|
// Optional: load Vulkan functions with a custom function loader
|
||||||
|
// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr);
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Internal / Miscellaneous Vulkan Helpers
|
||||||
|
// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// You probably do NOT need to use or care about those functions.
|
||||||
|
// Those functions only exist because:
|
||||||
|
// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files.
|
||||||
|
// 2) the multi-viewport / platform window implementation needs them internally.
|
||||||
|
// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings,
|
||||||
|
// but it is too much code to duplicate everywhere so we exceptionally expose them.
|
||||||
|
//
|
||||||
|
// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.).
|
||||||
|
// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work.
|
||||||
|
// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions)
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct ImGui_ImplVulkanH_Frame;
|
||||||
|
struct ImGui_ImplVulkanH_Window;
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator);
|
||||||
|
IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space);
|
||||||
|
IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count);
|
||||||
|
IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode);
|
||||||
|
|
||||||
|
// Helper structure to hold the data needed by one rendering frame
|
||||||
|
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
|
||||||
|
// [Please zero-clear before use!]
|
||||||
|
struct ImGui_ImplVulkanH_Frame
|
||||||
|
{
|
||||||
|
VkCommandPool CommandPool;
|
||||||
|
VkCommandBuffer CommandBuffer;
|
||||||
|
VkFence Fence;
|
||||||
|
VkImage Backbuffer;
|
||||||
|
VkImageView BackbufferView;
|
||||||
|
VkFramebuffer Framebuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImGui_ImplVulkanH_FrameSemaphores
|
||||||
|
{
|
||||||
|
VkSemaphore ImageAcquiredSemaphore;
|
||||||
|
VkSemaphore RenderCompleteSemaphore;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper structure to hold the data needed by one rendering context into one OS window
|
||||||
|
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
|
||||||
|
struct ImGui_ImplVulkanH_Window
|
||||||
|
{
|
||||||
|
int Width;
|
||||||
|
int Height;
|
||||||
|
VkSwapchainKHR Swapchain;
|
||||||
|
VkSurfaceKHR Surface;
|
||||||
|
VkSurfaceFormatKHR SurfaceFormat;
|
||||||
|
VkPresentModeKHR PresentMode;
|
||||||
|
VkRenderPass RenderPass;
|
||||||
|
VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo
|
||||||
|
bool UseDynamicRendering;
|
||||||
|
bool ClearEnable;
|
||||||
|
VkClearValue ClearValue;
|
||||||
|
uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount)
|
||||||
|
uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count)
|
||||||
|
uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data)
|
||||||
|
ImGui_ImplVulkanH_Frame* Frames;
|
||||||
|
ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores;
|
||||||
|
|
||||||
|
ImGui_ImplVulkanH_Window()
|
||||||
|
{
|
||||||
|
memset((void*)this, 0, sizeof(*this));
|
||||||
|
PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this.
|
||||||
|
ClearEnable = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
772
backends/imgui_impl_wgpu.cpp
Normal file
772
backends/imgui_impl_wgpu.cpp
Normal file
@ -0,0 +1,772 @@
|
|||||||
|
// dear imgui: Renderer for WebGPU
|
||||||
|
// This needs to be used along with a Platform Binding (e.g. GLFW)
|
||||||
|
// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2023-07-13: Use WGPUShaderModuleWGSLDescriptor's code instead of source. use WGPUMipmapFilterMode_Linear instead of WGPUFilterMode_Linear. (#6602)
|
||||||
|
// 2023-04-11: Align buffer sizes. Use WGSL shaders instead of precompiled SPIR-V.
|
||||||
|
// 2023-04-11: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
|
// 2023-01-25: Revert automatic pipeline layout generation (see https://github.com/gpuweb/gpuweb/issues/2470)
|
||||||
|
// 2022-11-24: Fixed validation error with default depth buffer settings.
|
||||||
|
// 2022-11-10: Fixed rendering when a depth buffer is enabled. Added 'WGPUTextureFormat depth_format' parameter to ImGui_ImplWGPU_Init().
|
||||||
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||||
|
// 2021-11-29: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer()/wgpuRenderPassEncoderSetIndexBuffer().
|
||||||
|
// 2021-08-24: Fixed for latest specs.
|
||||||
|
// 2021-05-24: Add support for draw_data->FramebufferScale.
|
||||||
|
// 2021-05-19: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||||
|
// 2021-05-16: Update to latest WebGPU specs (compatible with Emscripten 2.0.20 and Chrome Canary 92).
|
||||||
|
// 2021-02-18: Change blending equation to preserve alpha in output buffer.
|
||||||
|
// 2021-01-28: Initial version.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_wgpu.h"
|
||||||
|
#include <limits.h>
|
||||||
|
#include <webgpu/webgpu.h>
|
||||||
|
|
||||||
|
// Dear ImGui prototypes from imgui_internal.h
|
||||||
|
extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0);
|
||||||
|
#define MEMALIGN(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align (copied from IM_ALIGN() macro).
|
||||||
|
|
||||||
|
// WebGPU data
|
||||||
|
struct RenderResources
|
||||||
|
{
|
||||||
|
WGPUTexture FontTexture = nullptr; // Font texture
|
||||||
|
WGPUTextureView FontTextureView = nullptr; // Texture view for font texture
|
||||||
|
WGPUSampler Sampler = nullptr; // Sampler for the font texture
|
||||||
|
WGPUBuffer Uniforms = nullptr; // Shader uniforms
|
||||||
|
WGPUBindGroup CommonBindGroup = nullptr; // Resources bind-group to bind the common resources to pipeline
|
||||||
|
ImGuiStorage ImageBindGroups; // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map)
|
||||||
|
WGPUBindGroup ImageBindGroup = nullptr; // Default font-resource of Dear ImGui
|
||||||
|
WGPUBindGroupLayout ImageBindGroupLayout = nullptr; // Cache layout used for the image bind group. Avoids allocating unnecessary JS objects when working with WebASM
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FrameResources
|
||||||
|
{
|
||||||
|
WGPUBuffer IndexBuffer;
|
||||||
|
WGPUBuffer VertexBuffer;
|
||||||
|
ImDrawIdx* IndexBufferHost;
|
||||||
|
ImDrawVert* VertexBufferHost;
|
||||||
|
int IndexBufferSize;
|
||||||
|
int VertexBufferSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Uniforms
|
||||||
|
{
|
||||||
|
float MVP[4][4];
|
||||||
|
float Gamma;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImGui_ImplWGPU_Data
|
||||||
|
{
|
||||||
|
WGPUDevice wgpuDevice = nullptr;
|
||||||
|
WGPUQueue defaultQueue = nullptr;
|
||||||
|
WGPUTextureFormat renderTargetFormat = WGPUTextureFormat_Undefined;
|
||||||
|
WGPUTextureFormat depthStencilFormat = WGPUTextureFormat_Undefined;
|
||||||
|
WGPURenderPipeline pipelineState = nullptr;
|
||||||
|
|
||||||
|
RenderResources renderResources;
|
||||||
|
FrameResources* pFrameResources = nullptr;
|
||||||
|
unsigned int numFramesInFlight = 0;
|
||||||
|
unsigned int frameIndex = UINT_MAX;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
static ImGui_ImplWGPU_Data* ImGui_ImplWGPU_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplWGPU_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// SHADERS
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static const char __shader_vert_wgsl[] = R"(
|
||||||
|
struct VertexInput {
|
||||||
|
@location(0) position: vec2<f32>,
|
||||||
|
@location(1) uv: vec2<f32>,
|
||||||
|
@location(2) color: vec4<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
@location(0) color: vec4<f32>,
|
||||||
|
@location(1) uv: vec2<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Uniforms {
|
||||||
|
mvp: mat4x4<f32>,
|
||||||
|
gamma: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn main(in: VertexInput) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
out.position = uniforms.mvp * vec4<f32>(in.position, 0.0, 1.0);
|
||||||
|
out.color = in.color;
|
||||||
|
out.uv = in.uv;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static const char __shader_frag_wgsl[] = R"(
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
@location(0) color: vec4<f32>,
|
||||||
|
@location(1) uv: vec2<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Uniforms {
|
||||||
|
mvp: mat4x4<f32>,
|
||||||
|
gamma: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
||||||
|
@group(0) @binding(1) var s: sampler;
|
||||||
|
@group(1) @binding(0) var t: texture_2d<f32>;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
let color = in.color * textureSample(t, s, in.uv);
|
||||||
|
let corrected_color = pow(color.rgb, vec3<f32>(uniforms.gamma));
|
||||||
|
return vec4<f32>(corrected_color, color.a);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static void SafeRelease(ImDrawIdx*& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
delete[] res;
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
static void SafeRelease(ImDrawVert*& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
delete[] res;
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
static void SafeRelease(WGPUBindGroupLayout& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
wgpuBindGroupLayoutRelease(res);
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
static void SafeRelease(WGPUBindGroup& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
wgpuBindGroupRelease(res);
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
static void SafeRelease(WGPUBuffer& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
wgpuBufferRelease(res);
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
static void SafeRelease(WGPURenderPipeline& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
wgpuRenderPipelineRelease(res);
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
static void SafeRelease(WGPUSampler& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
wgpuSamplerRelease(res);
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
static void SafeRelease(WGPUShaderModule& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
wgpuShaderModuleRelease(res);
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
static void SafeRelease(WGPUTextureView& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
wgpuTextureViewRelease(res);
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
static void SafeRelease(WGPUTexture& res)
|
||||||
|
{
|
||||||
|
if (res)
|
||||||
|
wgpuTextureRelease(res);
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SafeRelease(RenderResources& res)
|
||||||
|
{
|
||||||
|
SafeRelease(res.FontTexture);
|
||||||
|
SafeRelease(res.FontTextureView);
|
||||||
|
SafeRelease(res.Sampler);
|
||||||
|
SafeRelease(res.Uniforms);
|
||||||
|
SafeRelease(res.CommonBindGroup);
|
||||||
|
SafeRelease(res.ImageBindGroup);
|
||||||
|
SafeRelease(res.ImageBindGroupLayout);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void SafeRelease(FrameResources& res)
|
||||||
|
{
|
||||||
|
SafeRelease(res.IndexBuffer);
|
||||||
|
SafeRelease(res.VertexBuffer);
|
||||||
|
SafeRelease(res.IndexBufferHost);
|
||||||
|
SafeRelease(res.VertexBufferHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const char* wgsl_source)
|
||||||
|
{
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
|
||||||
|
WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
|
||||||
|
wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
|
||||||
|
wgsl_desc.code = wgsl_source;
|
||||||
|
|
||||||
|
WGPUShaderModuleDescriptor desc = {};
|
||||||
|
desc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&wgsl_desc);
|
||||||
|
|
||||||
|
WGPUProgrammableStageDescriptor stage_desc = {};
|
||||||
|
stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc);
|
||||||
|
stage_desc.entryPoint = "main";
|
||||||
|
return stage_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WGPUBindGroup ImGui_ImplWGPU_CreateImageBindGroup(WGPUBindGroupLayout layout, WGPUTextureView texture)
|
||||||
|
{
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
WGPUBindGroupEntry image_bg_entries[] = { { nullptr, 0, 0, 0, 0, 0, texture } };
|
||||||
|
|
||||||
|
WGPUBindGroupDescriptor image_bg_descriptor = {};
|
||||||
|
image_bg_descriptor.layout = layout;
|
||||||
|
image_bg_descriptor.entryCount = sizeof(image_bg_entries) / sizeof(WGPUBindGroupEntry);
|
||||||
|
image_bg_descriptor.entries = image_bg_entries;
|
||||||
|
return wgpuDeviceCreateBindGroup(bd->wgpuDevice, &image_bg_descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPassEncoder ctx, FrameResources* fr)
|
||||||
|
{
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
|
||||||
|
// Setup orthographic projection matrix into our constant buffer
|
||||||
|
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
float mvp[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, 0.5f, 0.0f },
|
||||||
|
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||||
|
};
|
||||||
|
wgpuQueueWriteBuffer(bd->defaultQueue, bd->renderResources.Uniforms, offsetof(Uniforms, MVP), mvp, sizeof(Uniforms::MVP));
|
||||||
|
float gamma;
|
||||||
|
switch (bd->renderTargetFormat)
|
||||||
|
{
|
||||||
|
case WGPUTextureFormat_ASTC10x10UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC10x5UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC10x6UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC10x8UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC12x10UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC12x12UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC4x4UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC5x5UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC6x5UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC6x6UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC8x5UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC8x6UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ASTC8x8UnormSrgb:
|
||||||
|
case WGPUTextureFormat_BC1RGBAUnormSrgb:
|
||||||
|
case WGPUTextureFormat_BC2RGBAUnormSrgb:
|
||||||
|
case WGPUTextureFormat_BC3RGBAUnormSrgb:
|
||||||
|
case WGPUTextureFormat_BC7RGBAUnormSrgb:
|
||||||
|
case WGPUTextureFormat_BGRA8UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ETC2RGB8A1UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ETC2RGB8UnormSrgb:
|
||||||
|
case WGPUTextureFormat_ETC2RGBA8UnormSrgb:
|
||||||
|
case WGPUTextureFormat_RGBA8UnormSrgb:
|
||||||
|
gamma = 2.2f;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
gamma = 1.0f;
|
||||||
|
}
|
||||||
|
wgpuQueueWriteBuffer(bd->defaultQueue, bd->renderResources.Uniforms, offsetof(Uniforms, Gamma), &gamma, sizeof(Uniforms::Gamma));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup viewport
|
||||||
|
wgpuRenderPassEncoderSetViewport(ctx, 0, 0, draw_data->FramebufferScale.x * draw_data->DisplaySize.x, draw_data->FramebufferScale.y * draw_data->DisplaySize.y, 0, 1);
|
||||||
|
|
||||||
|
// Bind shader and vertex buffers
|
||||||
|
wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, fr->VertexBufferSize * sizeof(ImDrawVert));
|
||||||
|
wgpuRenderPassEncoderSetIndexBuffer(ctx, fr->IndexBuffer, sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, fr->IndexBufferSize * sizeof(ImDrawIdx));
|
||||||
|
wgpuRenderPassEncoderSetPipeline(ctx, bd->pipelineState);
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(ctx, 0, bd->renderResources.CommonBindGroup, 0, nullptr);
|
||||||
|
|
||||||
|
// Setup blend factor
|
||||||
|
WGPUColor blend_color = { 0.f, 0.f, 0.f, 0.f };
|
||||||
|
wgpuRenderPassEncoderSetBlendConstant(ctx, &blend_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder)
|
||||||
|
{
|
||||||
|
// Avoid rendering when minimized
|
||||||
|
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// FIXME: Assuming that this only gets called once per frame!
|
||||||
|
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
bd->frameIndex = bd->frameIndex + 1;
|
||||||
|
FrameResources* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight];
|
||||||
|
|
||||||
|
// Create and grow vertex/index buffers if needed
|
||||||
|
if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount)
|
||||||
|
{
|
||||||
|
if (fr->VertexBuffer)
|
||||||
|
{
|
||||||
|
wgpuBufferDestroy(fr->VertexBuffer);
|
||||||
|
wgpuBufferRelease(fr->VertexBuffer);
|
||||||
|
}
|
||||||
|
SafeRelease(fr->VertexBufferHost);
|
||||||
|
fr->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||||
|
|
||||||
|
WGPUBufferDescriptor vb_desc =
|
||||||
|
{
|
||||||
|
nullptr,
|
||||||
|
"Dear ImGui Vertex buffer",
|
||||||
|
WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex,
|
||||||
|
MEMALIGN(fr->VertexBufferSize * sizeof(ImDrawVert), 4),
|
||||||
|
false
|
||||||
|
};
|
||||||
|
fr->VertexBuffer = wgpuDeviceCreateBuffer(bd->wgpuDevice, &vb_desc);
|
||||||
|
if (!fr->VertexBuffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fr->VertexBufferHost = new ImDrawVert[fr->VertexBufferSize];
|
||||||
|
}
|
||||||
|
if (fr->IndexBuffer == nullptr || fr->IndexBufferSize < draw_data->TotalIdxCount)
|
||||||
|
{
|
||||||
|
if (fr->IndexBuffer)
|
||||||
|
{
|
||||||
|
wgpuBufferDestroy(fr->IndexBuffer);
|
||||||
|
wgpuBufferRelease(fr->IndexBuffer);
|
||||||
|
}
|
||||||
|
SafeRelease(fr->IndexBufferHost);
|
||||||
|
fr->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||||
|
|
||||||
|
WGPUBufferDescriptor ib_desc =
|
||||||
|
{
|
||||||
|
nullptr,
|
||||||
|
"Dear ImGui Index buffer",
|
||||||
|
WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
|
||||||
|
MEMALIGN(fr->IndexBufferSize * sizeof(ImDrawIdx), 4),
|
||||||
|
false
|
||||||
|
};
|
||||||
|
fr->IndexBuffer = wgpuDeviceCreateBuffer(bd->wgpuDevice, &ib_desc);
|
||||||
|
if (!fr->IndexBuffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fr->IndexBufferHost = new ImDrawIdx[fr->IndexBufferSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload vertex/index data into a single contiguous GPU buffer
|
||||||
|
ImDrawVert* vtx_dst = (ImDrawVert*)fr->VertexBufferHost;
|
||||||
|
ImDrawIdx* idx_dst = (ImDrawIdx*)fr->IndexBufferHost;
|
||||||
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||||
|
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||||
|
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||||
|
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||||
|
idx_dst += cmd_list->IdxBuffer.Size;
|
||||||
|
}
|
||||||
|
int64_t vb_write_size = MEMALIGN((char*)vtx_dst - (char*)fr->VertexBufferHost, 4);
|
||||||
|
int64_t ib_write_size = MEMALIGN((char*)idx_dst - (char*)fr->IndexBufferHost, 4);
|
||||||
|
wgpuQueueWriteBuffer(bd->defaultQueue, fr->VertexBuffer, 0, fr->VertexBufferHost, vb_write_size);
|
||||||
|
wgpuQueueWriteBuffer(bd->defaultQueue, fr->IndexBuffer, 0, fr->IndexBufferHost, ib_write_size);
|
||||||
|
|
||||||
|
// Setup desired render state
|
||||||
|
ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr);
|
||||||
|
|
||||||
|
// Render command lists
|
||||||
|
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||||
|
int global_vtx_offset = 0;
|
||||||
|
int global_idx_offset = 0;
|
||||||
|
ImVec2 clip_scale = draw_data->FramebufferScale;
|
||||||
|
ImVec2 clip_off = draw_data->DisplayPos;
|
||||||
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||||
|
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||||
|
{
|
||||||
|
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||||
|
if (pcmd->UserCallback != nullptr)
|
||||||
|
{
|
||||||
|
// 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_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr);
|
||||||
|
else
|
||||||
|
pcmd->UserCallback(cmd_list, pcmd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Bind custom texture
|
||||||
|
ImTextureID tex_id = pcmd->GetTexID();
|
||||||
|
ImGuiID tex_id_hash = ImHashData(&tex_id, sizeof(tex_id));
|
||||||
|
auto bind_group = bd->renderResources.ImageBindGroups.GetVoidPtr(tex_id_hash);
|
||||||
|
if (bind_group)
|
||||||
|
{
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, (WGPUBindGroup)bind_group, 0, nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bd->renderResources.ImageBindGroupLayout, (WGPUTextureView)tex_id);
|
||||||
|
bd->renderResources.ImageBindGroups.SetVoidPtr(tex_id_hash, image_bind_group);
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, image_bind_group, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project scissor/clipping rectangles into framebuffer space
|
||||||
|
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||||
|
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||||
|
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Apply scissor/clipping rectangle, Draw
|
||||||
|
wgpuRenderPassEncoderSetScissorRect(pass_encoder, (uint32_t)clip_min.x, (uint32_t)clip_min.y, (uint32_t)(clip_max.x - clip_min.x), (uint32_t)(clip_max.y - clip_min.y));
|
||||||
|
wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||||
|
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWGPU_CreateFontsTexture()
|
||||||
|
{
|
||||||
|
// Build texture atlas
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
unsigned char* pixels;
|
||||||
|
int width, height, size_pp;
|
||||||
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &size_pp);
|
||||||
|
|
||||||
|
// Upload texture to graphics system
|
||||||
|
{
|
||||||
|
WGPUTextureDescriptor tex_desc = {};
|
||||||
|
tex_desc.label = "Dear ImGui Font Texture";
|
||||||
|
tex_desc.dimension = WGPUTextureDimension_2D;
|
||||||
|
tex_desc.size.width = width;
|
||||||
|
tex_desc.size.height = height;
|
||||||
|
tex_desc.size.depthOrArrayLayers = 1;
|
||||||
|
tex_desc.sampleCount = 1;
|
||||||
|
tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
|
||||||
|
tex_desc.mipLevelCount = 1;
|
||||||
|
tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding;
|
||||||
|
bd->renderResources.FontTexture = wgpuDeviceCreateTexture(bd->wgpuDevice, &tex_desc);
|
||||||
|
|
||||||
|
WGPUTextureViewDescriptor tex_view_desc = {};
|
||||||
|
tex_view_desc.format = WGPUTextureFormat_RGBA8Unorm;
|
||||||
|
tex_view_desc.dimension = WGPUTextureViewDimension_2D;
|
||||||
|
tex_view_desc.baseMipLevel = 0;
|
||||||
|
tex_view_desc.mipLevelCount = 1;
|
||||||
|
tex_view_desc.baseArrayLayer = 0;
|
||||||
|
tex_view_desc.arrayLayerCount = 1;
|
||||||
|
tex_view_desc.aspect = WGPUTextureAspect_All;
|
||||||
|
bd->renderResources.FontTextureView = wgpuTextureCreateView(bd->renderResources.FontTexture, &tex_view_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload texture data
|
||||||
|
{
|
||||||
|
WGPUImageCopyTexture dst_view = {};
|
||||||
|
dst_view.texture = bd->renderResources.FontTexture;
|
||||||
|
dst_view.mipLevel = 0;
|
||||||
|
dst_view.origin = { 0, 0, 0 };
|
||||||
|
dst_view.aspect = WGPUTextureAspect_All;
|
||||||
|
WGPUTextureDataLayout layout = {};
|
||||||
|
layout.offset = 0;
|
||||||
|
layout.bytesPerRow = width * size_pp;
|
||||||
|
layout.rowsPerImage = height;
|
||||||
|
WGPUExtent3D size = { (uint32_t)width, (uint32_t)height, 1 };
|
||||||
|
wgpuQueueWriteTexture(bd->defaultQueue, &dst_view, pixels, (uint32_t)(width * size_pp * height), &layout, &size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the associated sampler
|
||||||
|
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||||
|
{
|
||||||
|
WGPUSamplerDescriptor sampler_desc = {};
|
||||||
|
sampler_desc.minFilter = WGPUFilterMode_Linear;
|
||||||
|
sampler_desc.magFilter = WGPUFilterMode_Linear;
|
||||||
|
sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
|
||||||
|
sampler_desc.addressModeU = WGPUAddressMode_Repeat;
|
||||||
|
sampler_desc.addressModeV = WGPUAddressMode_Repeat;
|
||||||
|
sampler_desc.addressModeW = WGPUAddressMode_Repeat;
|
||||||
|
sampler_desc.maxAnisotropy = 1;
|
||||||
|
bd->renderResources.Sampler = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store our identifier
|
||||||
|
static_assert(sizeof(ImTextureID) >= sizeof(bd->renderResources.FontTexture), "Can't pack descriptor handle into TexID, 32-bit not supported yet.");
|
||||||
|
io.Fonts->SetTexID((ImTextureID)bd->renderResources.FontTextureView);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWGPU_CreateUniformBuffer()
|
||||||
|
{
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
WGPUBufferDescriptor ub_desc =
|
||||||
|
{
|
||||||
|
nullptr,
|
||||||
|
"Dear ImGui Uniform buffer",
|
||||||
|
WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform,
|
||||||
|
MEMALIGN(sizeof(Uniforms), 16),
|
||||||
|
false
|
||||||
|
};
|
||||||
|
bd->renderResources.Uniforms = wgpuDeviceCreateBuffer(bd->wgpuDevice, &ub_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplWGPU_CreateDeviceObjects()
|
||||||
|
{
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
if (!bd->wgpuDevice)
|
||||||
|
return false;
|
||||||
|
if (bd->pipelineState)
|
||||||
|
ImGui_ImplWGPU_InvalidateDeviceObjects();
|
||||||
|
|
||||||
|
// Create render pipeline
|
||||||
|
WGPURenderPipelineDescriptor graphics_pipeline_desc = {};
|
||||||
|
graphics_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
|
||||||
|
graphics_pipeline_desc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined;
|
||||||
|
graphics_pipeline_desc.primitive.frontFace = WGPUFrontFace_CW;
|
||||||
|
graphics_pipeline_desc.primitive.cullMode = WGPUCullMode_None;
|
||||||
|
graphics_pipeline_desc.multisample.count = 1;
|
||||||
|
graphics_pipeline_desc.multisample.mask = UINT_MAX;
|
||||||
|
graphics_pipeline_desc.multisample.alphaToCoverageEnabled = false;
|
||||||
|
|
||||||
|
// Bind group layouts
|
||||||
|
WGPUBindGroupLayoutEntry common_bg_layout_entries[2] = {};
|
||||||
|
common_bg_layout_entries[0].binding = 0;
|
||||||
|
common_bg_layout_entries[0].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
|
||||||
|
common_bg_layout_entries[0].buffer.type = WGPUBufferBindingType_Uniform;
|
||||||
|
common_bg_layout_entries[1].binding = 1;
|
||||||
|
common_bg_layout_entries[1].visibility = WGPUShaderStage_Fragment;
|
||||||
|
common_bg_layout_entries[1].sampler.type = WGPUSamplerBindingType_Filtering;
|
||||||
|
|
||||||
|
WGPUBindGroupLayoutEntry image_bg_layout_entries[1] = {};
|
||||||
|
image_bg_layout_entries[0].binding = 0;
|
||||||
|
image_bg_layout_entries[0].visibility = WGPUShaderStage_Fragment;
|
||||||
|
image_bg_layout_entries[0].texture.sampleType = WGPUTextureSampleType_Float;
|
||||||
|
image_bg_layout_entries[0].texture.viewDimension = WGPUTextureViewDimension_2D;
|
||||||
|
|
||||||
|
WGPUBindGroupLayoutDescriptor common_bg_layout_desc = {};
|
||||||
|
common_bg_layout_desc.entryCount = 2;
|
||||||
|
common_bg_layout_desc.entries = common_bg_layout_entries;
|
||||||
|
|
||||||
|
WGPUBindGroupLayoutDescriptor image_bg_layout_desc = {};
|
||||||
|
image_bg_layout_desc.entryCount = 1;
|
||||||
|
image_bg_layout_desc.entries = image_bg_layout_entries;
|
||||||
|
|
||||||
|
WGPUBindGroupLayout bg_layouts[2];
|
||||||
|
bg_layouts[0] = wgpuDeviceCreateBindGroupLayout(bd->wgpuDevice, &common_bg_layout_desc);
|
||||||
|
bg_layouts[1] = wgpuDeviceCreateBindGroupLayout(bd->wgpuDevice, &image_bg_layout_desc);
|
||||||
|
|
||||||
|
WGPUPipelineLayoutDescriptor layout_desc = {};
|
||||||
|
layout_desc.bindGroupLayoutCount = 2;
|
||||||
|
layout_desc.bindGroupLayouts = bg_layouts;
|
||||||
|
graphics_pipeline_desc.layout = wgpuDeviceCreatePipelineLayout(bd->wgpuDevice, &layout_desc);
|
||||||
|
|
||||||
|
// Create the vertex shader
|
||||||
|
WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_vert_wgsl);
|
||||||
|
graphics_pipeline_desc.vertex.module = vertex_shader_desc.module;
|
||||||
|
graphics_pipeline_desc.vertex.entryPoint = vertex_shader_desc.entryPoint;
|
||||||
|
|
||||||
|
// Vertex input configuration
|
||||||
|
WGPUVertexAttribute attribute_desc[] =
|
||||||
|
{
|
||||||
|
{ WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 },
|
||||||
|
{ WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 },
|
||||||
|
{ WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
WGPUVertexBufferLayout buffer_layouts[1];
|
||||||
|
buffer_layouts[0].arrayStride = sizeof(ImDrawVert);
|
||||||
|
buffer_layouts[0].stepMode = WGPUVertexStepMode_Vertex;
|
||||||
|
buffer_layouts[0].attributeCount = 3;
|
||||||
|
buffer_layouts[0].attributes = attribute_desc;
|
||||||
|
|
||||||
|
graphics_pipeline_desc.vertex.bufferCount = 1;
|
||||||
|
graphics_pipeline_desc.vertex.buffers = buffer_layouts;
|
||||||
|
|
||||||
|
// Create the pixel shader
|
||||||
|
WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_frag_wgsl);
|
||||||
|
|
||||||
|
// Create the blending setup
|
||||||
|
WGPUBlendState blend_state = {};
|
||||||
|
blend_state.alpha.operation = WGPUBlendOperation_Add;
|
||||||
|
blend_state.alpha.srcFactor = WGPUBlendFactor_One;
|
||||||
|
blend_state.alpha.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
|
||||||
|
blend_state.color.operation = WGPUBlendOperation_Add;
|
||||||
|
blend_state.color.srcFactor = WGPUBlendFactor_SrcAlpha;
|
||||||
|
blend_state.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
|
||||||
|
|
||||||
|
WGPUColorTargetState color_state = {};
|
||||||
|
color_state.format = bd->renderTargetFormat;
|
||||||
|
color_state.blend = &blend_state;
|
||||||
|
color_state.writeMask = WGPUColorWriteMask_All;
|
||||||
|
|
||||||
|
WGPUFragmentState fragment_state = {};
|
||||||
|
fragment_state.module = pixel_shader_desc.module;
|
||||||
|
fragment_state.entryPoint = pixel_shader_desc.entryPoint;
|
||||||
|
fragment_state.targetCount = 1;
|
||||||
|
fragment_state.targets = &color_state;
|
||||||
|
|
||||||
|
graphics_pipeline_desc.fragment = &fragment_state;
|
||||||
|
|
||||||
|
// Create depth-stencil State
|
||||||
|
WGPUDepthStencilState depth_stencil_state = {};
|
||||||
|
depth_stencil_state.format = bd->depthStencilFormat;
|
||||||
|
depth_stencil_state.depthWriteEnabled = false;
|
||||||
|
depth_stencil_state.depthCompare = WGPUCompareFunction_Always;
|
||||||
|
depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always;
|
||||||
|
depth_stencil_state.stencilBack.compare = WGPUCompareFunction_Always;
|
||||||
|
|
||||||
|
// Configure disabled depth-stencil state
|
||||||
|
graphics_pipeline_desc.depthStencil = (bd->depthStencilFormat == WGPUTextureFormat_Undefined) ? nullptr : &depth_stencil_state;
|
||||||
|
|
||||||
|
bd->pipelineState = wgpuDeviceCreateRenderPipeline(bd->wgpuDevice, &graphics_pipeline_desc);
|
||||||
|
|
||||||
|
ImGui_ImplWGPU_CreateFontsTexture();
|
||||||
|
ImGui_ImplWGPU_CreateUniformBuffer();
|
||||||
|
|
||||||
|
// Create resource bind group
|
||||||
|
WGPUBindGroupEntry common_bg_entries[] =
|
||||||
|
{
|
||||||
|
{ nullptr, 0, bd->renderResources.Uniforms, 0, MEMALIGN(sizeof(Uniforms), 16), 0, 0 },
|
||||||
|
{ nullptr, 1, 0, 0, 0, bd->renderResources.Sampler, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
WGPUBindGroupDescriptor common_bg_descriptor = {};
|
||||||
|
common_bg_descriptor.layout = bg_layouts[0];
|
||||||
|
common_bg_descriptor.entryCount = sizeof(common_bg_entries) / sizeof(WGPUBindGroupEntry);
|
||||||
|
common_bg_descriptor.entries = common_bg_entries;
|
||||||
|
bd->renderResources.CommonBindGroup = wgpuDeviceCreateBindGroup(bd->wgpuDevice, &common_bg_descriptor);
|
||||||
|
|
||||||
|
WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bg_layouts[1], bd->renderResources.FontTextureView);
|
||||||
|
bd->renderResources.ImageBindGroup = image_bind_group;
|
||||||
|
bd->renderResources.ImageBindGroupLayout = bg_layouts[1];
|
||||||
|
bd->renderResources.ImageBindGroups.SetVoidPtr(ImHashData(&bd->renderResources.FontTextureView, sizeof(ImTextureID)), image_bind_group);
|
||||||
|
|
||||||
|
SafeRelease(vertex_shader_desc.module);
|
||||||
|
SafeRelease(pixel_shader_desc.module);
|
||||||
|
SafeRelease(bg_layouts[0]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplWGPU_InvalidateDeviceObjects()
|
||||||
|
{
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
if (!bd->wgpuDevice)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SafeRelease(bd->pipelineState);
|
||||||
|
SafeRelease(bd->renderResources);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.Fonts->SetTexID(0); // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < bd->numFramesInFlight; i++)
|
||||||
|
SafeRelease(bd->pFrameResources[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplWGPU_Data* bd = IM_NEW(ImGui_ImplWGPU_Data)();
|
||||||
|
io.BackendRendererUserData = (void*)bd;
|
||||||
|
io.BackendRendererName = "imgui_impl_webgpu";
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||||
|
|
||||||
|
bd->wgpuDevice = device;
|
||||||
|
bd->defaultQueue = wgpuDeviceGetQueue(bd->wgpuDevice);
|
||||||
|
bd->renderTargetFormat = rt_format;
|
||||||
|
bd->depthStencilFormat = depth_format;
|
||||||
|
bd->numFramesInFlight = num_frames_in_flight;
|
||||||
|
bd->frameIndex = UINT_MAX;
|
||||||
|
|
||||||
|
bd->renderResources.FontTexture = nullptr;
|
||||||
|
bd->renderResources.FontTextureView = nullptr;
|
||||||
|
bd->renderResources.Sampler = nullptr;
|
||||||
|
bd->renderResources.Uniforms = nullptr;
|
||||||
|
bd->renderResources.CommonBindGroup = nullptr;
|
||||||
|
bd->renderResources.ImageBindGroups.Data.reserve(100);
|
||||||
|
bd->renderResources.ImageBindGroup = nullptr;
|
||||||
|
bd->renderResources.ImageBindGroupLayout = nullptr;
|
||||||
|
|
||||||
|
// Create buffers with a default size (they will later be grown as needed)
|
||||||
|
bd->pFrameResources = new FrameResources[num_frames_in_flight];
|
||||||
|
for (int i = 0; i < num_frames_in_flight; i++)
|
||||||
|
{
|
||||||
|
FrameResources* fr = &bd->pFrameResources[i];
|
||||||
|
fr->IndexBuffer = nullptr;
|
||||||
|
fr->VertexBuffer = nullptr;
|
||||||
|
fr->IndexBufferHost = nullptr;
|
||||||
|
fr->VertexBufferHost = nullptr;
|
||||||
|
fr->IndexBufferSize = 10000;
|
||||||
|
fr->VertexBufferSize = 5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplWGPU_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
ImGui_ImplWGPU_InvalidateDeviceObjects();
|
||||||
|
delete[] bd->pFrameResources;
|
||||||
|
bd->pFrameResources = nullptr;
|
||||||
|
wgpuQueueRelease(bd->defaultQueue);
|
||||||
|
bd->wgpuDevice = nullptr;
|
||||||
|
bd->numFramesInFlight = 0;
|
||||||
|
bd->frameIndex = UINT_MAX;
|
||||||
|
|
||||||
|
io.BackendRendererName = nullptr;
|
||||||
|
io.BackendRendererUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplWGPU_NewFrame()
|
||||||
|
{
|
||||||
|
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
|
||||||
|
if (!bd->pipelineState)
|
||||||
|
ImGui_ImplWGPU_CreateDeviceObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
32
backends/imgui_impl_wgpu.h
Normal file
32
backends/imgui_impl_wgpu.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// dear imgui: Renderer for WebGPU
|
||||||
|
// This needs to be used along with a Platform Binding (e.g. GLFW)
|
||||||
|
// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
#include <webgpu/webgpu.h>
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format = WGPUTextureFormat_Undefined);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder);
|
||||||
|
|
||||||
|
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
903
backends/imgui_impl_win32.cpp
Normal file
903
backends/imgui_impl_win32.cpp
Normal file
@ -0,0 +1,903 @@
|
|||||||
|
// dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications)
|
||||||
|
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_win32.h"
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#include <windowsx.h> // GET_X_LPARAM(), GET_Y_LPARAM()
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <dwmapi.h>
|
||||||
|
|
||||||
|
// Configuration flags to add in your imconfig.h file:
|
||||||
|
//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant.
|
||||||
|
|
||||||
|
// Using XInput for gamepad (will load DLL dynamically)
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
#include <xinput.h>
|
||||||
|
typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*);
|
||||||
|
typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
|
||||||
|
// 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL).
|
||||||
|
// 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window.
|
||||||
|
// 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218)
|
||||||
|
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen. (#2702)
|
||||||
|
// 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162)
|
||||||
|
// 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
|
||||||
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||||
|
// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode).
|
||||||
|
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
|
||||||
|
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
|
||||||
|
// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
|
||||||
|
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
|
||||||
|
// 2022-01-17: Inputs: always update key mods next and before a key event (not in NewFrame) to fix input queue with very low framerates.
|
||||||
|
// 2022-01-12: Inputs: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API.
|
||||||
|
// 2022-01-12: Inputs: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted.
|
||||||
|
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
|
||||||
|
// 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness.
|
||||||
|
// 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages.
|
||||||
|
// 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus.
|
||||||
|
// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using TrackMouseEvent() to receive WM_MOUSELEAVE events).
|
||||||
|
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
|
// 2021-06-08: Fixed ImGui_ImplWin32_EnableDpiAwareness() and ImGui_ImplWin32_GetDpiScaleForMonitor() to handle Windows 8.1/10 features without a manifest (per-monitor DPI, and properly calls SetProcessDpiAwareness() on 8.1).
|
||||||
|
// 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS).
|
||||||
|
// 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi).
|
||||||
|
// 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1.
|
||||||
|
// 2021-01-25: Inputs: Dynamically loading XInput DLL.
|
||||||
|
// 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed.
|
||||||
|
// 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs)
|
||||||
|
// 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions.
|
||||||
|
// 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT.
|
||||||
|
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
|
||||||
|
// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter().
|
||||||
|
// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent.
|
||||||
|
// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages.
|
||||||
|
// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
|
||||||
|
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
|
||||||
|
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
|
||||||
|
// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
|
||||||
|
// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
|
||||||
|
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
|
||||||
|
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
|
||||||
|
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
|
||||||
|
// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
|
||||||
|
// 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-01-20: Inputs: Added Horizontal Mouse Wheel support.
|
||||||
|
// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
|
||||||
|
// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
|
||||||
|
// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
|
||||||
|
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
|
||||||
|
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set.
|
||||||
|
|
||||||
|
struct ImGui_ImplWin32_Data
|
||||||
|
{
|
||||||
|
HWND hWnd;
|
||||||
|
HWND MouseHwnd;
|
||||||
|
int MouseTrackedArea; // 0: not tracked, 1: client are, 2: non-client area
|
||||||
|
int MouseButtonsDown;
|
||||||
|
INT64 Time;
|
||||||
|
INT64 TicksPerSecond;
|
||||||
|
ImGuiMouseCursor LastMouseCursor;
|
||||||
|
UINT32 KeyboardCodePage;
|
||||||
|
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
bool HasGamepad;
|
||||||
|
bool WantUpdateHasGamepad;
|
||||||
|
HMODULE XInputDLL;
|
||||||
|
PFN_XInputGetCapabilities XInputGetCapabilities;
|
||||||
|
PFN_XInputGetState XInputGetState;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ImGui_ImplWin32_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
|
||||||
|
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
|
||||||
|
static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplWin32_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
static void ImGui_ImplWin32_UpdateKeyboardCodePage()
|
||||||
|
{
|
||||||
|
// Retrieve keyboard code page, required for handling of non-Unicode Windows.
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
HKL keyboard_layout = ::GetKeyboardLayout(0);
|
||||||
|
LCID keyboard_lcid = MAKELCID(HIWORD(keyboard_layout), SORT_DEFAULT);
|
||||||
|
if (::GetLocaleInfoA(keyboard_lcid, (LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE), (LPSTR)&bd->KeyboardCodePage, sizeof(bd->KeyboardCodePage)) == 0)
|
||||||
|
bd->KeyboardCodePage = CP_ACP; // Fallback to default ANSI code page when fails.
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||||
|
|
||||||
|
INT64 perf_frequency, perf_counter;
|
||||||
|
if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&perf_frequency))
|
||||||
|
return false;
|
||||||
|
if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)();
|
||||||
|
io.BackendPlatformUserData = (void*)bd;
|
||||||
|
io.BackendPlatformName = "imgui_impl_win32";
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||||
|
|
||||||
|
bd->hWnd = (HWND)hwnd;
|
||||||
|
bd->TicksPerSecond = perf_frequency;
|
||||||
|
bd->Time = perf_counter;
|
||||||
|
bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||||
|
ImGui_ImplWin32_UpdateKeyboardCodePage();
|
||||||
|
|
||||||
|
// Set platform dependent data in viewport
|
||||||
|
ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd;
|
||||||
|
IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch
|
||||||
|
|
||||||
|
// Dynamically load XInput library
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
bd->WantUpdateHasGamepad = true;
|
||||||
|
const char* xinput_dll_names[] =
|
||||||
|
{
|
||||||
|
"xinput1_4.dll", // Windows 8+
|
||||||
|
"xinput1_3.dll", // DirectX SDK
|
||||||
|
"xinput9_1_0.dll", // Windows Vista, Windows 7
|
||||||
|
"xinput1_2.dll", // DirectX SDK
|
||||||
|
"xinput1_1.dll" // DirectX SDK
|
||||||
|
};
|
||||||
|
for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++)
|
||||||
|
if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n]))
|
||||||
|
{
|
||||||
|
bd->XInputDLL = dll;
|
||||||
|
bd->XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities");
|
||||||
|
bd->XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd)
|
||||||
|
{
|
||||||
|
return ImGui_ImplWin32_InitEx(hwnd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd)
|
||||||
|
{
|
||||||
|
// OpenGL needs CS_OWNDC
|
||||||
|
return ImGui_ImplWin32_InitEx(hwnd, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplWin32_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Unload XInput library
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
if (bd->XInputDLL)
|
||||||
|
::FreeLibrary(bd->XInputDLL);
|
||||||
|
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
|
||||||
|
io.BackendPlatformName = nullptr;
|
||||||
|
io.BackendPlatformUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplWin32_UpdateMouseCursor()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||||
|
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
|
||||||
|
{
|
||||||
|
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||||
|
::SetCursor(nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Show OS mouse cursor
|
||||||
|
LPTSTR win32_cursor = IDC_ARROW;
|
||||||
|
switch (imgui_cursor)
|
||||||
|
{
|
||||||
|
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
|
||||||
|
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
|
||||||
|
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
|
||||||
|
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
|
||||||
|
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
|
||||||
|
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
|
||||||
|
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
|
||||||
|
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
|
||||||
|
case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break;
|
||||||
|
}
|
||||||
|
::SetCursor(::LoadCursor(nullptr, win32_cursor));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsVkDown(int vk)
|
||||||
|
{
|
||||||
|
return (::GetKeyState(vk) & 0x8000) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_AddKeyEvent(ImGuiKey key, bool down, int native_keycode, int native_scancode = -1)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(key, down);
|
||||||
|
io.SetKeyEventNativeData(key, native_keycode, native_scancode); // To support legacy indexing (<1.87 user code)
|
||||||
|
IM_UNUSED(native_scancode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds()
|
||||||
|
{
|
||||||
|
// Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one.
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && !IsVkDown(VK_LSHIFT))
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, false, VK_LSHIFT);
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_RightShift) && !IsVkDown(VK_RSHIFT))
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, false, VK_RSHIFT);
|
||||||
|
|
||||||
|
// Sometimes WM_KEYUP for Win key is not passed down to the app (e.g. for Win+V on some setups, according to GLFW).
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_LeftSuper) && !IsVkDown(VK_LWIN))
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftSuper, false, VK_LWIN);
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_RightSuper) && !IsVkDown(VK_RWIN))
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightSuper, false, VK_RWIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_UpdateKeyModifiers()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL));
|
||||||
|
io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT));
|
||||||
|
io.AddKeyEvent(ImGuiMod_Alt, IsVkDown(VK_MENU));
|
||||||
|
io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_UpdateMouseData()
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(bd->hWnd != 0);
|
||||||
|
|
||||||
|
HWND focused_window = ::GetForegroundWindow();
|
||||||
|
const bool is_app_focused = (focused_window == bd->hWnd);
|
||||||
|
if (is_app_focused)
|
||||||
|
{
|
||||||
|
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||||
|
if (io.WantSetMousePos)
|
||||||
|
{
|
||||||
|
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
|
||||||
|
if (::ClientToScreen(bd->hWnd, &pos))
|
||||||
|
::SetCursorPos(pos.x, pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
|
||||||
|
// This also fills a short gap when clicking non-client area: WM_NCMOUSELEAVE -> modal OS move -> gap -> WM_NCMOUSEMOVE
|
||||||
|
if (!io.WantSetMousePos && bd->MouseTrackedArea == 0)
|
||||||
|
{
|
||||||
|
POINT pos;
|
||||||
|
if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos))
|
||||||
|
io.AddMousePosEvent((float)pos.x, (float)pos.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gamepad navigation mapping
|
||||||
|
static void ImGui_ImplWin32_UpdateGamepads()
|
||||||
|
{
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
//if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
|
||||||
|
// return;
|
||||||
|
|
||||||
|
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
|
||||||
|
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
|
||||||
|
if (bd->WantUpdateHasGamepad)
|
||||||
|
{
|
||||||
|
XINPUT_CAPABILITIES caps = {};
|
||||||
|
bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false;
|
||||||
|
bd->WantUpdateHasGamepad = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||||
|
XINPUT_STATE xinput_state;
|
||||||
|
XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
|
||||||
|
if (!bd->HasGamepad || bd->XInputGetState == nullptr || bd->XInputGetState(0, &xinput_state) != ERROR_SUCCESS)
|
||||||
|
return;
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||||
|
|
||||||
|
#define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)
|
||||||
|
#define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0); }
|
||||||
|
#define MAP_ANALOG(KEY_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); io.AddKeyAnalogEvent(KEY_NO, vn > 0.10f, IM_SATURATE(vn)); }
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadStart, XINPUT_GAMEPAD_START);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadBack, XINPUT_GAMEPAD_BACK);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceLeft, XINPUT_GAMEPAD_X);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceRight, XINPUT_GAMEPAD_B);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceUp, XINPUT_GAMEPAD_Y);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceDown, XINPUT_GAMEPAD_A);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadLeft, XINPUT_GAMEPAD_DPAD_LEFT);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadRight, XINPUT_GAMEPAD_DPAD_RIGHT);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadUp, XINPUT_GAMEPAD_DPAD_UP);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadDown, XINPUT_GAMEPAD_DPAD_DOWN);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL1, XINPUT_GAMEPAD_LEFT_SHOULDER);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR1, XINPUT_GAMEPAD_RIGHT_SHOULDER);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadL2, gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadR2, gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL3, XINPUT_GAMEPAD_LEFT_THUMB);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR3, XINPUT_GAMEPAD_RIGHT_THUMB);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickLeft, gamepad.sThumbRX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickRight, gamepad.sThumbRX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickUp, gamepad.sThumbRY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickDown, gamepad.sThumbRY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||||
|
#undef MAP_BUTTON
|
||||||
|
#undef MAP_ANALOG
|
||||||
|
#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplWin32_NewFrame()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplWin32_Init()?");
|
||||||
|
|
||||||
|
// Setup display size (every frame to accommodate for window resizing)
|
||||||
|
RECT rect = { 0, 0, 0, 0 };
|
||||||
|
::GetClientRect(bd->hWnd, &rect);
|
||||||
|
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
|
||||||
|
|
||||||
|
// Setup time step
|
||||||
|
INT64 current_time = 0;
|
||||||
|
::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time);
|
||||||
|
io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond;
|
||||||
|
bd->Time = current_time;
|
||||||
|
|
||||||
|
// Update OS mouse position
|
||||||
|
ImGui_ImplWin32_UpdateMouseData();
|
||||||
|
|
||||||
|
// Process workarounds for known Windows key handling issues
|
||||||
|
ImGui_ImplWin32_ProcessKeyEventsWorkarounds();
|
||||||
|
|
||||||
|
// Update OS mouse cursor with the cursor requested by imgui
|
||||||
|
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
|
||||||
|
if (bd->LastMouseCursor != mouse_cursor)
|
||||||
|
{
|
||||||
|
bd->LastMouseCursor = mouse_cursor;
|
||||||
|
ImGui_ImplWin32_UpdateMouseCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update game controllers (if enabled and available)
|
||||||
|
ImGui_ImplWin32_UpdateGamepads();
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255)
|
||||||
|
#define IM_VK_KEYPAD_ENTER (VK_RETURN + 256)
|
||||||
|
|
||||||
|
// Map VK_xxx to ImGuiKey_xxx.
|
||||||
|
static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
|
||||||
|
{
|
||||||
|
switch (wParam)
|
||||||
|
{
|
||||||
|
case VK_TAB: return ImGuiKey_Tab;
|
||||||
|
case VK_LEFT: return ImGuiKey_LeftArrow;
|
||||||
|
case VK_RIGHT: return ImGuiKey_RightArrow;
|
||||||
|
case VK_UP: return ImGuiKey_UpArrow;
|
||||||
|
case VK_DOWN: return ImGuiKey_DownArrow;
|
||||||
|
case VK_PRIOR: return ImGuiKey_PageUp;
|
||||||
|
case VK_NEXT: return ImGuiKey_PageDown;
|
||||||
|
case VK_HOME: return ImGuiKey_Home;
|
||||||
|
case VK_END: return ImGuiKey_End;
|
||||||
|
case VK_INSERT: return ImGuiKey_Insert;
|
||||||
|
case VK_DELETE: return ImGuiKey_Delete;
|
||||||
|
case VK_BACK: return ImGuiKey_Backspace;
|
||||||
|
case VK_SPACE: return ImGuiKey_Space;
|
||||||
|
case VK_RETURN: return ImGuiKey_Enter;
|
||||||
|
case VK_ESCAPE: return ImGuiKey_Escape;
|
||||||
|
case VK_OEM_7: return ImGuiKey_Apostrophe;
|
||||||
|
case VK_OEM_COMMA: return ImGuiKey_Comma;
|
||||||
|
case VK_OEM_MINUS: return ImGuiKey_Minus;
|
||||||
|
case VK_OEM_PERIOD: return ImGuiKey_Period;
|
||||||
|
case VK_OEM_2: return ImGuiKey_Slash;
|
||||||
|
case VK_OEM_1: return ImGuiKey_Semicolon;
|
||||||
|
case VK_OEM_PLUS: return ImGuiKey_Equal;
|
||||||
|
case VK_OEM_4: return ImGuiKey_LeftBracket;
|
||||||
|
case VK_OEM_5: return ImGuiKey_Backslash;
|
||||||
|
case VK_OEM_6: return ImGuiKey_RightBracket;
|
||||||
|
case VK_OEM_3: return ImGuiKey_GraveAccent;
|
||||||
|
case VK_CAPITAL: return ImGuiKey_CapsLock;
|
||||||
|
case VK_SCROLL: return ImGuiKey_ScrollLock;
|
||||||
|
case VK_NUMLOCK: return ImGuiKey_NumLock;
|
||||||
|
case VK_SNAPSHOT: return ImGuiKey_PrintScreen;
|
||||||
|
case VK_PAUSE: return ImGuiKey_Pause;
|
||||||
|
case VK_NUMPAD0: return ImGuiKey_Keypad0;
|
||||||
|
case VK_NUMPAD1: return ImGuiKey_Keypad1;
|
||||||
|
case VK_NUMPAD2: return ImGuiKey_Keypad2;
|
||||||
|
case VK_NUMPAD3: return ImGuiKey_Keypad3;
|
||||||
|
case VK_NUMPAD4: return ImGuiKey_Keypad4;
|
||||||
|
case VK_NUMPAD5: return ImGuiKey_Keypad5;
|
||||||
|
case VK_NUMPAD6: return ImGuiKey_Keypad6;
|
||||||
|
case VK_NUMPAD7: return ImGuiKey_Keypad7;
|
||||||
|
case VK_NUMPAD8: return ImGuiKey_Keypad8;
|
||||||
|
case VK_NUMPAD9: return ImGuiKey_Keypad9;
|
||||||
|
case VK_DECIMAL: return ImGuiKey_KeypadDecimal;
|
||||||
|
case VK_DIVIDE: return ImGuiKey_KeypadDivide;
|
||||||
|
case VK_MULTIPLY: return ImGuiKey_KeypadMultiply;
|
||||||
|
case VK_SUBTRACT: return ImGuiKey_KeypadSubtract;
|
||||||
|
case VK_ADD: return ImGuiKey_KeypadAdd;
|
||||||
|
case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter;
|
||||||
|
case VK_LSHIFT: return ImGuiKey_LeftShift;
|
||||||
|
case VK_LCONTROL: return ImGuiKey_LeftCtrl;
|
||||||
|
case VK_LMENU: return ImGuiKey_LeftAlt;
|
||||||
|
case VK_LWIN: return ImGuiKey_LeftSuper;
|
||||||
|
case VK_RSHIFT: return ImGuiKey_RightShift;
|
||||||
|
case VK_RCONTROL: return ImGuiKey_RightCtrl;
|
||||||
|
case VK_RMENU: return ImGuiKey_RightAlt;
|
||||||
|
case VK_RWIN: return ImGuiKey_RightSuper;
|
||||||
|
case VK_APPS: return ImGuiKey_Menu;
|
||||||
|
case '0': return ImGuiKey_0;
|
||||||
|
case '1': return ImGuiKey_1;
|
||||||
|
case '2': return ImGuiKey_2;
|
||||||
|
case '3': return ImGuiKey_3;
|
||||||
|
case '4': return ImGuiKey_4;
|
||||||
|
case '5': return ImGuiKey_5;
|
||||||
|
case '6': return ImGuiKey_6;
|
||||||
|
case '7': return ImGuiKey_7;
|
||||||
|
case '8': return ImGuiKey_8;
|
||||||
|
case '9': return ImGuiKey_9;
|
||||||
|
case 'A': return ImGuiKey_A;
|
||||||
|
case 'B': return ImGuiKey_B;
|
||||||
|
case 'C': return ImGuiKey_C;
|
||||||
|
case 'D': return ImGuiKey_D;
|
||||||
|
case 'E': return ImGuiKey_E;
|
||||||
|
case 'F': return ImGuiKey_F;
|
||||||
|
case 'G': return ImGuiKey_G;
|
||||||
|
case 'H': return ImGuiKey_H;
|
||||||
|
case 'I': return ImGuiKey_I;
|
||||||
|
case 'J': return ImGuiKey_J;
|
||||||
|
case 'K': return ImGuiKey_K;
|
||||||
|
case 'L': return ImGuiKey_L;
|
||||||
|
case 'M': return ImGuiKey_M;
|
||||||
|
case 'N': return ImGuiKey_N;
|
||||||
|
case 'O': return ImGuiKey_O;
|
||||||
|
case 'P': return ImGuiKey_P;
|
||||||
|
case 'Q': return ImGuiKey_Q;
|
||||||
|
case 'R': return ImGuiKey_R;
|
||||||
|
case 'S': return ImGuiKey_S;
|
||||||
|
case 'T': return ImGuiKey_T;
|
||||||
|
case 'U': return ImGuiKey_U;
|
||||||
|
case 'V': return ImGuiKey_V;
|
||||||
|
case 'W': return ImGuiKey_W;
|
||||||
|
case 'X': return ImGuiKey_X;
|
||||||
|
case 'Y': return ImGuiKey_Y;
|
||||||
|
case 'Z': return ImGuiKey_Z;
|
||||||
|
case VK_F1: return ImGuiKey_F1;
|
||||||
|
case VK_F2: return ImGuiKey_F2;
|
||||||
|
case VK_F3: return ImGuiKey_F3;
|
||||||
|
case VK_F4: return ImGuiKey_F4;
|
||||||
|
case VK_F5: return ImGuiKey_F5;
|
||||||
|
case VK_F6: return ImGuiKey_F6;
|
||||||
|
case VK_F7: return ImGuiKey_F7;
|
||||||
|
case VK_F8: return ImGuiKey_F8;
|
||||||
|
case VK_F9: return ImGuiKey_F9;
|
||||||
|
case VK_F10: return ImGuiKey_F10;
|
||||||
|
case VK_F11: return ImGuiKey_F11;
|
||||||
|
case VK_F12: return ImGuiKey_F12;
|
||||||
|
case VK_F13: return ImGuiKey_F13;
|
||||||
|
case VK_F14: return ImGuiKey_F14;
|
||||||
|
case VK_F15: return ImGuiKey_F15;
|
||||||
|
case VK_F16: return ImGuiKey_F16;
|
||||||
|
case VK_F17: return ImGuiKey_F17;
|
||||||
|
case VK_F18: return ImGuiKey_F18;
|
||||||
|
case VK_F19: return ImGuiKey_F19;
|
||||||
|
case VK_F20: return ImGuiKey_F20;
|
||||||
|
case VK_F21: return ImGuiKey_F21;
|
||||||
|
case VK_F22: return ImGuiKey_F22;
|
||||||
|
case VK_F23: return ImGuiKey_F23;
|
||||||
|
case VK_F24: return ImGuiKey_F24;
|
||||||
|
case VK_BROWSER_BACK: return ImGuiKey_AppBack;
|
||||||
|
case VK_BROWSER_FORWARD: return ImGuiKey_AppForward;
|
||||||
|
default: return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
|
||||||
|
#ifndef WM_MOUSEHWHEEL
|
||||||
|
#define WM_MOUSEHWHEEL 0x020E
|
||||||
|
#endif
|
||||||
|
#ifndef DBT_DEVNODES_CHANGED
|
||||||
|
#define DBT_DEVNODES_CHANGED 0x0007
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Win32 message handler (process Win32 mouse/keyboard inputs, etc.)
|
||||||
|
// Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
|
||||||
|
// When implementing your own backend, you can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if Dear ImGui wants to use your inputs.
|
||||||
|
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
||||||
|
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
||||||
|
// Generally you may always pass all inputs to Dear ImGui, and hide them from your application based on those two flags.
|
||||||
|
// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
|
||||||
|
// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
|
||||||
|
#if 0
|
||||||
|
// Copy this line into your .cpp file to forward declare the function.
|
||||||
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// See https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages
|
||||||
|
// Prefer to call this at the top of the message handler to avoid the possibility of other Win32 calls interfering with this.
|
||||||
|
static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()
|
||||||
|
{
|
||||||
|
LPARAM extra_info = ::GetMessageExtraInfo();
|
||||||
|
if ((extra_info & 0xFFFFFF80) == 0xFF515700)
|
||||||
|
return ImGuiMouseSource_Pen;
|
||||||
|
if ((extra_info & 0xFFFFFF80) == 0xFF515780)
|
||||||
|
return ImGuiMouseSource_TouchScreen;
|
||||||
|
return ImGuiMouseSource_Mouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (ImGui::GetCurrentContext() == nullptr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case WM_MOUSEMOVE:
|
||||||
|
case WM_NCMOUSEMOVE:
|
||||||
|
{
|
||||||
|
// We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
|
||||||
|
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||||
|
const int area = (msg == WM_MOUSEMOVE) ? 1 : 2;
|
||||||
|
bd->MouseHwnd = hwnd;
|
||||||
|
if (bd->MouseTrackedArea != area)
|
||||||
|
{
|
||||||
|
TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
|
||||||
|
TRACKMOUSEEVENT tme_track = { sizeof(tme_track), (DWORD)((area == 2) ? (TME_LEAVE | TME_NONCLIENT) : TME_LEAVE), hwnd, 0 };
|
||||||
|
if (bd->MouseTrackedArea != 0)
|
||||||
|
::TrackMouseEvent(&tme_cancel);
|
||||||
|
::TrackMouseEvent(&tme_track);
|
||||||
|
bd->MouseTrackedArea = area;
|
||||||
|
}
|
||||||
|
POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) };
|
||||||
|
if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &mouse_pos) == FALSE) // WM_NCMOUSEMOVE are provided in absolute coordinates.
|
||||||
|
break;
|
||||||
|
io.AddMouseSourceEvent(mouse_source);
|
||||||
|
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_MOUSELEAVE:
|
||||||
|
case WM_NCMOUSELEAVE:
|
||||||
|
{
|
||||||
|
const int area = (msg == WM_MOUSELEAVE) ? 1 : 2;
|
||||||
|
if (bd->MouseTrackedArea == area)
|
||||||
|
{
|
||||||
|
if (bd->MouseHwnd == hwnd)
|
||||||
|
bd->MouseHwnd = nullptr;
|
||||||
|
bd->MouseTrackedArea = 0;
|
||||||
|
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
|
||||||
|
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
|
||||||
|
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
|
||||||
|
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
|
||||||
|
{
|
||||||
|
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||||
|
int button = 0;
|
||||||
|
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
|
||||||
|
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
|
||||||
|
if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
|
||||||
|
if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
|
||||||
|
if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr)
|
||||||
|
::SetCapture(hwnd);
|
||||||
|
bd->MouseButtonsDown |= 1 << button;
|
||||||
|
io.AddMouseSourceEvent(mouse_source);
|
||||||
|
io.AddMouseButtonEvent(button, true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
case WM_MBUTTONUP:
|
||||||
|
case WM_XBUTTONUP:
|
||||||
|
{
|
||||||
|
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||||
|
int button = 0;
|
||||||
|
if (msg == WM_LBUTTONUP) { button = 0; }
|
||||||
|
if (msg == WM_RBUTTONUP) { button = 1; }
|
||||||
|
if (msg == WM_MBUTTONUP) { button = 2; }
|
||||||
|
if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
|
||||||
|
bd->MouseButtonsDown &= ~(1 << button);
|
||||||
|
if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd)
|
||||||
|
::ReleaseCapture();
|
||||||
|
io.AddMouseSourceEvent(mouse_source);
|
||||||
|
io.AddMouseButtonEvent(button, false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_MOUSEWHEEL:
|
||||||
|
io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA);
|
||||||
|
return 0;
|
||||||
|
case WM_MOUSEHWHEEL:
|
||||||
|
io.AddMouseWheelEvent(-(float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f);
|
||||||
|
return 0;
|
||||||
|
case WM_KEYDOWN:
|
||||||
|
case WM_KEYUP:
|
||||||
|
case WM_SYSKEYDOWN:
|
||||||
|
case WM_SYSKEYUP:
|
||||||
|
{
|
||||||
|
const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN);
|
||||||
|
if (wParam < 256)
|
||||||
|
{
|
||||||
|
// Submit modifiers
|
||||||
|
ImGui_ImplWin32_UpdateKeyModifiers();
|
||||||
|
|
||||||
|
// Obtain virtual key code
|
||||||
|
// (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.)
|
||||||
|
int vk = (int)wParam;
|
||||||
|
if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
|
||||||
|
vk = IM_VK_KEYPAD_ENTER;
|
||||||
|
const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk);
|
||||||
|
const int scancode = (int)LOBYTE(HIWORD(lParam));
|
||||||
|
|
||||||
|
// Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event.
|
||||||
|
if (key == ImGuiKey_PrintScreen && !is_key_down)
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(key, true, vk, scancode);
|
||||||
|
|
||||||
|
// Submit key event
|
||||||
|
if (key != ImGuiKey_None)
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode);
|
||||||
|
|
||||||
|
// Submit individual left/right modifier events
|
||||||
|
if (vk == VK_SHIFT)
|
||||||
|
{
|
||||||
|
// Important: Shift keys tend to get stuck when pressed together, missing key-up events are corrected in ImGui_ImplWin32_ProcessKeyEventsWorkarounds()
|
||||||
|
if (IsVkDown(VK_LSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, is_key_down, VK_LSHIFT, scancode); }
|
||||||
|
if (IsVkDown(VK_RSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, is_key_down, VK_RSHIFT, scancode); }
|
||||||
|
}
|
||||||
|
else if (vk == VK_CONTROL)
|
||||||
|
{
|
||||||
|
if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftCtrl, is_key_down, VK_LCONTROL, scancode); }
|
||||||
|
if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightCtrl, is_key_down, VK_RCONTROL, scancode); }
|
||||||
|
}
|
||||||
|
else if (vk == VK_MENU)
|
||||||
|
{
|
||||||
|
if (IsVkDown(VK_LMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftAlt, is_key_down, VK_LMENU, scancode); }
|
||||||
|
if (IsVkDown(VK_RMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightAlt, is_key_down, VK_RMENU, scancode); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_SETFOCUS:
|
||||||
|
case WM_KILLFOCUS:
|
||||||
|
io.AddFocusEvent(msg == WM_SETFOCUS);
|
||||||
|
return 0;
|
||||||
|
case WM_INPUTLANGCHANGE:
|
||||||
|
ImGui_ImplWin32_UpdateKeyboardCodePage();
|
||||||
|
return 0;
|
||||||
|
case WM_CHAR:
|
||||||
|
if (::IsWindowUnicode(hwnd))
|
||||||
|
{
|
||||||
|
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
|
||||||
|
if (wParam > 0 && wParam < 0x10000)
|
||||||
|
io.AddInputCharacterUTF16((unsigned short)wParam);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wchar_t wch = 0;
|
||||||
|
::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1);
|
||||||
|
io.AddInputCharacter(wch);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
case WM_SETCURSOR:
|
||||||
|
// This is required to restore cursor when transitioning from e.g resize borders to client area.
|
||||||
|
if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
case WM_DEVICECHANGE:
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
if ((UINT)wParam == DBT_DEVNODES_CHANGED)
|
||||||
|
bd->WantUpdateHasGamepad = true;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
// DPI-related helpers (optional)
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
// - Use to enable DPI awareness without having to create an application manifest.
|
||||||
|
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
|
||||||
|
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
|
||||||
|
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
|
||||||
|
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
// This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable.
|
||||||
|
// ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically.
|
||||||
|
// If you are trying to implement your own backend for your own engine, you may ignore that noise.
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Perform our own check with RtlVerifyVersionInfo() instead of using functions from <VersionHelpers.h> as they
|
||||||
|
// require a manifest to be functional for checks above 8.1. See https://github.com/ocornut/imgui/issues/4200
|
||||||
|
static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD)
|
||||||
|
{
|
||||||
|
typedef LONG(WINAPI* PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*, ULONG, ULONGLONG);
|
||||||
|
static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = nullptr;
|
||||||
|
if (RtlVerifyVersionInfoFn == nullptr)
|
||||||
|
if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll"))
|
||||||
|
RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo");
|
||||||
|
if (RtlVerifyVersionInfoFn == nullptr)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
RTL_OSVERSIONINFOEXW versionInfo = { };
|
||||||
|
ULONGLONG conditionMask = 0;
|
||||||
|
versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
|
||||||
|
versionInfo.dwMajorVersion = major;
|
||||||
|
versionInfo.dwMinorVersion = minor;
|
||||||
|
VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
||||||
|
VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
|
||||||
|
return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) == 0) ? TRUE : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _IsWindowsVistaOrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA
|
||||||
|
#define _IsWindows8OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8
|
||||||
|
#define _IsWindows8Point1OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE
|
||||||
|
#define _IsWindows10OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WINTHRESHOLD / _WIN32_WINNT_WIN10
|
||||||
|
|
||||||
|
#ifndef DPI_ENUMS_DECLARED
|
||||||
|
typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
|
||||||
|
typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
|
||||||
|
#endif
|
||||||
|
#ifndef _DPI_AWARENESS_CONTEXTS_
|
||||||
|
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
|
||||||
|
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3
|
||||||
|
#endif
|
||||||
|
#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
|
||||||
|
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4
|
||||||
|
#endif
|
||||||
|
typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+
|
||||||
|
typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+
|
||||||
|
typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update)
|
||||||
|
|
||||||
|
// Helper function to enable DPI awareness without setting up a manifest
|
||||||
|
void ImGui_ImplWin32_EnableDpiAwareness()
|
||||||
|
{
|
||||||
|
if (_IsWindows10OrGreater())
|
||||||
|
{
|
||||||
|
static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
|
||||||
|
if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"))
|
||||||
|
{
|
||||||
|
SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_IsWindows8Point1OrGreater())
|
||||||
|
{
|
||||||
|
static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
|
||||||
|
if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness"))
|
||||||
|
{
|
||||||
|
SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if _WIN32_WINNT >= 0x0600
|
||||||
|
::SetProcessDPIAware();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(NOGDI)
|
||||||
|
#pragma comment(lib, "gdi32") // Link with gdi32.lib for GetDeviceCaps(). MinGW will require linking with '-lgdi32'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor)
|
||||||
|
{
|
||||||
|
UINT xdpi = 96, ydpi = 96;
|
||||||
|
if (_IsWindows8Point1OrGreater())
|
||||||
|
{
|
||||||
|
static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
|
||||||
|
static PFN_GetDpiForMonitor GetDpiForMonitorFn = nullptr;
|
||||||
|
if (GetDpiForMonitorFn == nullptr && shcore_dll != nullptr)
|
||||||
|
GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor");
|
||||||
|
if (GetDpiForMonitorFn != nullptr)
|
||||||
|
{
|
||||||
|
GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
|
||||||
|
IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
|
||||||
|
return xdpi / 96.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef NOGDI
|
||||||
|
const HDC dc = ::GetDC(nullptr);
|
||||||
|
xdpi = ::GetDeviceCaps(dc, LOGPIXELSX);
|
||||||
|
ydpi = ::GetDeviceCaps(dc, LOGPIXELSY);
|
||||||
|
IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
|
||||||
|
::ReleaseDC(nullptr, dc);
|
||||||
|
#endif
|
||||||
|
return xdpi / 96.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd)
|
||||||
|
{
|
||||||
|
HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST);
|
||||||
|
return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
// Transparency related helpers (optional)
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma comment(lib, "dwmapi") // Link with dwmapi.lib. MinGW will require linking with '-ldwmapi'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// [experimental]
|
||||||
|
// Borrowed from GLFW's function updateFramebufferTransparency() in src/win32_window.c
|
||||||
|
// (the Dwm* functions are Vista era functions but we are borrowing logic from GLFW)
|
||||||
|
void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd)
|
||||||
|
{
|
||||||
|
if (!_IsWindowsVistaOrGreater())
|
||||||
|
return;
|
||||||
|
|
||||||
|
BOOL composition;
|
||||||
|
if (FAILED(::DwmIsCompositionEnabled(&composition)) || !composition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BOOL opaque;
|
||||||
|
DWORD color;
|
||||||
|
if (_IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque))
|
||||||
|
{
|
||||||
|
HRGN region = ::CreateRectRgn(0, 0, -1, -1);
|
||||||
|
DWM_BLURBEHIND bb = {};
|
||||||
|
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
|
||||||
|
bb.hRgnBlur = region;
|
||||||
|
bb.fEnable = TRUE;
|
||||||
|
::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
|
||||||
|
::DeleteObject(region);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DWM_BLURBEHIND bb = {};
|
||||||
|
bb.dwFlags = DWM_BB_ENABLE;
|
||||||
|
::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
52
backends/imgui_impl_win32.h
Normal file
52
backends/imgui_impl_win32.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications)
|
||||||
|
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
|
||||||
|
|
||||||
|
// Win32 message handler your application need to call.
|
||||||
|
// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on <windows.h> from this helper.
|
||||||
|
// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it.
|
||||||
|
// - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DPI-related helpers (optional)
|
||||||
|
// - Use to enable DPI awareness without having to create an application manifest.
|
||||||
|
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
|
||||||
|
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
|
||||||
|
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
|
||||||
|
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness();
|
||||||
|
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd
|
||||||
|
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor
|
||||||
|
|
||||||
|
// Transparency related helpers (optional) [experimental]
|
||||||
|
// - Use to enable alpha compositing transparency with the desktop.
|
||||||
|
// - Use together with e.g. clearing your framebuffer with zero-alpha.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
Loading…
Reference in New Issue
Block a user