mirror of
https://github.com/InstanceBrick/nightcap.git
synced 2026-10-23 14:01:15 +02:00
190 lines
6.5 KiB
C++
190 lines
6.5 KiB
C++
#include "runtime/roblox_window_input_runtime.h"
|
|
|
|
#include <SDL3/SDL_video.h>
|
|
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
#include "window/window.h"
|
|
|
|
namespace mocktail {
|
|
namespace runtime {
|
|
|
|
RobloxWindowInputRuntime::RobloxWindowInputRuntime(
|
|
JniEnvironmentProvider environment, RobloxInputSymbols symbols)
|
|
: text_clipboard_(platform::CreateSdlTextClipboard()),
|
|
runtime_(environment, symbols, text_surface_overlay_.sink(),
|
|
text_clipboard_.get()) {}
|
|
|
|
RobloxWindowInputRuntime::~RobloxWindowInputRuntime() { Shutdown(); }
|
|
|
|
Status RobloxWindowInputRuntime::Initialize() {
|
|
if (observer_registered_) {
|
|
return Status::Error(StatusCode::kFailedPrecondition,
|
|
"window input runtime is already initialized");
|
|
}
|
|
const window::WindowViewportSnapshot window_viewport =
|
|
window::GetWindowViewportSnapshot();
|
|
if (!window_viewport.valid()) {
|
|
return Status::Error(StatusCode::kUnavailable,
|
|
"SDL input viewport is unavailable");
|
|
}
|
|
auto* sdl_window = static_cast<SDL_Window*>(window::GetBackendWindow());
|
|
if (sdl_window == nullptr) {
|
|
return Status::Error(StatusCode::kUnavailable,
|
|
"SDL input window is unavailable");
|
|
}
|
|
Status status = text_surface_overlay_.Initialize(
|
|
{window_viewport.pixel_width, window_viewport.pixel_height,
|
|
window_viewport.dpi_scale});
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
const bool initially_focused =
|
|
(SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_INPUT_FOCUS) != 0;
|
|
status = runtime_.Initialize(
|
|
{window_viewport.logical_width, window_viewport.logical_height,
|
|
window_viewport.pixel_width, window_viewport.pixel_height,
|
|
window_viewport.dpi_scale},
|
|
initially_focused);
|
|
if (!status.ok()) {
|
|
(void)text_surface_overlay_.Shutdown();
|
|
return status;
|
|
}
|
|
if (!window::SetPlatformEventObserver(
|
|
&RobloxWindowInputRuntime::PlatformEventCallback, this)) {
|
|
runtime_.Deactivate();
|
|
(void)text_surface_overlay_.Shutdown();
|
|
return Status::Error(StatusCode::kFailedPrecondition,
|
|
"SDL platform event observer is unavailable");
|
|
}
|
|
observer_registered_ = true;
|
|
if (!window::SetMouseLockQueryCallback(
|
|
&RobloxWindowInputRuntime::MouseLockQueryCallback, this)) {
|
|
window::ClearPlatformEventObserver();
|
|
observer_registered_ = false;
|
|
runtime_.Deactivate();
|
|
(void)text_surface_overlay_.Shutdown();
|
|
return Status::Error(StatusCode::kFailedPrecondition,
|
|
"SDL mouse lock query owner is unavailable");
|
|
}
|
|
mouse_lock_query_registered_ = true;
|
|
initialized_ = true;
|
|
return Status::Ok();
|
|
}
|
|
|
|
void RobloxWindowInputRuntime::PlatformEventCallback(
|
|
void* context, const platform::PlatformEvent& event) {
|
|
if (context == nullptr) {
|
|
return;
|
|
}
|
|
auto* self = static_cast<RobloxWindowInputRuntime*>(context);
|
|
if (const auto* resized =
|
|
std::get_if<platform::WindowResizedEvent>(&event.payload)) {
|
|
(void)self->text_surface_overlay_.UpdateViewport(
|
|
{resized->pixel_width, resized->pixel_height, resized->density_scale});
|
|
}
|
|
(void)self->runtime_.HandleEvent(event);
|
|
}
|
|
|
|
Status RobloxWindowInputRuntime::Shutdown() {
|
|
if (!initialized_) {
|
|
return Status::Ok();
|
|
}
|
|
if (observer_registered_) {
|
|
window::ClearPlatformEventObserver();
|
|
observer_registered_ = false;
|
|
}
|
|
if (mouse_lock_query_registered_) {
|
|
window::ClearMouseLockQueryCallback();
|
|
mouse_lock_query_registered_ = false;
|
|
}
|
|
const RobloxInputSnapshot snapshot = runtime_.Snapshot();
|
|
Status status = runtime_.Deactivate();
|
|
Status overlay_status = text_surface_overlay_.Shutdown();
|
|
initialized_ = false;
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
if (!overlay_status.ok()) {
|
|
return overlay_status;
|
|
}
|
|
if (snapshot.native_errors != 0) {
|
|
return Status::Error(StatusCode::kPlatformError,
|
|
"Roblox native input reported " +
|
|
std::to_string(snapshot.native_errors) +
|
|
" dispatch error(s)");
|
|
}
|
|
std::fprintf(stderr,
|
|
" [input] typed production input stopped after observer "
|
|
"clear\n");
|
|
return Status::Ok();
|
|
}
|
|
|
|
Status RobloxWindowInputRuntime::BeginTextFocusSession(
|
|
RobloxTextFocusSession session) {
|
|
if (!initialized_) {
|
|
return Status::Error(StatusCode::kFailedPrecondition,
|
|
"window input runtime is not initialized");
|
|
}
|
|
return runtime_.BeginTextFocusSession(std::move(session));
|
|
}
|
|
|
|
Status RobloxWindowInputRuntime::EndTextFocusSession(int64_t textbox_handle,
|
|
uint64_t generation,
|
|
bool notify_native) {
|
|
if (!initialized_) {
|
|
return Status::Error(StatusCode::kFailedPrecondition,
|
|
"window input runtime is not initialized");
|
|
}
|
|
return runtime_.EndTextFocusSession(textbox_handle, generation,
|
|
notify_native);
|
|
}
|
|
|
|
Status RobloxWindowInputRuntime::ReplaceFocusedTextFromEngine(
|
|
uint64_t generation, std::string authoritative_utf8) {
|
|
if (!initialized_) {
|
|
return Status::Error(StatusCode::kFailedPrecondition,
|
|
"window input runtime is not initialized");
|
|
}
|
|
return runtime_.ReplaceFocusedTextFromEngine(generation,
|
|
std::move(authoritative_utf8));
|
|
}
|
|
|
|
Status RobloxWindowInputRuntime::QueryCurrentTextBoxInfo(
|
|
RobloxNativeTextBoxInfoQueryResult* result) {
|
|
if (!initialized_) {
|
|
return Status::Error(StatusCode::kFailedPrecondition,
|
|
"window input runtime is not initialized");
|
|
}
|
|
return runtime_.QueryCurrentTextBoxInfo(result);
|
|
}
|
|
|
|
Status RobloxWindowInputRuntime::UpdateTextFocusProperties(
|
|
uint64_t generation, const RobloxTextFocusProperties& properties) {
|
|
if (!initialized_) {
|
|
return Status::Error(StatusCode::kFailedPrecondition,
|
|
"window input runtime is not initialized");
|
|
}
|
|
return runtime_.UpdateTextFocusProperties(generation, properties);
|
|
}
|
|
|
|
RobloxInputSnapshot RobloxWindowInputRuntime::Snapshot() const {
|
|
return runtime_.Snapshot();
|
|
}
|
|
|
|
bool RobloxWindowInputRuntime::MouseLockQueryCallback(void* context,
|
|
bool* locked_center) {
|
|
if (context == nullptr || locked_center == nullptr) {
|
|
return false;
|
|
}
|
|
return static_cast<RobloxWindowInputRuntime*>(context)
|
|
->runtime_.GetMainWindowIsMouseLockedCenter(locked_center)
|
|
.ok();
|
|
}
|
|
|
|
} // namespace runtime
|
|
} // namespace mocktail
|