ftpiiu_plugin/source/3ds/imgui_ctru.cpp

249 lines
6.8 KiB
C++

// ftpd is a server implementation based on the following:
// - RFC 959 (https://tools.ietf.org/html/rfc959)
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
//
// The MIT License (MIT)
//
// Copyright (C) 2023 Michael Theall
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef CLASSIC
#include "imgui_ctru.h"
#include "imgui.h"
#include "../imgui/imgui_internal.h"
#include "fs.h"
#include "platform.h"
#include <chrono>
#include <cstring>
#include <functional>
#include <string>
#include <tuple>
using namespace std::chrono_literals;
#undef keysDown
#undef keysUp
namespace
{
/// \brief Clipboard
std::string s_clipboard;
/// \brief Get clipboard text callback
/// \param userData_ User data
char const *getClipboardText (void *const userData_)
{
(void)userData_;
return s_clipboard.c_str ();
}
/// \brief Set clipboard text callback
/// \param userData_ User data
/// \param text_ Clipboard text
void setClipboardText (void *const userData_, char const *const text_)
{
(void)userData_;
s_clipboard = text_;
}
/// \brief Update touch position
/// \param io_ ImGui IO
void updateTouch (ImGuiIO &io_)
{
// check if touchpad was released
if (hidKeysUp () & KEY_TOUCH)
{
// keep mouse position for one frame for release event
io_.AddMouseButtonEvent (0, false);
return;
}
// check if touchpad is touched
if (!(hidKeysHeld () & KEY_TOUCH))
{
// set mouse cursor off-screen
io_.AddMousePosEvent (-10.0f, -10.0f);
io_.AddMouseButtonEvent (0, false);
return;
}
// read touch position
touchPosition pos;
hidTouchRead (&pos);
// transform to bottom-screen space
io_.AddMousePosEvent (pos.px + 40.0f, pos.py + 240.0f);
io_.AddMouseButtonEvent (0, true);
}
/// \brief Update gamepad inputs
/// \param io_ ImGui IO
void updateGamepads (ImGuiIO &io_)
{
auto const buttonMapping = {
// clang-format off
std::make_pair (KEY_A, ImGuiKey_GamepadFaceDown), // A and B are swapped
std::make_pair (KEY_B, ImGuiKey_GamepadFaceRight), // this is more intuitive
std::make_pair (KEY_X, ImGuiKey_GamepadFaceUp),
std::make_pair (KEY_Y, ImGuiKey_GamepadFaceLeft),
std::make_pair (KEY_L, ImGuiKey_GamepadL1),
std::make_pair (KEY_ZL, ImGuiKey_GamepadL1),
std::make_pair (KEY_R, ImGuiKey_GamepadR1),
std::make_pair (KEY_ZR, ImGuiKey_GamepadR1),
std::make_pair (KEY_DUP, ImGuiKey_GamepadDpadUp),
std::make_pair (KEY_DRIGHT, ImGuiKey_GamepadDpadRight),
std::make_pair (KEY_DDOWN, ImGuiKey_GamepadDpadDown),
std::make_pair (KEY_DLEFT, ImGuiKey_GamepadDpadLeft),
// clang-format on
};
// read buttons from 3DS
auto const keysDown = hidKeysDown ();
auto const keysUp = hidKeysUp ();
for (auto const &[in, out] : buttonMapping)
{
if (keysUp & in)
io_.AddKeyEvent (out, false);
else if (keysDown & in)
io_.AddKeyEvent (out, true);
}
// update joystick
circlePosition cpad;
auto const analogMapping = {
// clang-format off
std::make_tuple (std::ref (cpad.dx), ImGuiKey_GamepadLStickLeft, -0.3f, -0.9f),
std::make_tuple (std::ref (cpad.dx), ImGuiKey_GamepadLStickRight, +0.3f, +0.9f),
std::make_tuple (std::ref (cpad.dy), ImGuiKey_GamepadLStickUp, +0.3f, +0.9f),
std::make_tuple (std::ref (cpad.dy), ImGuiKey_GamepadLStickDown, -0.3f, -0.9f),
// clang-format on
};
// read left joystick from circle pad
hidCircleRead (&cpad);
for (auto const &[in, out, min, max] : analogMapping)
{
auto const value = std::clamp ((in / 156.0f - min) / (max - min), 0.0f, 1.0f);
io_.AddKeyAnalogEvent (out, value > 0.1f, value);
}
}
/// \brief Update keyboard inputs
/// \param io_ ImGui IO
void updateKeyboard (ImGuiIO &io_)
{
static enum {
INACTIVE,
KEYBOARD,
CLEARED,
} state = INACTIVE;
switch (state)
{
case INACTIVE:
{
if (!io_.WantTextInput)
return;
auto &textState = ImGui::GetCurrentContext ()->InputTextState;
SwkbdState kbd;
swkbdInit (&kbd, SWKBD_TYPE_NORMAL, 2, -1);
swkbdSetButton (&kbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton (&kbd, SWKBD_BUTTON_RIGHT, "OK", true);
swkbdSetInitialText (
&kbd, std::string (textState.InitialTextA.Data, textState.InitialTextA.Size).c_str ());
if (textState.Flags & ImGuiInputTextFlags_Password)
swkbdSetPasswordMode (&kbd, SWKBD_PASSWORD_HIDE_DELAY);
char buffer[32] = {0};
auto const button = swkbdInputText (&kbd, buffer, sizeof (buffer));
if (button == SWKBD_BUTTON_RIGHT)
io_.AddInputCharactersUTF8 (buffer);
state = KEYBOARD;
break;
}
case KEYBOARD:
// need to skip a frame for active id to really be cleared
ImGui::ClearActiveID ();
state = CLEARED;
break;
case CLEARED:
state = INACTIVE;
break;
}
}
}
bool imgui::ctru::init ()
{
auto &io = ImGui::GetIO ();
// setup config flags
io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
// setup platform backend
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
io.BackendPlatformName = "3DS";
// disable mouse cursor
io.MouseDrawCursor = false;
// clipboard callbacks
io.SetClipboardTextFn = setClipboardText;
io.GetClipboardTextFn = getClipboardText;
io.ClipboardUserData = nullptr;
return true;
}
void imgui::ctru::newFrame ()
{
auto &io = ImGui::GetIO ();
// check that font was built
IM_ASSERT (io.Fonts->IsBuilt () &&
"Font atlas not built! It is generally built by the renderer back-end. Missing call "
"to renderer _NewFrame() function?");
// time step
static auto const start = platform::steady_clock::now ();
static auto prev = start;
auto const now = platform::steady_clock::now ();
io.DeltaTime = std::chrono::duration<float> (now - prev).count ();
prev = now;
updateTouch (io);
updateGamepads (io);
updateKeyboard (io);
}
#endif