305 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C++
		
	
	
	
| // Copyright (C) 2019 Nomango
 | |
| 
 | |
| #include "../kiwano-imgui.h"
 | |
| #include "imgui_impl.h"
 | |
| 
 | |
| #include <XInput.h>
 | |
| #pragma comment(lib, "xinput")
 | |
| 
 | |
| // 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
 | |
| 
 | |
| namespace kiwano
 | |
| {
 | |
| 	namespace imgui
 | |
| 	{
 | |
| 		ImGuiModule::ImGuiModule()
 | |
| 			: has_gamepad_(false)
 | |
| 			, want_update_has_gamepad_(false)
 | |
| 			, target_window_(nullptr)
 | |
| 		{
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::SetupComponent(Application* app)
 | |
| 		{
 | |
| 			// Setup Dear ImGui context
 | |
| 			IMGUI_CHECKVERSION();
 | |
| 			ImGui::CreateContext();
 | |
| 			ImGuiIO& io = ImGui::GetIO(); (void)io;
 | |
| 			//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;  // Enable Keyboard Controls
 | |
| 
 | |
| 			// Setup Dear ImGui style
 | |
| 			ImGui::StyleColorsDark();
 | |
| 			//ImGui::StyleColorsClassic();
 | |
| 
 | |
| 			// Setup Platform/Renderer bindings
 | |
| 			Init(Window::Instance()->GetHandle());
 | |
| 
 | |
| 			target_window_ = Renderer::Instance()->GetTargetWindow();
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::DestroyComponent()
 | |
| 		{
 | |
| 			ImGui_Impl_Shutdown();
 | |
| 			ImGui::DestroyContext();
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::BeforeUpdate(float dt)
 | |
| 		{
 | |
| 			ImGuiIO& io = ImGui::GetIO();
 | |
| 
 | |
| 			// Setup time step
 | |
| 			io.DeltaTime = dt;
 | |
| 
 | |
| 			// Read keyboard modifiers inputs
 | |
| 			io.KeyCtrl = Input::Instance()->IsDown(KeyCode::Ctrl);
 | |
| 			io.KeyShift = Input::Instance()->IsDown(KeyCode::Shift);
 | |
| 			io.KeyAlt = Input::Instance()->IsDown(KeyCode::Alt);
 | |
| 			io.KeySuper = false;
 | |
| 			// io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
 | |
| 
 | |
| 			// Update OS mouse position
 | |
| 			UpdateMousePos();
 | |
| 
 | |
| 			// Update OS mouse cursor with the cursor requested by imgui
 | |
| 			UpdateMouseCursor();
 | |
| 
 | |
| 			// Update game controllers (if enabled and available)
 | |
| 			UpdateGamepads();
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::Init(HWND hwnd)
 | |
| 		{
 | |
| 			ImGuiIO& io = ImGui::GetIO();
 | |
| 			io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;         // We can honor GetMouseCursor() values (optional)
 | |
| 			io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;          // We can honor io.WantSetMousePos requests (optional, rarely used)
 | |
| 			io.BackendPlatformName = "imgui_impl_win32";
 | |
| 			io.ImeWindowHandle = hwnd;
 | |
| 
 | |
| 			// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
 | |
| 			io.KeyMap[ImGuiKey_Tab] = KeyCode::Tab;
 | |
| 			io.KeyMap[ImGuiKey_LeftArrow] = KeyCode::Left;
 | |
| 			io.KeyMap[ImGuiKey_RightArrow] = KeyCode::Right;
 | |
| 			io.KeyMap[ImGuiKey_UpArrow] = KeyCode::Up;
 | |
| 			io.KeyMap[ImGuiKey_DownArrow] = KeyCode::Down;
 | |
| 			io.KeyMap[ImGuiKey_Delete] = KeyCode::Delete;
 | |
| 			io.KeyMap[ImGuiKey_Backspace] = KeyCode::Back;
 | |
| 			io.KeyMap[ImGuiKey_Space] = KeyCode::Space;
 | |
| 			io.KeyMap[ImGuiKey_Enter] = KeyCode::Enter;
 | |
| 			io.KeyMap[ImGuiKey_Escape] = KeyCode::Esc;
 | |
| 			io.KeyMap[ImGuiKey_A] = KeyCode::A;
 | |
| 			io.KeyMap[ImGuiKey_C] = KeyCode::C;
 | |
| 			io.KeyMap[ImGuiKey_V] = KeyCode::V;
 | |
| 			io.KeyMap[ImGuiKey_X] = KeyCode::X;
 | |
| 			io.KeyMap[ImGuiKey_Y] = KeyCode::Y;
 | |
| 			io.KeyMap[ImGuiKey_Z] = KeyCode::Z;
 | |
| 
 | |
| 			ImGui_Impl_Init(Renderer::Instance());
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::BeforeRender()
 | |
| 		{
 | |
| 			NewFrame();
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::AfterRender()
 | |
| 		{
 | |
| 			Render();
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::HandleMessage(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
 | |
| 		{
 | |
| 			if (ImGui::GetCurrentContext() == NULL)
 | |
| 				return;
 | |
| 
 | |
| 			ImGuiIO& io = ImGui::GetIO();
 | |
| 			switch (msg)
 | |
| 			{
 | |
| 			case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
 | |
| 			case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
 | |
| 			case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
 | |
| 			case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
 | |
| 			{
 | |
| 				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 (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL)
 | |
| 					::SetCapture(hwnd);
 | |
| 
 | |
| 				io.MouseDown[button] = true;
 | |
| 				break;
 | |
| 			}
 | |
| 			case WM_LBUTTONUP:
 | |
| 			case WM_RBUTTONUP:
 | |
| 			case WM_MBUTTONUP:
 | |
| 			case WM_XBUTTONUP:
 | |
| 			{
 | |
| 				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; }
 | |
| 				io.MouseDown[button] = false;
 | |
| 				if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd)
 | |
| 					::ReleaseCapture();
 | |
| 				break;
 | |
| 			}
 | |
| 			case WM_MOUSEWHEEL:
 | |
| 			{
 | |
| 				io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wparam) / (float)WHEEL_DELTA;
 | |
| 				break;
 | |
| 			}
 | |
| 			case WM_MOUSEHWHEEL:
 | |
| 			{
 | |
| 				io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wparam) / (float)WHEEL_DELTA;
 | |
| 				break;
 | |
| 			}
 | |
| 			case WM_KEYDOWN:
 | |
| 			case WM_SYSKEYDOWN:
 | |
| 			{
 | |
| 				if (wparam < 256)
 | |
| 					io.KeysDown[wparam] = 1;
 | |
| 				break;
 | |
| 			}
 | |
| 			case WM_KEYUP:
 | |
| 			case WM_SYSKEYUP:
 | |
| 			{
 | |
| 				if (wparam < 256)
 | |
| 					io.KeysDown[wparam] = 0;
 | |
| 				break;
 | |
| 			}
 | |
| 			case WM_CHAR:
 | |
| 			{
 | |
| 				// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
 | |
| 				io.AddInputCharacter((unsigned int)wparam);
 | |
| 				break;
 | |
| 			}
 | |
| 			case WM_SETCURSOR:
 | |
| 			{
 | |
| 				if (LOWORD(lparam) == HTCLIENT)
 | |
| 				{
 | |
| 					UpdateMouseCursor();
 | |
| 				}
 | |
| 				break;
 | |
| 			}
 | |
| 			case WM_DEVICECHANGE:
 | |
| 			{
 | |
| 				if ((UINT)wparam == DBT_DEVNODES_CHANGED)
 | |
| 					want_update_has_gamepad_ = true;
 | |
| 				break;
 | |
| 			}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::NewFrame()
 | |
| 		{
 | |
| 			ImGui_Impl_NewFrame();
 | |
| 
 | |
| 			ImGuiIO& io = ImGui::GetIO();
 | |
| 			KGE_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built!");
 | |
| 
 | |
| 			// Setup display size (every frame to accommodate for window resizing)
 | |
| 			Size display_size = Renderer::Instance()->GetOutputSize();
 | |
| 			io.DisplaySize = ImVec2(display_size.x, display_size.y);
 | |
| 
 | |
| 			ImGui::NewFrame();
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::Render()
 | |
| 		{
 | |
| 			ImGui::Render();
 | |
| 
 | |
| 			ImGui_Impl_RenderDrawData(ImGui::GetDrawData());
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::UpdateMousePos()
 | |
| 		{
 | |
| 			ImGuiIO& io = ImGui::GetIO();
 | |
| 
 | |
| 			// Set OS mouse position 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 };
 | |
| 				::ClientToScreen(target_window_, &pos);
 | |
| 				::SetCursorPos(pos.x, pos.y);
 | |
| 			}
 | |
| 
 | |
| 			Point pos = Input::Instance()->GetMousePos();
 | |
| 			io.MousePos = ImVec2(pos.x, pos.y);
 | |
| 		}
 | |
| 
 | |
| 		void ImGuiModule::UpdateMouseCursor()
 | |
| 		{
 | |
| 			if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
 | |
| 				return;
 | |
| 
 | |
| 			MouseCursor cursor = MouseCursor::Arrow;
 | |
| 			switch (ImGui::GetMouseCursor())
 | |
| 			{
 | |
| 			case ImGuiMouseCursor_Arrow:        cursor = MouseCursor::Arrow; break;
 | |
| 			case ImGuiMouseCursor_TextInput:    cursor = MouseCursor::TextInput; break;
 | |
| 			case ImGuiMouseCursor_ResizeAll:    cursor = MouseCursor::SizeAll; break;
 | |
| 			case ImGuiMouseCursor_ResizeEW:     cursor = MouseCursor::SizeWE; break;
 | |
| 			case ImGuiMouseCursor_ResizeNS:     cursor = MouseCursor::SizeNS; break;
 | |
| 			case ImGuiMouseCursor_ResizeNESW:   cursor = MouseCursor::SizeNESW; break;
 | |
| 			case ImGuiMouseCursor_ResizeNWSE:   cursor = MouseCursor::SizeNWSE; break;
 | |
| 			case ImGuiMouseCursor_Hand:         cursor = MouseCursor::Hand; break;
 | |
| 			}
 | |
| 
 | |
| 			Window::Instance()->SetMouseCursor(cursor);
 | |
| 		}
 | |
| 		void ImGuiModule::UpdateGamepads()
 | |
| 		{
 | |
| 			ImGuiIO& io = ImGui::GetIO();
 | |
| 			memset(io.NavInputs, 0, sizeof(io.NavInputs));
 | |
| 			if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
 | |
| 				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 (want_update_has_gamepad_)
 | |
| 			{
 | |
| 				XINPUT_CAPABILITIES caps;
 | |
| 				has_gamepad_ = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS);
 | |
| 				want_update_has_gamepad_ = false;
 | |
| 			}
 | |
| 
 | |
| 			XINPUT_STATE xinput_state;
 | |
| 			io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
 | |
| 			if (has_gamepad_ && XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
 | |
| 			{
 | |
| 				const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
 | |
| 				io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
 | |
| 
 | |
| #define MAP_BUTTON(NAV_NO, BUTTON_ENUM)     { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
 | |
| #define MAP_ANALOG(NAV_NO, VALUE, V0, V1)   { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
 | |
| 				MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A);               // Cross / A
 | |
| 				MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B);                 // Circle / B
 | |
| 				MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X);                   // Square / X
 | |
| 				MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y);                  // Triangle / Y
 | |
| 				MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT);       // D-Pad Left
 | |
| 				MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT);     // D-Pad Right
 | |
| 				MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP);           // D-Pad Up
 | |
| 				MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN);       // D-Pad Down
 | |
| 				MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER);  // L1 / LB
 | |
| 				MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
 | |
| 				MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER);  // L1 / LB
 | |
| 				MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
 | |
| 				MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
 | |
| 				MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
 | |
| 				MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
 | |
| 				MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
 | |
| #undef MAP_BUTTON
 | |
| #undef MAP_ANALOG
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| } |