Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 91 additions & 4 deletions Kits/ATGTK/imgui/imgui_atg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,54 @@
#include <ShellScalingApi.h>
#pragma comment(lib, "shcore.lib") // GetDpiForMonitor
#endif
#include <algorithm>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>

namespace ImGuiAtg
{
//--------------------------------------------------------------------------------------
// Input helpers
//--------------------------------------------------------------------------------------


namespace
{
std::vector<std::string> g_navigationGroups;
std::string g_currentNavigationGroup;
std::string g_requestedNavigationGroup;
int g_navigationGroupDepth = 0;
bool g_navigationGroupInputActive = false;
}

void BeginNavigationGroup(const char* id)
{
IM_ASSERT(id != nullptr && id[0] != '\0');
IM_ASSERT(g_navigationGroupDepth == 0);
IM_ASSERT(std::find(g_navigationGroups.cbegin(), g_navigationGroups.cend(), id) == g_navigationGroups.cend());
g_navigationGroupDepth++;
g_navigationGroups.emplace_back(id);

if (g_requestedNavigationGroup == id)
{
ImGui::SetNavCursorVisible(true);
ImGui::SetKeyboardFocusHere();
g_currentNavigationGroup = id;
g_requestedNavigationGroup.clear();
}
}

void EndNavigationGroup()
{
IM_ASSERT(g_navigationGroupDepth > 0);
g_navigationGroupDepth--;
}

void HandleStandardInput()
{
IM_ASSERT(g_navigationGroupDepth == 0);

if ((!ImGui::GetIO().WantTextInput && ImGui::IsKeyPressed(ImGuiKey_F2, false)) ||
(ImGui::IsKeyDown(ImGuiKey_GamepadL1) &&
ImGui::IsKeyDown(ImGuiKey_GamepadR1) &&
Expand All @@ -43,6 +79,57 @@ namespace ImGuiAtg
{
PostQuitMessage(0);
}

const bool groupNavigationChord = ImGui::IsKeyDown(ImGuiKey_GamepadL1)
&& ImGui::IsKeyDown(ImGuiKey_GamepadR1);
const bool previousGroupPressed = groupNavigationChord
&& ImGui::IsKeyPressed(ImGuiKey_GamepadDpadUp, false);
const bool nextGroupPressed = groupNavigationChord
&& ImGui::IsKeyPressed(ImGuiKey_GamepadDpadDown, false);
const bool groupNavigationInputDown = groupNavigationChord
&& (ImGui::IsKeyDown(ImGuiKey_GamepadDpadUp)
|| ImGui::IsKeyDown(ImGuiKey_GamepadDpadDown));

if (!g_requestedNavigationGroup.empty()
&& std::find(
g_navigationGroups.cbegin(),
g_navigationGroups.cend(),
g_requestedNavigationGroup) == g_navigationGroups.cend())
{
g_requestedNavigationGroup.clear();
}

if (!g_navigationGroups.empty() && (previousGroupPressed || nextGroupPressed))
{
auto currentGroup = std::find(
g_navigationGroups.cbegin(),
g_navigationGroups.cend(),
g_currentNavigationGroup);

size_t requestedGroupIndex = 0;
if (currentGroup == g_navigationGroups.cend())
{
requestedGroupIndex = previousGroupPressed ? g_navigationGroups.size() - 1 : 0;
}
else
{
const size_t currentGroupIndex = static_cast<size_t>(currentGroup - g_navigationGroups.cbegin());
requestedGroupIndex = previousGroupPressed
? (currentGroupIndex + g_navigationGroups.size() - 1) % g_navigationGroups.size()
: (currentGroupIndex + 1) % g_navigationGroups.size();
}

g_requestedNavigationGroup = g_navigationGroups[requestedGroupIndex];
g_navigationGroupInputActive = true;
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
}
else if (g_navigationGroupInputActive && !groupNavigationInputDown)
{
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
g_navigationGroupInputActive = false;
}

g_navigationGroups.clear();
}

//--------------------------------------------------------------------------------------
Expand Down Expand Up @@ -324,11 +411,11 @@ namespace ImGuiAtg
// with FooterItem() before calling EndFooter().
}

bool FooterItem(const char* text)
bool FooterItem(const char* text, bool clickable)
{
ControllerText(text);
ImGui::SameLine(0, Scaled(80.0f));
if (ImGui::IsItemHovered())
ImGui::SameLine(0, Scaled(40.0f));
if (clickable && ImGui::IsItemHovered())
{
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
Expand Down
13 changes: 12 additions & 1 deletion Kits/ATGTK/imgui/imgui_atg.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,25 @@ namespace ImGuiAtg
void EndFooter();

// FooterItem() renders one footer hint between BeginFooter() and EndFooter().
bool FooterItem(const char* text);
bool FooterItem(const char* text, bool clickable = true);

// Handles standard sample input -- the same shortcuts that DrawFooter() advertises:
//
// Alt+F4 / LB+RB+View+Menu -- exit the app (Alt+F4 is handled by Windows)
// F2 / LB+RB+Y -- toggle light/dark theme
void HandleStandardInput();

// Ordered gamepad navigation groups. LB+RB+D-pad up/down moves between the groups
// submitted during Draw(), in submission order. When a group is selected, its first
// navigable item receives focus.
//
// Group IDs must be unique within a frame. Groups cannot be nested, and every
// BeginNavigationGroup() must have a matching EndNavigationGroup(). Conditional groups
// participate only on frames where they are submitted. The standard frame loop must call
// HandleStandardInput() once after ImGui::NewFrame() and before submitting groups.
void BeginNavigationGroup(const char* id);
void EndNavigationGroup();

// Splitter -- splits the current region into two resizable panels.
//
// Usage (horizontal -- top/bottom):
Expand Down
126 changes: 102 additions & 24 deletions Kits/OpenSource/imgui/backends/imgui_impl_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
#include <dwmapi.h>
#include <wrl.h>
using Microsoft::WRL::ComPtr;
#include <mutex>
#include <vector>

#include <GameInput.h>
#if GAMEINPUT_API_VERSION == 1
Expand All @@ -124,21 +126,25 @@ using namespace GameInput::v3;

struct ImGui_ImplWin32_Data
{
HWND hWnd;
HWND MouseHwnd;
int MouseTrackedArea; // 0: not tracked, 1: client area, 2: non-client area
int MouseButtonsDown;
INT64 Time;
INT64 TicksPerSecond;
ImGuiMouseCursor LastMouseCursor;
UINT32 KeyboardCodePage;
HWND hWnd = nullptr;
HWND MouseHwnd = nullptr;
int MouseTrackedArea = 0; // 0: not tracked, 1: client area, 2: non-client area
int MouseButtonsDown = 0;
INT64 Time = 0;
INT64 TicksPerSecond = 0;
ImGuiMouseCursor LastMouseCursor = ImGuiMouseCursor_Arrow;
UINT32 KeyboardCodePage = 0;

#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
bool HasGamepad;
ComPtr<IGameInput> GameInput;
bool HasGamepad = false;
GameInputCallbackToken DeviceCallbackToken = 0;

ComPtr<IGameInput> GameInput{};
std::vector<ComPtr<IGameInputDevice>> Gamepads{};
std::mutex GamepadsMutex{};
#endif

ImGui_ImplWin32_Data() { memset((void*)this, 0, sizeof(*this)); }
ImGui_ImplWin32_Data() = default;
};

// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
Expand Down Expand Up @@ -169,6 +175,39 @@ static void ImGui_ImplWin32_UpdateKeyboardCodePage(ImGuiIO& io)
#endif
}

static void CALLBACK ImGui_ImplWin32_DeviceCallback(
_In_ GameInputCallbackToken,
_In_ void* context,
_In_ IGameInputDevice* device,
_In_ uint64_t,
_In_ GameInputDeviceStatus current,
_In_ GameInputDeviceStatus previous) noexcept
{
auto* bd = static_cast<ImGui_ImplWin32_Data*>(context);

const bool wasConnected = (previous & GameInputDeviceConnected) != 0;
const bool isConnected = (current & GameInputDeviceConnected) != 0;

std::lock_guard<std::mutex> lock(bd->GamepadsMutex);

// newly connected device, add to our list
if (isConnected && !wasConnected)
{
bd->Gamepads.emplace_back(device);
}

// newly disconnected device, remove from our list
else if (wasConnected && !isConnected)
{
bd->Gamepads.erase(
std::remove_if(bd->Gamepads.begin(), bd->Gamepads.end(), [device](ComPtr<IGameInputDevice> const& entry)
{
return entry.Get() == device;
}), bd->Gamepads.end()
);
}
}

static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
{
ImGuiIO& io = ImGui::GetIO();
Expand All @@ -181,30 +220,51 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter))
return false;

// Setup backend capabilities flags
ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)();
bd->hWnd = (HWND)hwnd;
bd->TicksPerSecond = perf_frequency;
bd->Time = perf_counter;
bd->LastMouseCursor = ImGuiMouseCursor_COUNT;

// Setup GameInput
#ifndef IMGUI_IMPL_GDK_DISABLE_GAMEPAD
HRESULT hr = GameInputCreate(&bd->GameInput);
if (FAILED(hr))
{
IM_DELETE(bd);
return false;
}

hr = bd->GameInput->RegisterDeviceCallback(
nullptr,
GameInputKindGamepad,
GameInputDeviceConnected,
GameInputBlockingEnumeration,
bd,
ImGui_ImplWin32_DeviceCallback,
&bd->DeviceCallbackToken);

if (FAILED(hr))
{
IM_DELETE(bd);
return false;
}

#endif // IMGUI_IMPL_GDK_DISABLE_GAMEPAD

// Setup backend capabilities flags
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(io);

// Set platform dependent data in viewport
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd;
IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch

// Setup GameInput
#ifndef IMGUI_IMPL_GDK_DISABLE_GAMEPAD
if(GameInputCreate(&bd->GameInput) != S_OK)
return false;
#endif // IMGUI_IMPL_GDK_DISABLE_GAMEPAD

return true;
}

Expand All @@ -226,6 +286,18 @@ void ImGui_ImplWin32_Shutdown()
ImGuiIO& io = ImGui::GetIO();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();

#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
if (bd->GameInput != nullptr && bd->DeviceCallbackToken != 0)
{
#if defined(GAMEINPUT_API_VERSION) && (GAMEINPUT_API_VERSION >= 1)
bd->GameInput->UnregisterCallback(bd->DeviceCallbackToken);
#else
bd->GameInput->UnregisterCallback(bd->DeviceCallbackToken, UINT64_MAX);
#endif
bd->DeviceCallbackToken = 0;
}
#endif

io.BackendPlatformName = nullptr;
io.BackendPlatformUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);
Expand Down Expand Up @@ -344,8 +416,14 @@ static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io)
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);

if(bd->GameInput == nullptr)
std::lock_guard<std::mutex> lock(bd->GamepadsMutex);

if(bd->GameInput == nullptr || bd->Gamepads.empty() || bd->Gamepads.back() == nullptr)
{
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
bd->HasGamepad = false;
return;
}

#if GAMEINPUT_API_VERSION == 1
ComPtr<GameInput::v1::IGameInputReading> reading;
Expand All @@ -357,7 +435,7 @@ static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io)
ComPtr<IGameInputReading> reading;
#endif

HRESULT hr = bd->GameInput->GetCurrentReading(GameInputKindGamepad, nullptr, &reading);
HRESULT hr = bd->GameInput->GetCurrentReading(GameInputKindGamepad, bd->Gamepads.back().Get(), &reading);
bd->HasGamepad = hr == S_OK;

io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
Expand Down
Binary file added Samples/Graphics/FramePacing/Assets/LOGO_ATG.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Samples/Graphics/FramePacing/Assets/LargeLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Samples/Graphics/FramePacing/Assets/Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Samples/Graphics/FramePacing/Assets/SmallLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Samples/Graphics/FramePacing/Assets/StoreLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading