code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/vi/application_display_service.h"
#include "core/hle/service/vi/application_root_service.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/service_creator.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_types.h"

namespace Service::VI {

IApplicationRootService::IApplicationRootService(Core::System& system_,
                                                 std::shared_ptr<Container> container)
    : ServiceFramework{system_, "vi:u"}, m_container{std::move(container)} {
    static const FunctionInfo functions[] = {
        {0, C<&IApplicationRootService::GetDisplayService>, "GetDisplayService"},
        {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
    };
    RegisterHandlers(functions);
}

IApplicationRootService::~IApplicationRootService() = default;

Result IApplicationRootService::GetDisplayService(
    Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
    LOG_DEBUG(Service_VI, "called");
    R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
                                          Permission::User, policy));
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::VI {

class Container;
class IApplicationDisplayService;
enum class Policy : u32;

class IApplicationRootService final : public ServiceFramework<IApplicationRootService> {
public:
    explicit IApplicationRootService(Core::System& system_, std::shared_ptr<Container> container);
    ~IApplicationRootService() override;

private:
    Result GetDisplayService(
        Out<SharedPointer<IApplicationDisplayService>> out_application_display_service,
        Policy policy);

private:
    const std::shared_ptr<Container> m_container;
};

} // namespace Service::VI

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/vi/conductor.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/display_list.h"
#include "core/hle/service/vi/vsync_manager.h"

constexpr auto FrameNs = std::chrono::nanoseconds{1000000000 / 60};

namespace Service::VI {

Conductor::Conductor(Core::System& system, Container& container, DisplayList& displays)
    : m_system(system), m_container(container) {
    displays.ForEachDisplay([&](Display& display) {
        m_vsync_managers.insert({display.GetId(), VsyncManager{}});
    });

    if (system.IsMulticore()) {
        m_event = Core::Timing::CreateEvent(
            "ScreenComposition",
            [this](s64 time,
                   std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
                m_signal.Set();
                return std::chrono::nanoseconds(this->GetNextTicks());
            });

        system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
        m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
    } else {
        m_event = Core::Timing::CreateEvent(
            "ScreenComposition",
            [this](s64 time,
                   std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
                this->ProcessVsync();
                return std::chrono::nanoseconds(this->GetNextTicks());
            });

        system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
    }
}

Conductor::~Conductor() {
    m_system.CoreTiming().UnscheduleEvent(m_event);

    if (m_system.IsMulticore()) {
        m_thread.request_stop();
        m_signal.Set();
    }
}

void Conductor::LinkVsyncEvent(u64 display_id, Event* event) {
    if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
        it->second.LinkVsyncEvent(event);
    }
}

void Conductor::UnlinkVsyncEvent(u64 display_id, Event* event) {
    if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
        it->second.UnlinkVsyncEvent(event);
    }
}

void Conductor::ProcessVsync() {
    for (auto& [display_id, manager] : m_vsync_managers) {
        m_container.ComposeOnDisplay(&m_swap_interval, &m_compose_speed_scale, display_id);
        manager.SignalVsync();
    }
}

void Conductor::VsyncThread(std::stop_token token) {
    Common::SetCurrentThreadName("VSyncThread");

    while (!token.stop_requested()) {
        m_signal.Wait();

        if (m_system.IsShuttingDown()) {
            return;
        }

        this->ProcessVsync();
    }
}

s64 Conductor::GetNextTicks() const {
    const auto& settings = Settings::values;
    auto speed_scale = 1.f;
    if (settings.use_multi_core.GetValue()) {
        if (settings.use_speed_limit.GetValue()) {
            // Scales the speed based on speed_limit setting on MC. SC is handled by
            // SpeedLimiter::DoSpeedLimiting.
            speed_scale = 100.f / settings.speed_limit.GetValue();
        } else {
            // Run at unlocked framerate.
            speed_scale = 0.01f;
        }
    }

    // Adjust by speed limit determined during composition.
    speed_scale /= m_compose_speed_scale;

    if (m_system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
        // Run at intended presentation rate during video playback.
        speed_scale = 1.f;
    }

    const f32 effective_fps = 60.f / static_cast<f32>(m_swap_interval);
    return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <unordered_map>

#include "common/common_types.h"
#include "common/polyfill_thread.h"
#include "common/thread.h"

#include "core/hle/service/vi/vsync_manager.h"

namespace Core {
class System;
}

namespace Core::Timing {
struct EventType;
}

namespace Service {
class Event;
}

namespace Service::VI {

class Container;
class DisplayList;

class Conductor {
public:
    explicit Conductor(Core::System& system, Container& container, DisplayList& displays);
    ~Conductor();

    void LinkVsyncEvent(u64 display_id, Event* event);
    void UnlinkVsyncEvent(u64 display_id, Event* event);

private:
    void ProcessVsync();
    void VsyncThread(std::stop_token token);
    s64 GetNextTicks() const;

private:
    Core::System& m_system;
    Container& m_container;
    std::unordered_map<u64, VsyncManager> m_vsync_managers;
    std::shared_ptr<Core::Timing::EventType> m_event;
    Common::Event m_signal;
    std::jthread m_thread;

private:
    s32 m_swap_interval = 1;
    f32 m_compose_speed_scale = 1.0f;
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/service/nvdrv/nvdrv_interface.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
#include "core/hle/service/nvnflinger/hos_binder_driver.h"
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvnflinger/surface_flinger.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/vi_results.h"

namespace Service::VI {

Container::Container(Core::System& system) {
    m_displays.CreateDisplay(DisplayName{"Default"});
    m_displays.CreateDisplay(DisplayName{"External"});
    m_displays.CreateDisplay(DisplayName{"Edid"});
    m_displays.CreateDisplay(DisplayName{"Internal"});
    m_displays.CreateDisplay(DisplayName{"Null"});

    m_binder_driver =
        system.ServiceManager().GetService<Nvnflinger::IHOSBinderDriver>("dispdrv", true);
    m_surface_flinger = m_binder_driver->GetSurfaceFlinger();

    const auto nvdrv =
        system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule();
    m_shared_buffer_manager.emplace(system, *this, nvdrv);

    m_displays.ForEachDisplay(
        [&](auto& display) { m_surface_flinger->AddDisplay(display.GetId()); });

    m_conductor.emplace(system, *this, m_displays);
}

Container::~Container() {
    this->OnTerminate();
}

void Container::OnTerminate() {
    std::scoped_lock lk{m_lock};

    m_is_shut_down = true;

    m_layers.ForEachLayer([&](auto& layer) { this->DestroyLayerLocked(layer.GetId()); });

    m_displays.ForEachDisplay(
        [&](auto& display) { m_surface_flinger->RemoveDisplay(display.GetId()); });
}

SharedBufferManager* Container::GetSharedBufferManager() {
    return std::addressof(*m_shared_buffer_manager);
}

Result Container::GetBinderDriver(
    std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver) {
    *out_binder_driver = m_binder_driver;
    R_SUCCEED();
}

Result Container::GetLayerProducerHandle(
    std::shared_ptr<android::BufferQueueProducer>* out_producer, u64 layer_id) {
    std::scoped_lock lk{m_lock};

    auto* const layer = m_layers.GetLayerById(layer_id);
    R_UNLESS(layer != nullptr, VI::ResultNotFound);

    const auto binder = m_binder_driver->GetServer()->TryGetBinder(layer->GetProducerBinderId());
    R_UNLESS(binder != nullptr, VI::ResultNotFound);

    *out_producer = std::static_pointer_cast<android::BufferQueueProducer>(binder);
    R_SUCCEED();
}

Result Container::OpenDisplay(u64* out_display_id, const DisplayName& display_name) {
    auto* const display = m_displays.GetDisplayByName(display_name);
    R_UNLESS(display != nullptr, VI::ResultNotFound);

    *out_display_id = display->GetId();
    R_SUCCEED();
}

Result Container::CloseDisplay(u64 display_id) {
    R_SUCCEED();
}

Result Container::CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid) {
    std::scoped_lock lk{m_lock};
    R_RETURN(this->CreateLayerLocked(out_layer_id, display_id, owner_aruid));
}

Result Container::DestroyManagedLayer(u64 layer_id) {
    std::scoped_lock lk{m_lock};

    // Try to close, if open, but don't fail if not.
    this->CloseLayerLocked(layer_id);

    R_RETURN(this->DestroyLayerLocked(layer_id));
}

Result Container::OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid) {
    std::scoped_lock lk{m_lock};
    R_RETURN(this->OpenLayerLocked(out_producer_binder_id, layer_id, aruid));
}

Result Container::CloseLayer(u64 layer_id) {
    std::scoped_lock lk{m_lock};
    R_RETURN(this->CloseLayerLocked(layer_id));
}

Result Container::SetLayerVisibility(u64 layer_id, bool visible) {
    std::scoped_lock lk{m_lock};

    auto* const layer = m_layers.GetLayerById(layer_id);
    R_UNLESS(layer != nullptr, VI::ResultNotFound);

    m_surface_flinger->SetLayerVisibility(layer->GetConsumerBinderId(), visible);
    R_SUCCEED();
}

Result Container::SetLayerBlending(u64 layer_id, bool enabled) {
    std::scoped_lock lk{m_lock};

    auto* const layer = m_layers.GetLayerById(layer_id);
    R_UNLESS(layer != nullptr, VI::ResultNotFound);

    m_surface_flinger->SetLayerBlending(layer->GetConsumerBinderId(),
                                        enabled ? Nvnflinger::LayerBlending::Coverage
                                                : Nvnflinger::LayerBlending::None);
    R_SUCCEED();
}

void Container::LinkVsyncEvent(u64 display_id, Event* event) {
    std::scoped_lock lk{m_lock};
    m_conductor->LinkVsyncEvent(display_id, event);
}

void Container::UnlinkVsyncEvent(u64 display_id, Event* event) {
    std::scoped_lock lk{m_lock};
    m_conductor->UnlinkVsyncEvent(display_id, event);
}

Result Container::CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id) {
    std::scoped_lock lk{m_lock};
    R_TRY(this->CreateLayerLocked(out_layer_id, display_id, {}));
    R_RETURN(this->OpenLayerLocked(out_producer_binder_id, *out_layer_id, {}));
}

Result Container::DestroyStrayLayer(u64 layer_id) {
    std::scoped_lock lk{m_lock};
    R_TRY(this->CloseLayerLocked(layer_id));
    R_RETURN(this->DestroyLayerLocked(layer_id));
}

Result Container::CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid) {
    auto* const display = m_displays.GetDisplayById(display_id);
    R_UNLESS(display != nullptr, VI::ResultNotFound);

    s32 consumer_binder_id, producer_binder_id;
    m_surface_flinger->CreateBufferQueue(&consumer_binder_id, &producer_binder_id);

    auto* const layer =
        m_layers.CreateLayer(owner_aruid, display, consumer_binder_id, producer_binder_id);
    R_UNLESS(layer != nullptr, VI::ResultNotFound);

    m_surface_flinger->CreateLayer(consumer_binder_id);

    *out_layer_id = layer->GetId();
    R_SUCCEED();
}

Result Container::DestroyLayerLocked(u64 layer_id) {
    auto* const layer = m_layers.GetLayerById(layer_id);
    R_UNLESS(layer != nullptr, VI::ResultNotFound);

    m_surface_flinger->DestroyLayer(layer->GetConsumerBinderId());
    m_surface_flinger->DestroyBufferQueue(layer->GetConsumerBinderId(),
                                          layer->GetProducerBinderId());
    m_layers.DestroyLayer(layer_id);

    R_SUCCEED();
}

Result Container::OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid) {
    R_UNLESS(!m_is_shut_down, VI::ResultOperationFailed);

    auto* const layer = m_layers.GetLayerById(layer_id);
    R_UNLESS(layer != nullptr, VI::ResultNotFound);
    R_UNLESS(!layer->IsOpen(), VI::ResultOperationFailed);
    R_UNLESS(layer->GetOwnerAruid() == aruid, VI::ResultPermissionDenied);

    layer->Open();

    if (auto* display = layer->GetDisplay(); display != nullptr) {
        m_surface_flinger->AddLayerToDisplayStack(display->GetId(), layer->GetConsumerBinderId());
    }

    *out_producer_binder_id = layer->GetProducerBinderId();

    R_SUCCEED();
}

Result Container::CloseLayerLocked(u64 layer_id) {
    auto* const layer = m_layers.GetLayerById(layer_id);
    R_UNLESS(layer != nullptr, VI::ResultNotFound);
    R_UNLESS(layer->IsOpen(), VI::ResultOperationFailed);

    if (auto* display = layer->GetDisplay(); display != nullptr) {
        m_surface_flinger->RemoveLayerFromDisplayStack(display->GetId(),
                                                       layer->GetConsumerBinderId());
    }

    layer->Close();

    R_SUCCEED();
}

bool Container::ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale,
                                 u64 display_id) {
    std::scoped_lock lk{m_lock};
    return m_surface_flinger->ComposeDisplay(out_swap_interval, out_compose_speed_scale,
                                             display_id);
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <mutex>
#include <optional>

#include "core/hle/service/vi/conductor.h"
#include "core/hle/service/vi/display_list.h"
#include "core/hle/service/vi/layer_list.h"
#include "core/hle/service/vi/shared_buffer_manager.h"

union Result;

namespace Service::android {
class BufferQueueProducer;
}

namespace Service::Nvnflinger {
class IHOSBinderDriver;
class SurfaceFlinger;
} // namespace Service::Nvnflinger

namespace Service {
class Event;
}

namespace Service::VI {

class SharedBufferManager;

class Container {
public:
    explicit Container(Core::System& system);
    ~Container();

    void OnTerminate();

    SharedBufferManager* GetSharedBufferManager();

    Result GetBinderDriver(std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver);
    Result GetLayerProducerHandle(std::shared_ptr<android::BufferQueueProducer>* out_producer,
                                  u64 layer_id);

    Result OpenDisplay(u64* out_display_id, const DisplayName& display_name);
    Result CloseDisplay(u64 display_id);

    // Managed layers are created by the interaction between am and ommdisp
    // on behalf of an applet. Their lifetime ends with the lifetime of the
    // applet's ISelfController.
    Result CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid);
    Result DestroyManagedLayer(u64 layer_id);
    Result OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid);
    Result CloseLayer(u64 layer_id);

    // Stray layers are created by non-applet sysmodules. Their lifetime ends
    // with the lifetime of the IApplicationDisplayService which created them.
    Result CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id);
    Result DestroyStrayLayer(u64 layer_id);

    Result SetLayerVisibility(u64 layer_id, bool visible);
    Result SetLayerBlending(u64 layer_id, bool enabled);

    void LinkVsyncEvent(u64 display_id, Event* event);
    void UnlinkVsyncEvent(u64 display_id, Event* event);

private:
    Result CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid);
    Result DestroyLayerLocked(u64 layer_id);
    Result OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid);
    Result CloseLayerLocked(u64 layer_id);

public:
    bool ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id);

private:
    std::mutex m_lock{};
    DisplayList m_displays{};
    LayerList m_layers{};
    std::shared_ptr<Nvnflinger::IHOSBinderDriver> m_binder_driver{};
    std::shared_ptr<Nvnflinger::SurfaceFlinger> m_surface_flinger{};
    std::optional<SharedBufferManager> m_shared_buffer_manager{};
    std::optional<Conductor> m_conductor{};
    bool m_is_shut_down{};
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/vi/vi_types.h"

namespace Service::VI {

class Display {
public:
    constexpr Display() = default;

    void Initialize(u64 id, const DisplayName& display_name) {
        m_id = id;
        m_display_name = display_name;
        m_is_initialized = true;
    }

    void Finalize() {
        m_id = {};
        m_display_name = {};
        m_is_initialized = {};
    }

    u64 GetId() const {
        return m_id;
    }

    const DisplayName& GetDisplayName() const {
        return m_display_name;
    }

    bool IsInitialized() const {
        return m_is_initialized;
    }

private:
    u64 m_id{};
    DisplayName m_display_name{};
    bool m_is_initialized{};
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <cstring>

#include "core/hle/service/vi/display.h"

namespace Service::VI {

class DisplayList {
public:
    constexpr DisplayList() = default;

    bool CreateDisplay(const DisplayName& name) {
        Display* const display = this->GetFreeDisplay();
        if (!display) {
            return false;
        }

        display->Initialize(m_next_id++, name);
        return true;
    }

    bool DestroyDisplay(u64 display_id) {
        Display* display = this->GetDisplayById(display_id);
        if (!display) {
            return false;
        }

        display->Finalize();
        return true;
    }

    Display* GetDisplayByName(const DisplayName& name) {
        for (auto& display : m_displays) {
            if (display.IsInitialized() &&
                std::strncmp(name.data(), display.GetDisplayName().data(), sizeof(DisplayName)) ==
                    0) {
                return &display;
            }
        }

        return nullptr;
    }

    Display* GetDisplayById(u64 display_id) {
        for (auto& display : m_displays) {
            if (display.IsInitialized() && display.GetId() == display_id) {
                return &display;
            }
        }

        return nullptr;
    }

    template <typename F>
    void ForEachDisplay(F&& cb) {
        for (auto& display : m_displays) {
            if (display.IsInitialized()) {
                cb(display);
            }
        }
    }

private:
    Display* GetFreeDisplay() {
        for (auto& display : m_displays) {
            if (!display.IsInitialized()) {
                return &display;
            }
        }

        return nullptr;
    }

private:
    std::array<Display, 8> m_displays{};
    u64 m_next_id{};
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_types.h"

namespace Service::VI {

class Display;

class Layer {
public:
    constexpr Layer() = default;

    void Initialize(u64 id, u64 owner_aruid, Display* display, s32 consumer_binder_id,
                    s32 producer_binder_id) {
        m_id = id;
        m_owner_aruid = owner_aruid;
        m_display = display;
        m_consumer_binder_id = consumer_binder_id;
        m_producer_binder_id = producer_binder_id;
        m_is_initialized = true;
    }

    void Finalize() {
        m_id = {};
        m_owner_aruid = {};
        m_display = {};
        m_consumer_binder_id = {};
        m_producer_binder_id = {};
        m_is_initialized = {};
    }

    void Open() {
        m_is_open = true;
    }

    void Close() {
        m_is_open = false;
    }

    u64 GetId() const {
        return m_id;
    }

    u64 GetOwnerAruid() const {
        return m_owner_aruid;
    }

    Display* GetDisplay() const {
        return m_display;
    }

    s32 GetConsumerBinderId() const {
        return m_consumer_binder_id;
    }

    s32 GetProducerBinderId() const {
        return m_producer_binder_id;
    }

    bool IsInitialized() const {
        return m_is_initialized;
    }

    bool IsOpen() const {
        return m_is_open;
    }

private:
    u64 m_id{};
    u64 m_owner_aruid{};
    Display* m_display{};
    s32 m_consumer_binder_id{};
    s32 m_producer_binder_id{};
    bool m_is_initialized{};
    bool m_is_open{};
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/vi/layer.h"

namespace Service::VI {

class LayerList {
public:
    constexpr LayerList() = default;

    Layer* CreateLayer(u64 owner_aruid, Display* display, s32 consumer_binder_id,
                       s32 producer_binder_id) {
        Layer* const layer = GetFreeLayer();
        if (!layer) {
            return nullptr;
        }

        layer->Initialize(++m_next_id, owner_aruid, display, consumer_binder_id,
                          producer_binder_id);
        return layer;
    }

    bool DestroyLayer(u64 layer_id) {
        Layer* const layer = GetLayerById(layer_id);
        if (!layer) {
            return false;
        }

        layer->Finalize();
        return true;
    }

    Layer* GetLayerById(u64 layer_id) {
        for (auto& layer : m_layers) {
            if (layer.IsInitialized() && layer.GetId() == layer_id) {
                return &layer;
            }
        }

        return nullptr;
    }

    template <typename F>
    void ForEachLayer(F&& cb) {
        for (auto& layer : m_layers) {
            if (layer.IsInitialized()) {
                cb(layer);
            }
        }
    }

private:
    Layer* GetFreeLayer() {
        for (auto& layer : m_layers) {
            if (!layer.IsInitialized()) {
                return &layer;
            }
        }

        return nullptr;
    }

private:
    std::array<Layer, 8> m_layers{};
    u64 m_next_id{};
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/manager_display_service.h"

namespace Service::VI {

IManagerDisplayService::IManagerDisplayService(Core::System& system_,
                                               std::shared_ptr<Container> container)
    : ServiceFramework{system_, "IManagerDisplayService"}, m_container{std::move(container)} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {200, nullptr, "AllocateProcessHeapBlock"},
        {201, nullptr, "FreeProcessHeapBlock"},
        {1102, nullptr, "GetDisplayResolution"},
        {2010, C<&IManagerDisplayService::CreateManagedLayer>, "CreateManagedLayer"},
        {2011, C<&IManagerDisplayService::DestroyManagedLayer>, "DestroyManagedLayer"},
        {2012, nullptr, "CreateStrayLayer"},
        {2050, nullptr, "CreateIndirectLayer"},
        {2051, nullptr, "DestroyIndirectLayer"},
        {2052, nullptr, "CreateIndirectProducerEndPoint"},
        {2053, nullptr, "DestroyIndirectProducerEndPoint"},
        {2054, nullptr, "CreateIndirectConsumerEndPoint"},
        {2055, nullptr, "DestroyIndirectConsumerEndPoint"},
        {2060, nullptr, "CreateWatermarkCompositor"},
        {2062, nullptr, "SetWatermarkText"},
        {2063, nullptr, "SetWatermarkLayerStacks"},
        {2300, nullptr, "AcquireLayerTexturePresentingEvent"},
        {2301, nullptr, "ReleaseLayerTexturePresentingEvent"},
        {2302, nullptr, "GetDisplayHotplugEvent"},
        {2303, nullptr, "GetDisplayModeChangedEvent"},
        {2402, nullptr, "GetDisplayHotplugState"},
        {2501, nullptr, "GetCompositorErrorInfo"},
        {2601, nullptr, "GetDisplayErrorEvent"},
        {2701, nullptr, "GetDisplayFatalErrorEvent"},
        {4201, nullptr, "SetDisplayAlpha"},
        {4203, nullptr, "SetDisplayLayerStack"},
        {4205, nullptr, "SetDisplayPowerState"},
        {4206, nullptr, "SetDefaultDisplay"},
        {4207, nullptr, "ResetDisplayPanel"},
        {4208, nullptr, "SetDisplayFatalErrorEnabled"},
        {4209, nullptr, "IsDisplayPanelOn"},
        {4300, nullptr, "GetInternalPanelId"},
        {6000, C<&IManagerDisplayService::AddToLayerStack>, "AddToLayerStack"},
        {6001, nullptr, "RemoveFromLayerStack"},
        {6002, C<&IManagerDisplayService::SetLayerVisibility>, "SetLayerVisibility"},
        {6003, nullptr, "SetLayerConfig"},
        {6004, nullptr, "AttachLayerPresentationTracer"},
        {6005, nullptr, "DetachLayerPresentationTracer"},
        {6006, nullptr, "StartLayerPresentationRecording"},
        {6007, nullptr, "StopLayerPresentationRecording"},
        {6008, nullptr, "StartLayerPresentationFenceWait"},
        {6009, nullptr, "StopLayerPresentationFenceWait"},
        {6010, nullptr, "GetLayerPresentationAllFencesExpiredEvent"},
        {6011, nullptr, "EnableLayerAutoClearTransitionBuffer"},
        {6012, nullptr, "DisableLayerAutoClearTransitionBuffer"},
        {6013, nullptr, "SetLayerOpacity"},
        {6014, nullptr, "AttachLayerWatermarkCompositor"},
        {6015, nullptr, "DetachLayerWatermarkCompositor"},
        {7000, nullptr, "SetContentVisibility"},
        {8000, nullptr, "SetConductorLayer"},
        {8001, nullptr, "SetTimestampTracking"},
        {8100, nullptr, "SetIndirectProducerFlipOffset"},
        {8200, nullptr, "CreateSharedBufferStaticStorage"},
        {8201, nullptr, "CreateSharedBufferTransferMemory"},
        {8202, nullptr, "DestroySharedBuffer"},
        {8203, nullptr, "BindSharedLowLevelLayerToManagedLayer"},
        {8204, nullptr, "BindSharedLowLevelLayerToIndirectLayer"},
        {8207, nullptr, "UnbindSharedLowLevelLayer"},
        {8208, nullptr, "ConnectSharedLowLevelLayerToSharedBuffer"},
        {8209, nullptr, "DisconnectSharedLowLevelLayerFromSharedBuffer"},
        {8210, nullptr, "CreateSharedLayer"},
        {8211, nullptr, "DestroySharedLayer"},
        {8216, nullptr, "AttachSharedLayerToLowLevelLayer"},
        {8217, nullptr, "ForceDetachSharedLayerFromLowLevelLayer"},
        {8218, nullptr, "StartDetachSharedLayerFromLowLevelLayer"},
        {8219, nullptr, "FinishDetachSharedLayerFromLowLevelLayer"},
        {8220, nullptr, "GetSharedLayerDetachReadyEvent"},
        {8221, nullptr, "GetSharedLowLevelLayerSynchronizedEvent"},
        {8222, nullptr, "CheckSharedLowLevelLayerSynchronized"},
        {8223, nullptr, "RegisterSharedBufferImporterAruid"},
        {8224, nullptr, "UnregisterSharedBufferImporterAruid"},
        {8227, nullptr, "CreateSharedBufferProcessHeap"},
        {8228, nullptr, "GetSharedLayerLayerStacks"},
        {8229, nullptr, "SetSharedLayerLayerStacks"},
        {8291, nullptr, "PresentDetachedSharedFrameBufferToLowLevelLayer"},
        {8292, nullptr, "FillDetachedSharedFrameBufferColor"},
        {8293, nullptr, "GetDetachedSharedFrameBufferImage"},
        {8294, nullptr, "SetDetachedSharedFrameBufferImage"},
        {8295, nullptr, "CopyDetachedSharedFrameBufferImage"},
        {8296, nullptr, "SetDetachedSharedFrameBufferSubImage"},
        {8297, nullptr, "GetSharedFrameBufferContentParameter"},
        {8298, nullptr, "ExpandStartupLogoOnSharedFrameBuffer"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IManagerDisplayService::~IManagerDisplayService() = default;

Result IManagerDisplayService::CreateSharedLayerSession(Kernel::KProcess* owner_process,
                                                        u64* out_buffer_id, u64* out_layer_handle,
                                                        u64 display_id, bool enable_blending) {
    R_RETURN(m_container->GetSharedBufferManager()->CreateSession(
        owner_process, out_buffer_id, out_layer_handle, display_id, enable_blending));
}

void IManagerDisplayService::DestroySharedLayerSession(Kernel::KProcess* owner_process) {
    m_container->GetSharedBufferManager()->DestroySession(owner_process);
}

Result IManagerDisplayService::SetLayerBlending(bool enabled, u64 layer_id) {
    R_RETURN(m_container->SetLayerBlending(layer_id, enabled));
}

Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
                                                  AppletResourceUserId aruid) {
    LOG_DEBUG(Service_VI, "called. flags={}, display={}, aruid={}", flags, display_id, aruid.pid);
    R_RETURN(m_container->CreateManagedLayer(out_layer_id, display_id, aruid.pid));
}

Result IManagerDisplayService::DestroyManagedLayer(u64 layer_id) {
    LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id);
    R_RETURN(m_container->DestroyManagedLayer(layer_id));
}

Result IManagerDisplayService::AddToLayerStack(u32 stack_id, u64 layer_id) {
    LOG_WARNING(Service_VI, "(STUBBED) called. stack_id={}, layer_id={}", stack_id, layer_id);
    R_SUCCEED();
}

Result IManagerDisplayService::SetLayerVisibility(bool visible, u64 layer_id) {
    LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible);
    R_RETURN(m_container->SetLayerVisibility(layer_id, visible));
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Kernel {
class KProcess;
}

namespace Service::VI {

class Container;

class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
public:
    explicit IManagerDisplayService(Core::System& system_, std::shared_ptr<Container> container);
    ~IManagerDisplayService() override;

    Result CreateSharedLayerSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
                                    u64* out_layer_handle, u64 display_id, bool enable_blending);
    void DestroySharedLayerSession(Kernel::KProcess* owner_process);

    Result SetLayerBlending(bool enabled, u64 layer_id);

public:
    Result CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
                              AppletResourceUserId aruid);
    Result DestroyManagedLayer(u64 layer_id);
    Result AddToLayerStack(u32 stack_id, u64 layer_id);
    Result SetLayerVisibility(bool visible, u64 layer_id);

private:
    const std::shared_ptr<Container> m_container;
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/nvnflinger/hos_binder_driver.h"
#include "core/hle/service/vi/application_display_service.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/manager_root_service.h"
#include "core/hle/service/vi/service_creator.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_types.h"

namespace Service::VI {

IManagerRootService::IManagerRootService(Core::System& system_,
                                         std::shared_ptr<Container> container)
    : ServiceFramework{system_, "vi:m"}, m_container{std::move(container)} {
    static const FunctionInfo functions[] = {
        {2, C<&IManagerRootService::GetDisplayService>, "GetDisplayService"},
        {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
        {100, nullptr, "PrepareFatal"},
        {101, nullptr, "ShowFatal"},
        {102, nullptr, "DrawFatalRectangle"},
        {103, nullptr, "DrawFatalText32"},
    };
    RegisterHandlers(functions);
}

IManagerRootService::~IManagerRootService() = default;

Result IManagerRootService::GetDisplayService(
    Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
    LOG_DEBUG(Service_VI, "called");
    R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
                                          Permission::Manager, policy));
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::VI {

class Container;
class IApplicationDisplayService;
enum class Policy : u32;

class IManagerRootService final : public ServiceFramework<IManagerRootService> {
public:
    explicit IManagerRootService(Core::System& system_, std::shared_ptr<Container> container);
    ~IManagerRootService() override;

    Result GetDisplayService(
        Out<SharedPointer<IApplicationDisplayService>> out_application_display_service,
        Policy policy);

private:
    const std::shared_ptr<Container> m_container;
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/vi/application_display_service.h"
#include "core/hle/service/vi/service_creator.h"
#include "core/hle/service/vi/vi_results.h"
#include "core/hle/service/vi/vi_types.h"

namespace Service::VI {

static bool IsValidServiceAccess(Permission permission, Policy policy) {
    if (permission == Permission::User) {
        return policy == Policy::User;
    }

    if (permission == Permission::System || permission == Permission::Manager) {
        return policy == Policy::User || policy == Policy::Compositor;
    }

    return false;
}

Result GetApplicationDisplayService(
    std::shared_ptr<IApplicationDisplayService>* out_application_display_service,
    Core::System& system, std::shared_ptr<Container> container, Permission permission,
    Policy policy) {

    if (!IsValidServiceAccess(permission, policy)) {
        LOG_ERROR(Service_VI, "Permission denied for policy {}", policy);
        R_THROW(ResultPermissionDenied);
    }

    *out_application_display_service =
        std::make_shared<IApplicationDisplayService>(system, std::move(container));
    R_SUCCEED();
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>

#include "common/common_types.h"

namespace Core {
class System;
}

union Result;

namespace Service::VI {

class Container;
class IApplicationDisplayService;
enum class Permission;
enum class Policy : u32;

Result GetApplicationDisplayService(
    std::shared_ptr<IApplicationDisplayService>* out_application_display_service,
    Core::System& system, std::shared_ptr<Container> container, Permission permission,
    Policy policy);

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <random>

#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_system_resource.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
#include "core/hle/service/nvnflinger/pixel_format.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/shared_buffer_manager.h"
#include "core/hle/service/vi/vi_results.h"
#include "video_core/gpu.h"
#include "video_core/host1x/host1x.h"

namespace Service::VI {

namespace {

Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_group,
                                  Core::System& system, u32 size) {
    using Core::Memory::YUZU_PAGESIZE;

    // Allocate memory for the system shared buffer.
    auto& kernel = system.Kernel();

    // Hold a temporary page group reference while we try to map it.
    auto pg = std::make_unique<Kernel::KPageGroup>(
        kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager()));

    // Allocate memory from secure pool.
    R_TRY(kernel.MemoryManager().AllocateAndOpen(
        pg.get(), size / YUZU_PAGESIZE,
        Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
                                             Kernel::KMemoryManager::Direction::FromBack)));

    // Fill the output data with red.
    for (auto& block : *pg) {
        u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress());
        u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());

        for (; start < end; start++) {
            *start = 0xFF0000FF;
        }
    }

    // Return the mapped page group.
    *out_page_group = std::move(pg);

    // We succeeded.
    R_SUCCEED();
}

Result MapSharedBufferIntoProcessAddressSpace(Common::ProcessAddress* out_map_address,
                                              std::unique_ptr<Kernel::KPageGroup>& pg,
                                              Kernel::KProcess* process, Core::System& system) {
    using Core::Memory::YUZU_PAGESIZE;

    auto& page_table = process->GetPageTable();

    // Get bounds of where mapping is possible.
    const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
    const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
    const auto state = Kernel::KMemoryState::IoMemory;
    const auto perm = Kernel::KMemoryPermission::UserReadWrite;
    std::mt19937_64 rng{process->GetRandomEntropy(0)};

    // Retry up to 64 times to map into alias code range.
    Result res = ResultSuccess;
    int i;
    for (i = 0; i < 64; i++) {
        *out_map_address = alias_code_begin + ((rng() % alias_code_size) * YUZU_PAGESIZE);
        res = page_table.MapPageGroup(*out_map_address, *pg, state, perm);
        if (R_SUCCEEDED(res)) {
            break;
        }
    }

    // Return failure, if necessary
    R_UNLESS(i < 64, res);

    // We succeeded.
    R_SUCCEED();
}

Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) {
    // Create a handle.
    Nvidia::Devices::nvmap::IocCreateParams create_params{
        .size = size,
        .handle = 0,
    };
    R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success,
             VI::ResultOperationFailed);

    // Assign the output handle.
    *out_nv_map_handle = create_params.handle;

    // We succeeded.
    R_SUCCEED();
}

Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Nvidia::DeviceFD nvmap_fd) {
    // Free the handle.
    Nvidia::Devices::nvmap::IocFreeParams free_params{
        .handle = handle,
    };
    R_UNLESS(nvmap.IocFree(free_params, nvmap_fd) == Nvidia::NvResult::Success,
             VI::ResultOperationFailed);

    // We succeeded.
    R_SUCCEED();
}

Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
                        u32 size, Nvidia::DeviceFD nvmap_fd) {
    // Assign the allocated memory to the handle.
    Nvidia::Devices::nvmap::IocAllocParams alloc_params{
        .handle = handle,
        .heap_mask = 0,
        .flags = {},
        .align = 0,
        .kind = 0,
        .address = GetInteger(buffer),
    };
    R_UNLESS(nvmap.IocAlloc(alloc_params, nvmap_fd) == Nvidia::NvResult::Success,
             VI::ResultOperationFailed);

    // We succeeded.
    R_SUCCEED();
}

Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd,
                               Common::ProcessAddress buffer, u32 size) {
    // Get the nvmap device.
    auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
    ASSERT(nvmap != nullptr);

    // Create a handle.
    R_TRY(CreateNvMapHandle(out_handle, *nvmap, size));

    // Ensure we maintain a clean state on failure.
    ON_RESULT_FAILURE {
        R_ASSERT(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd));
    };

    // Assign the allocated memory to the handle.
    R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
}

void FreeHandle(u32 handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd) {
    auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
    ASSERT(nvmap != nullptr);

    R_ASSERT(FreeNvMapHandle(*nvmap, handle, nvmap_fd));
}

constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
constexpr u32 SharedBufferBlockLinearBpp = 4;

constexpr u32 SharedBufferBlockLinearWidth = 1280;
constexpr u32 SharedBufferBlockLinearHeight = 768;
constexpr u32 SharedBufferBlockLinearStride =
    SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp;
constexpr u32 SharedBufferNumSlots = 7;

constexpr u32 SharedBufferWidth = 1280;
constexpr u32 SharedBufferHeight = 720;
constexpr u32 SharedBufferAsync = false;

constexpr u32 SharedBufferSlotSize =
    SharedBufferBlockLinearWidth * SharedBufferBlockLinearHeight * SharedBufferBlockLinearBpp;
constexpr u32 SharedBufferSize = SharedBufferSlotSize * SharedBufferNumSlots;

constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
    SharedMemoryPoolLayout layout{};
    layout.num_slots = SharedBufferNumSlots;

    for (u32 i = 0; i < SharedBufferNumSlots; i++) {
        layout.slots[i].buffer_offset = i * SharedBufferSlotSize;
        layout.slots[i].size = SharedBufferSlotSize;
        layout.slots[i].width = SharedBufferWidth;
        layout.slots[i].height = SharedBufferHeight;
    }

    return layout;
}();

void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
    auto buffer = std::make_shared<android::NvGraphicBuffer>();
    buffer->width = SharedBufferWidth;
    buffer->height = SharedBufferHeight;
    buffer->stride = SharedBufferBlockLinearStride;
    buffer->format = SharedBufferBlockLinearFormat;
    buffer->external_format = SharedBufferBlockLinearFormat;
    buffer->buffer_id = handle;
    buffer->offset = slot * SharedBufferSlotSize;
    ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
}

} // namespace

SharedBufferManager::SharedBufferManager(Core::System& system, Container& container,
                                         std::shared_ptr<Nvidia::Module> nvdrv)
    : m_system(system), m_container(container), m_nvdrv(std::move(nvdrv)) {}

SharedBufferManager::~SharedBufferManager() = default;

Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
                                          u64* out_layer_handle, u64 display_id,
                                          bool enable_blending) {
    std::scoped_lock lk{m_guard};

    // Ensure we haven't already created.
    const u64 aruid = owner_process->GetProcessId();
    R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);

    // Allocate memory for the shared buffer if needed.
    if (!m_buffer_page_group) {
        R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
                                         SharedBufferSize));

        // Record buffer id.
        m_buffer_id = m_next_buffer_id++;

        // Record display id.
        m_display_id = display_id;
    }

    // Map into process.
    Common::ProcessAddress map_address{};
    R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
                                                 owner_process, m_system));

    // Create new session.
    auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
    auto& session = it->second;

    auto& container = m_nvdrv->GetContainer();
    session.session_id = container.OpenSession(owner_process);
    session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);

    // Create an nvmap handle for the buffer and assign the memory to it.
    R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
                                  session.nvmap_fd, map_address, SharedBufferSize));

    // Create and open a layer for the display.
    s32 producer_binder_id;
    R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
                                       std::addressof(session.layer_id), display_id));

    // Configure blending.
    R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));

    // Get the producer and set preallocated buffers.
    std::shared_ptr<android::BufferQueueProducer> producer;
    R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id));
    MakeGraphicBuffer(*producer, 0, session.buffer_nvmap_handle);
    MakeGraphicBuffer(*producer, 1, session.buffer_nvmap_handle);

    // Assign outputs.
    *out_buffer_id = m_buffer_id;
    *out_layer_handle = session.layer_id;

    // We succeeded.
    R_SUCCEED();
}

void SharedBufferManager::DestroySession(Kernel::KProcess* owner_process) {
    std::scoped_lock lk{m_guard};

    if (m_buffer_id == 0) {
        return;
    }

    const u64 aruid = owner_process->GetProcessId();
    const auto it = m_sessions.find(aruid);
    if (it == m_sessions.end()) {
        return;
    }

    auto& session = it->second;

    // Destroy the layer.
    m_container.DestroyStrayLayer(session.layer_id);

    // Close nvmap handle.
    FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);

    // Close nvmap device.
    m_nvdrv->Close(session.nvmap_fd);

    // Close session.
    auto& container = m_nvdrv->GetContainer();
    container.CloseSession(session.session_id);

    // Erase.
    m_sessions.erase(it);
}

Result SharedBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
                                                          s32* out_nvmap_handle,
                                                          SharedMemoryPoolLayout* out_pool_layout,
                                                          u64 buffer_id,
                                                          u64 applet_resource_user_id) {
    std::scoped_lock lk{m_guard};

    R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
    R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
    R_UNLESS(m_sessions.contains(applet_resource_user_id), VI::ResultNotFound);

    *out_pool_layout = SharedBufferPoolLayout;
    *out_buffer_size = SharedBufferSize;
    *out_nvmap_handle = m_sessions[applet_resource_user_id].buffer_nvmap_handle;

    R_SUCCEED();
}

Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
                                                     std::array<s32, 4>& out_slot_indexes,
                                                     s64* out_target_slot, u64 layer_id) {
    // Get the producer.
    std::shared_ptr<android::BufferQueueProducer> producer;
    R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));

    // Get the next buffer from the producer.
    s32 slot;
    R_UNLESS(producer->DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0,
                                     SharedBufferWidth, SharedBufferHeight,
                                     SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
             VI::ResultOperationFailed);

    // Assign remaining outputs.
    *out_target_slot = slot;
    out_slot_indexes = {0, 1, -1, -1};

    // We succeeded.
    R_SUCCEED();
}

Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
                                                     Common::Rectangle<s32> crop_region,
                                                     u32 transform, s32 swap_interval, u64 layer_id,
                                                     s64 slot) {
    // Get the producer.
    std::shared_ptr<android::BufferQueueProducer> producer;
    R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));

    // Request to queue the buffer.
    std::shared_ptr<android::GraphicBuffer> buffer;
    R_UNLESS(producer->RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
                 android::Status::NoError,
             VI::ResultOperationFailed);

    ON_RESULT_FAILURE {
        producer->CancelBuffer(static_cast<s32>(slot), fence);
    };

    // Queue the buffer to the producer.
    android::QueueBufferInput input{};
    android::QueueBufferOutput output{};
    input.crop = crop_region;
    input.fence = fence;
    input.transform = static_cast<android::NativeWindowTransform>(transform);
    input.swap_interval = swap_interval;
    R_UNLESS(producer->QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
                 android::Status::NoError,
             VI::ResultOperationFailed);

    // We succeeded.
    R_SUCCEED();
}

Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
    // Get the producer.
    std::shared_ptr<android::BufferQueueProducer> producer;
    R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));

    // Cancel.
    producer->CancelBuffer(static_cast<s32>(slot), android::Fence::NoFence());

    // We succeeded.
    R_SUCCEED();
}

Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event,
                                                                u64 layer_id) {
    // Get the producer.
    std::shared_ptr<android::BufferQueueProducer> producer;
    R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));

    // Set the event.
    *out_event = producer->GetNativeHandle({});

    // We succeeded.
    R_SUCCEED();
}

Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
    std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
    Common::ScratchBuffer<u32> scratch;

    // TODO: this could be optimized
    s64 e = -1280 * 768 * 4;
    for (auto& block : *m_buffer_page_group) {
        u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
        u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());

        for (; start < end; start++) {
            *start = 0;

            if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
                *start = capture_buffer[e];
            }
            e++;
        }

        m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
            m_system.GPU().InvalidateRegion(addr, end - start);
        });
    }

    *out_was_written = true;
    *out_layer_index = 1;
    R_SUCCEED();
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <map>

#include "common/math_util.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/nvnflinger/ui/fence.h"

namespace Kernel {
class KPageGroup;
class KReadableEvent;
} // namespace Kernel

namespace Service::android {
class BufferQueueProducer;
}

namespace Service::Nvidia {
class Module;
}

union Result;

namespace Service::VI {

class Container;

struct SharedMemorySlot {
    u64 buffer_offset;
    u64 size;
    s32 width;
    s32 height;
};
static_assert(sizeof(SharedMemorySlot) == 0x18, "SharedMemorySlot has wrong size");

struct SharedMemoryPoolLayout {
    s32 num_slots;
    std::array<SharedMemorySlot, 0x10> slots;
};
static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");

struct SharedBufferSession;

class SharedBufferManager final {
public:
    explicit SharedBufferManager(Core::System& system, Container& container,
                                 std::shared_ptr<Nvidia::Module> nvdrv);
    ~SharedBufferManager();

    Result CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle,
                         u64 display_id, bool enable_blending);
    void DestroySession(Kernel::KProcess* owner_process);

    Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
                                         SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
                                         u64 applet_resource_user_id);
    Result AcquireSharedFrameBuffer(android::Fence* out_fence, std::array<s32, 4>& out_slots,
                                    s64* out_target_slot, u64 layer_id);
    Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
                                    u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
    Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);
    Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);

    Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);

private:
    u64 m_next_buffer_id = 1;
    u64 m_display_id = 0;
    u64 m_buffer_id = 0;
    SharedMemoryPoolLayout m_pool_layout = {};
    std::map<u64, SharedBufferSession> m_sessions;
    std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;

    std::mutex m_guard;
    Core::System& m_system;
    Container& m_container;
    const std::shared_ptr<Nvidia::Module> m_nvdrv;
};

struct SharedBufferSession {
    Nvidia::DeviceFD nvmap_fd = {};
    Nvidia::NvCore::SessionId session_id = {};
    u64 layer_id = {};
    u32 buffer_nvmap_handle = 0;
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/settings.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/system_display_service.h"
#include "core/hle/service/vi/vi_types.h"

namespace Service::VI {

ISystemDisplayService::ISystemDisplayService(Core::System& system_,
                                             std::shared_ptr<Container> container)
    : ServiceFramework{system_, "ISystemDisplayService"}, m_container{std::move(container)} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {1200, nullptr, "GetZOrderCountMin"},
        {1202, nullptr, "GetZOrderCountMax"},
        {1203, nullptr, "GetDisplayLogicalResolution"},
        {1204, nullptr, "SetDisplayMagnification"},
        {2201, nullptr, "SetLayerPosition"},
        {2203, nullptr, "SetLayerSize"},
        {2204, nullptr, "GetLayerZ"},
        {2205, C<&ISystemDisplayService::SetLayerZ>, "SetLayerZ"},
        {2207, C<&ISystemDisplayService::SetLayerVisibility>, "SetLayerVisibility"},
        {2209, nullptr, "SetLayerAlpha"},
        {2210, nullptr, "SetLayerPositionAndSize"},
        {2312, nullptr, "CreateStrayLayer"},
        {2400, nullptr, "OpenIndirectLayer"},
        {2401, nullptr, "CloseIndirectLayer"},
        {2402, nullptr, "FlipIndirectLayer"},
        {3000, C<&ISystemDisplayService::ListDisplayModes>, "ListDisplayModes"},
        {3001, nullptr, "ListDisplayRgbRanges"},
        {3002, nullptr, "ListDisplayContentTypes"},
        {3200, C<&ISystemDisplayService::GetDisplayMode>, "GetDisplayMode"},
        {3201, nullptr, "SetDisplayMode"},
        {3202, nullptr, "GetDisplayUnderscan"},
        {3203, nullptr, "SetDisplayUnderscan"},
        {3204, nullptr, "GetDisplayContentType"},
        {3205, nullptr, "SetDisplayContentType"},
        {3206, nullptr, "GetDisplayRgbRange"},
        {3207, nullptr, "SetDisplayRgbRange"},
        {3208, nullptr, "GetDisplayCmuMode"},
        {3209, nullptr, "SetDisplayCmuMode"},
        {3210, nullptr, "GetDisplayContrastRatio"},
        {3211, nullptr, "SetDisplayContrastRatio"},
        {3214, nullptr, "GetDisplayGamma"},
        {3215, nullptr, "SetDisplayGamma"},
        {3216, nullptr, "GetDisplayCmuLuma"},
        {3217, nullptr, "SetDisplayCmuLuma"},
        {3218, nullptr, "SetDisplayCrcMode"},
        {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
        {8225, C<&ISystemDisplayService::GetSharedBufferMemoryHandleId>, "GetSharedBufferMemoryHandleId"},
        {8250, C<&ISystemDisplayService::OpenSharedLayer>, "OpenSharedLayer"},
        {8251, nullptr, "CloseSharedLayer"},
        {8252, C<&ISystemDisplayService::ConnectSharedLayer>, "ConnectSharedLayer"},
        {8253, nullptr, "DisconnectSharedLayer"},
        {8254, C<&ISystemDisplayService::AcquireSharedFrameBuffer>, "AcquireSharedFrameBuffer"},
        {8255, C<&ISystemDisplayService::PresentSharedFrameBuffer>, "PresentSharedFrameBuffer"},
        {8256, C<&ISystemDisplayService::GetSharedFrameBufferAcquirableEvent>, "GetSharedFrameBufferAcquirableEvent"},
        {8257, nullptr, "FillSharedFrameBufferColor"},
        {8258, C<&ISystemDisplayService::CancelSharedFrameBuffer>, "CancelSharedFrameBuffer"},
        {9000, nullptr, "GetDp2hdmiController"},
    };
    // clang-format on
    RegisterHandlers(functions);
}

ISystemDisplayService::~ISystemDisplayService() = default;

Result ISystemDisplayService::SetLayerZ(u32 z_value, u64 layer_id) {
    LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}, z_value={}", layer_id, z_value);
    R_SUCCEED();
}

// This function currently does nothing but return a success error code in
// the vi library itself, so do the same thing, but log out the passed in values.
Result ISystemDisplayService::SetLayerVisibility(bool visible, u64 layer_id) {
    LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible);
    R_SUCCEED();
}

Result ISystemDisplayService::ListDisplayModes(
    Out<u64> out_count, u64 display_id,
    OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes) {
    LOG_WARNING(Service_VI, "(STUBBED) called, display_id={}", display_id);

    if (!out_display_modes.empty()) {
        out_display_modes[0] = {
            .width = 1920,
            .height = 1080,
            .refresh_rate = 60.f,
            .unknown = {},
        };
        *out_count = 1;
    } else {
        *out_count = 0;
    }

    R_SUCCEED();
}

Result ISystemDisplayService::GetDisplayMode(Out<DisplayMode> out_display_mode, u64 display_id) {
    LOG_WARNING(Service_VI, "(STUBBED) called, display_id={}", display_id);

    if (Settings::IsDockedMode()) {
        out_display_mode->width = static_cast<u32>(DisplayResolution::DockedWidth);
        out_display_mode->height = static_cast<u32>(DisplayResolution::DockedHeight);
    } else {
        out_display_mode->width = static_cast<u32>(DisplayResolution::UndockedWidth);
        out_display_mode->height = static_cast<u32>(DisplayResolution::UndockedHeight);
    }

    out_display_mode->refresh_rate = 60.f; // This wouldn't seem to be correct for 30 fps games.
    out_display_mode->unknown = 0;

    R_SUCCEED();
}

Result ISystemDisplayService::GetSharedBufferMemoryHandleId(
    Out<s32> out_nvmap_handle, Out<u64> out_size,
    OutLargeData<SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout, u64 buffer_id,
    ClientAppletResourceUserId aruid) {
    LOG_INFO(Service_VI, "called. buffer_id={}, aruid={:#x}", buffer_id, aruid.pid);

    R_RETURN(m_container->GetSharedBufferManager()->GetSharedBufferMemoryHandleId(
        out_size, out_nvmap_handle, out_pool_layout, buffer_id, aruid.pid));
}

Result ISystemDisplayService::OpenSharedLayer(u64 layer_id) {
    LOG_INFO(Service_VI, "(STUBBED) called. layer_id={}", layer_id);
    R_SUCCEED();
}

Result ISystemDisplayService::ConnectSharedLayer(u64 layer_id) {
    LOG_INFO(Service_VI, "(STUBBED) called. layer_id={}", layer_id);
    R_SUCCEED();
}

Result ISystemDisplayService::AcquireSharedFrameBuffer(Out<android::Fence> out_fence,
                                                       Out<std::array<s32, 4>> out_slots,
                                                       Out<s64> out_target_slot, u64 layer_id) {
    LOG_DEBUG(Service_VI, "called");
    R_RETURN(m_container->GetSharedBufferManager()->AcquireSharedFrameBuffer(
        out_fence, *out_slots, out_target_slot, layer_id));
}

Result ISystemDisplayService::PresentSharedFrameBuffer(android::Fence fence,
                                                       Common::Rectangle<s32> crop_region,
                                                       u32 window_transform, s32 swap_interval,
                                                       u64 layer_id, s64 surface_id) {
    LOG_DEBUG(Service_VI, "called");
    R_RETURN(m_container->GetSharedBufferManager()->PresentSharedFrameBuffer(
        fence, crop_region, window_transform, swap_interval, layer_id, surface_id));
}

Result ISystemDisplayService::GetSharedFrameBufferAcquirableEvent(
    OutCopyHandle<Kernel::KReadableEvent> out_event, u64 layer_id) {
    LOG_DEBUG(Service_VI, "called");
    R_RETURN(m_container->GetSharedBufferManager()->GetSharedFrameBufferAcquirableEvent(out_event,
                                                                                layer_id));
}

Result ISystemDisplayService::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
    LOG_DEBUG(Service_VI, "called");
    R_RETURN(m_container->GetSharedBufferManager()->CancelSharedFrameBuffer(layer_id, slot));
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/math_util.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/nvnflinger/ui/fence.h"
#include "core/hle/service/service.h"
#include "core/hle/service/vi/shared_buffer_manager.h"

namespace Service::VI {
struct DisplayMode;

class Container;

class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
public:
    explicit ISystemDisplayService(Core::System& system_, std::shared_ptr<Container> container);
    ~ISystemDisplayService() override;

private:
    Result SetLayerZ(u32 z_value, u64 layer_id);
    Result SetLayerVisibility(bool visible, u64 layer_id);
    Result ListDisplayModes(Out<u64> out_count, u64 display_id,
                            OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes);
    Result GetDisplayMode(Out<DisplayMode> out_display_mode, u64 display_id);

    Result GetSharedBufferMemoryHandleId(
        Out<s32> out_nvmap_handle, Out<u64> out_size,
        OutLargeData<SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout,
        u64 buffer_id, ClientAppletResourceUserId aruid);
    Result OpenSharedLayer(u64 layer_id);
    Result ConnectSharedLayer(u64 layer_id);
    Result GetSharedFrameBufferAcquirableEvent(OutCopyHandle<Kernel::KReadableEvent> out_event,
                                               u64 layer_id);
    Result AcquireSharedFrameBuffer(Out<android::Fence> out_fence,
                                    Out<std::array<s32, 4>> out_slots, Out<s64> out_target_slot,
                                    u64 layer_id);
    Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
                                    u32 window_transform, s32 swap_interval, u64 layer_id,
                                    s64 surface_id);
    Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);

private:
    const std::shared_ptr<Container> m_container;
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/vi/application_display_service.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/service_creator.h"
#include "core/hle/service/vi/system_root_service.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_types.h"

namespace Service::VI {

ISystemRootService::ISystemRootService(Core::System& system_, std::shared_ptr<Container> container)
    : ServiceFramework{system_, "vi:s"}, m_container{std::move(container)} {
    static const FunctionInfo functions[] = {
        {1, C<&ISystemRootService::GetDisplayService>, "GetDisplayService"},
        {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
    };
    RegisterHandlers(functions);
}

ISystemRootService::~ISystemRootService() = default;

Result ISystemRootService::GetDisplayService(
    Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
    LOG_DEBUG(Service_VI, "called");
    R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
                                          Permission::System, policy));
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::VI {

class Container;
class IApplicationDisplayService;
enum class Policy : u32;

class ISystemRootService final : public ServiceFramework<ISystemRootService> {
public:
    explicit ISystemRootService(Core::System& system_, std::shared_ptr<Container> container);
    ~ISystemRootService() override;

private:
    Result GetDisplayService(
        Out<SharedPointer<IApplicationDisplayService>> out_application_display_service,
        Policy policy);

    const std::shared_ptr<Container> m_container;
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/vi/application_root_service.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/manager_root_service.h"
#include "core/hle/service/vi/system_root_service.h"
#include "core/hle/service/vi/vi.h"

namespace Service::VI {

void LoopProcess(Core::System& system, std::stop_token token) {
    const auto container = std::make_shared<Container>(system);

    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("vi:m",
                                         std::make_shared<IManagerRootService>(system, container));
    server_manager->RegisterNamedService("vi:s",
                                         std::make_shared<ISystemRootService>(system, container));
    server_manager->RegisterNamedService(
        "vi:u", std::make_shared<IApplicationRootService>(system, container));

    std::stop_callback cb(token, [=] { container->OnTerminate(); });

    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/polyfill_thread.h"

namespace Core {
class System;
}

namespace Service::VI {

void LoopProcess(Core::System& system, std::stop_token token);

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"

namespace Service::VI {

constexpr Result ResultOperationFailed{ErrorModule::VI, 1};
constexpr Result ResultPermissionDenied{ErrorModule::VI, 5};
constexpr Result ResultNotSupported{ErrorModule::VI, 6};
constexpr Result ResultNotFound{ErrorModule::VI, 7};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_funcs.h"

namespace Service::VI {

enum class DisplayResolution : u32 {
    DockedWidth = 1920,
    DockedHeight = 1080,
    UndockedWidth = 1280,
    UndockedHeight = 720,
};

/// Permission level for a particular VI service instance
enum class Permission {
    User,
    System,
    Manager,
};

/// A policy type that may be requested via GetDisplayService and
/// GetDisplayServiceWithProxyNameExchange
enum class Policy : u32 {
    User,
    Compositor,
};

enum class ConvertedScaleMode : u64 {
    Freeze = 0,
    ScaleToWindow = 1,
    ScaleAndCrop = 2,
    None = 3,
    PreserveAspectRatio = 4,
};

enum class NintendoScaleMode : u32 {
    None = 0,
    Freeze = 1,
    ScaleToWindow = 2,
    ScaleAndCrop = 3,
    PreserveAspectRatio = 4,
};

using DisplayName = std::array<char, 0x40>;

struct DisplayInfo {
    /// The name of this particular display.
    DisplayName display_name{"Default"};

    /// Whether or not the display has a limited number of layers.
    u8 has_limited_layers{1};
    INSERT_PADDING_BYTES(7);

    /// Indicates the total amount of layers supported by the display.
    /// @note This is only valid if has_limited_layers is set.
    u64 max_layers{1};

    /// Maximum width in pixels.
    u64 width{1920};

    /// Maximum height in pixels.
    u64 height{1080};
};
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");

struct DisplayMode {
    u32 width;
    u32 height;
    f32 refresh_rate;
    u32 unknown;
};
static_assert(sizeof(DisplayMode) == 0x10, "DisplayMode has wrong size");

class NativeWindow final {
public:
    constexpr explicit NativeWindow(s32 id_) : id{static_cast<u64>(id_)} {}
    constexpr explicit NativeWindow(const NativeWindow& other) = default;

private:
    const u32 magic = 2;
    const u32 process_id = 1;
    const u64 id;
    INSERT_PADDING_WORDS(2);
    std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
    INSERT_PADDING_WORDS(2);
};
static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/os/event.h"
#include "core/hle/service/vi/vsync_manager.h"

namespace Service::VI {

VsyncManager::VsyncManager() = default;
VsyncManager::~VsyncManager() = default;

void VsyncManager::SignalVsync() {
    for (auto* event : m_vsync_events) {
        event->Signal();
    }
}

void VsyncManager::LinkVsyncEvent(Event* event) {
    m_vsync_events.insert(event);
}

void VsyncManager::UnlinkVsyncEvent(Event* event) {
    m_vsync_events.erase(event);
}

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <set>

namespace Service {
class Event;
}

namespace Service::VI {

class DisplayList;

class VsyncManager {
public:
    explicit VsyncManager();
    ~VsyncManager();

    void SignalVsync();
    void LinkVsyncEvent(Event* event);
    void UnlinkVsyncEvent(Event* event);

private:
    std::set<Event*> m_vsync_events;
};

} // namespace Service::VI


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <cstring>
#include <limits>
#include <utility>
#include <vector>

#include "common/error.h"

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#elif YUZU_UNIX
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <sys/socket.h>
#include <unistd.h>
#else
#error "Unimplemented platform"
#endif

#include "common/assert.h"
#include "common/common_types.h"
#include "common/expected.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/internal_network/network.h"
#include "core/internal_network/network_interface.h"
#include "core/internal_network/sockets.h"
#include "network/network.h"

namespace Network {

namespace {

enum class CallType {
    Send,
    Other,
};

#ifdef _WIN32

using socklen_t = int;

SOCKET interrupt_socket = static_cast<SOCKET>(-1);

void InterruptSocketOperations() {
    closesocket(interrupt_socket);
}

void AcknowledgeInterrupt() {
    interrupt_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
}

void Initialize() {
    WSADATA wsa_data;
    (void)WSAStartup(MAKEWORD(2, 2), &wsa_data);

    AcknowledgeInterrupt();
}

void Finalize() {
    InterruptSocketOperations();
    WSACleanup();
}

SOCKET GetInterruptSocket() {
    return interrupt_socket;
}

sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
    sockaddr_in result;

#if YUZU_UNIX
    result.sin_len = sizeof(result);
#endif

    switch (static_cast<Domain>(input.family)) {
    case Domain::INET:
        result.sin_family = AF_INET;
        break;
    default:
        UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family);
        result.sin_family = AF_INET;
        break;
    }

    result.sin_port = htons(input.portno);

    auto& ip = result.sin_addr.S_un.S_un_b;
    ip.s_b1 = input.ip[0];
    ip.s_b2 = input.ip[1];
    ip.s_b3 = input.ip[2];
    ip.s_b4 = input.ip[3];

    sockaddr addr;
    std::memcpy(&addr, &result, sizeof(addr));
    return addr;
}

LINGER MakeLinger(bool enable, u32 linger_value) {
    ASSERT(linger_value <= std::numeric_limits<u_short>::max());

    LINGER value;
    value.l_onoff = enable ? 1 : 0;
    value.l_linger = static_cast<u_short>(linger_value);
    return value;
}

bool EnableNonBlock(SOCKET fd, bool enable) {
    u_long value = enable ? 1 : 0;
    return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
}

Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
    switch (e) {
    case 0:
        return Errno::SUCCESS;
    case WSAEBADF:
        return Errno::BADF;
    case WSAEINVAL:
        return Errno::INVAL;
    case WSAEMFILE:
        return Errno::MFILE;
    case WSAENOTCONN:
        return Errno::NOTCONN;
    case WSAEWOULDBLOCK:
        return Errno::AGAIN;
    case WSAECONNREFUSED:
        return Errno::CONNREFUSED;
    case WSAECONNABORTED:
        if (call_type == CallType::Send) {
            // Winsock yields WSAECONNABORTED from `send` in situations where Unix
            // systems, and actual Switches, yield EPIPE.
            return Errno::PIPE;
        } else {
            return Errno::CONNABORTED;
        }
    case WSAECONNRESET:
        return Errno::CONNRESET;
    case WSAEHOSTUNREACH:
        return Errno::HOSTUNREACH;
    case WSAENETDOWN:
        return Errno::NETDOWN;
    case WSAENETUNREACH:
        return Errno::NETUNREACH;
    case WSAEMSGSIZE:
        return Errno::MSGSIZE;
    case WSAETIMEDOUT:
        return Errno::TIMEDOUT;
    case WSAEINPROGRESS:
        return Errno::INPROGRESS;
    default:
        UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
        return Errno::OTHER;
    }
}

#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX

using SOCKET = int;
using WSAPOLLFD = pollfd;
using ULONG = u64;

constexpr SOCKET SOCKET_ERROR = -1;

constexpr int SD_RECEIVE = SHUT_RD;
constexpr int SD_SEND = SHUT_WR;
constexpr int SD_BOTH = SHUT_RDWR;

int interrupt_pipe_fd[2] = {-1, -1};

void Initialize() {
    if (pipe(interrupt_pipe_fd) != 0) {
        LOG_ERROR(Network, "Failed to create interrupt pipe!");
    }
    int flags = fcntl(interrupt_pipe_fd[0], F_GETFL);
    ASSERT_MSG(fcntl(interrupt_pipe_fd[0], F_SETFL, flags | O_NONBLOCK) == 0,
               "Failed to set nonblocking state for interrupt pipe");
}

void Finalize() {
    if (interrupt_pipe_fd[0] >= 0) {
        close(interrupt_pipe_fd[0]);
    }
    if (interrupt_pipe_fd[1] >= 0) {
        close(interrupt_pipe_fd[1]);
    }
}

void InterruptSocketOperations() {
    u8 value = 0;
    ASSERT(write(interrupt_pipe_fd[1], &value, sizeof(value)) == 1);
}

void AcknowledgeInterrupt() {
    u8 value = 0;
    ssize_t ret = read(interrupt_pipe_fd[0], &value, sizeof(value));
    if (ret != 1 && errno != EAGAIN && errno != EWOULDBLOCK) {
        LOG_ERROR(Network, "Failed to acknowledge interrupt on shutdown");
    }
}

SOCKET GetInterruptSocket() {
    return interrupt_pipe_fd[0];
}

sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
    sockaddr_in result;

    switch (static_cast<Domain>(input.family)) {
    case Domain::INET:
        result.sin_family = AF_INET;
        break;
    default:
        UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family);
        result.sin_family = AF_INET;
        break;
    }

    result.sin_port = htons(input.portno);

    result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24;

    sockaddr addr;
    std::memcpy(&addr, &result, sizeof(addr));
    return addr;
}

int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) {
    return poll(fds, static_cast<nfds_t>(nfds), timeout);
}

int closesocket(SOCKET fd) {
    return close(fd);
}

linger MakeLinger(bool enable, u32 linger_value) {
    linger value;
    value.l_onoff = enable ? 1 : 0;
    value.l_linger = linger_value;
    return value;
}

bool EnableNonBlock(int fd, bool enable) {
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1) {
        return false;
    }
    if (enable) {
        flags |= O_NONBLOCK;
    } else {
        flags &= ~O_NONBLOCK;
    }
    return fcntl(fd, F_SETFL, flags) == 0;
}

Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
    switch (e) {
    case 0:
        return Errno::SUCCESS;
    case EBADF:
        return Errno::BADF;
    case EINVAL:
        return Errno::INVAL;
    case EMFILE:
        return Errno::MFILE;
    case EPIPE:
        return Errno::PIPE;
    case ECONNABORTED:
        return Errno::CONNABORTED;
    case ENOTCONN:
        return Errno::NOTCONN;
    case EAGAIN:
        return Errno::AGAIN;
    case ECONNREFUSED:
        return Errno::CONNREFUSED;
    case ECONNRESET:
        return Errno::CONNRESET;
    case EHOSTUNREACH:
        return Errno::HOSTUNREACH;
    case ENETDOWN:
        return Errno::NETDOWN;
    case ENETUNREACH:
        return Errno::NETUNREACH;
    case EMSGSIZE:
        return Errno::MSGSIZE;
    case ETIMEDOUT:
        return Errno::TIMEDOUT;
    case EINPROGRESS:
        return Errno::INPROGRESS;
    default:
        UNIMPLEMENTED_MSG("Unimplemented errno={} ({})", e, strerror(e));
        return Errno::OTHER;
    }
}

#endif

Errno GetAndLogLastError(CallType call_type = CallType::Other) {
#ifdef _WIN32
    int e = WSAGetLastError();
#else
    int e = errno;
#endif
    const Errno err = TranslateNativeError(e, call_type);
    if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) {
        // These happen during normal operation, so only log them at debug level.
        LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
        return err;
    }
    LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
    return err;
}

GetAddrInfoError TranslateGetAddrInfoErrorFromNative(int gai_err) {
    switch (gai_err) {
    case 0:
        return GetAddrInfoError::SUCCESS;
#ifdef EAI_ADDRFAMILY
    case EAI_ADDRFAMILY:
        return GetAddrInfoError::ADDRFAMILY;
#endif
    case EAI_AGAIN:
        return GetAddrInfoError::AGAIN;
    case EAI_BADFLAGS:
        return GetAddrInfoError::BADFLAGS;
    case EAI_FAIL:
        return GetAddrInfoError::FAIL;
    case EAI_FAMILY:
        return GetAddrInfoError::FAMILY;
    case EAI_MEMORY:
        return GetAddrInfoError::MEMORY;
    case EAI_NONAME:
        return GetAddrInfoError::NONAME;
    case EAI_SERVICE:
        return GetAddrInfoError::SERVICE;
    case EAI_SOCKTYPE:
        return GetAddrInfoError::SOCKTYPE;
        // These codes may not be defined on all systems:
#ifdef EAI_SYSTEM
    case EAI_SYSTEM:
        return GetAddrInfoError::SYSTEM;
#endif
#ifdef EAI_BADHINTS
    case EAI_BADHINTS:
        return GetAddrInfoError::BADHINTS;
#endif
#ifdef EAI_PROTOCOL
    case EAI_PROTOCOL:
        return GetAddrInfoError::PROTOCOL;
#endif
#ifdef EAI_OVERFLOW
    case EAI_OVERFLOW:
        return GetAddrInfoError::OVERFLOW_;
#endif
    default:
#ifdef EAI_NODATA
        // This can't be a case statement because it would create a duplicate
        // case on Windows where EAI_NODATA is an alias for EAI_NONAME.
        if (gai_err == EAI_NODATA) {
            return GetAddrInfoError::NODATA;
        }
#endif
        return GetAddrInfoError::OTHER;
    }
}

Domain TranslateDomainFromNative(int domain) {
    switch (domain) {
    case 0:
        return Domain::Unspecified;
    case AF_INET:
        return Domain::INET;
    default:
        UNIMPLEMENTED_MSG("Unhandled domain={}", domain);
        return Domain::INET;
    }
}

int TranslateDomainToNative(Domain domain) {
    switch (domain) {
    case Domain::Unspecified:
        return 0;
    case Domain::INET:
        return AF_INET;
    default:
        UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
        return 0;
    }
}

Type TranslateTypeFromNative(int type) {
    switch (type) {
    case 0:
        return Type::Unspecified;
    case SOCK_STREAM:
        return Type::STREAM;
    case SOCK_DGRAM:
        return Type::DGRAM;
    case SOCK_RAW:
        return Type::RAW;
    case SOCK_SEQPACKET:
        return Type::SEQPACKET;
    default:
        UNIMPLEMENTED_MSG("Unimplemented type={}", type);
        return Type::STREAM;
    }
}

int TranslateTypeToNative(Type type) {
    switch (type) {
    case Type::Unspecified:
        return 0;
    case Type::STREAM:
        return SOCK_STREAM;
    case Type::DGRAM:
        return SOCK_DGRAM;
    case Type::RAW:
        return SOCK_RAW;
    default:
        UNIMPLEMENTED_MSG("Unimplemented type={}", type);
        return 0;
    }
}

Protocol TranslateProtocolFromNative(int protocol) {
    switch (protocol) {
    case 0:
        return Protocol::Unspecified;
    case IPPROTO_TCP:
        return Protocol::TCP;
    case IPPROTO_UDP:
        return Protocol::UDP;
    default:
        UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
        return Protocol::Unspecified;
    }
}

int TranslateProtocolToNative(Protocol protocol) {
    switch (protocol) {
    case Protocol::Unspecified:
        return 0;
    case Protocol::TCP:
        return IPPROTO_TCP;
    case Protocol::UDP:
        return IPPROTO_UDP;
    default:
        UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
        return 0;
    }
}

SockAddrIn TranslateToSockAddrIn(sockaddr_in input, size_t input_len) {
    SockAddrIn result;

    result.family = TranslateDomainFromNative(input.sin_family);

    result.portno = ntohs(input.sin_port);

    result.ip = TranslateIPv4(input.sin_addr);

    return result;
}

short TranslatePollEvents(PollEvents events) {
    short result = 0;

    const auto translate = [&result, &events](PollEvents guest, short host) {
        if (True(events & guest)) {
            events &= ~guest;
            result |= host;
        }
    };

    translate(PollEvents::In, POLLIN);
    translate(PollEvents::Pri, POLLPRI);
    translate(PollEvents::Out, POLLOUT);
    translate(PollEvents::Err, POLLERR);
    translate(PollEvents::Hup, POLLHUP);
    translate(PollEvents::Nval, POLLNVAL);
    translate(PollEvents::RdNorm, POLLRDNORM);
    translate(PollEvents::RdBand, POLLRDBAND);
    translate(PollEvents::WrBand, POLLWRBAND);

#ifdef _WIN32
    short allowed_events = POLLRDBAND | POLLRDNORM | POLLWRNORM;
    // Unlike poll on other OSes, WSAPoll will complain if any other flags are set on input.
    if (result & ~allowed_events) {
        LOG_DEBUG(Network,
                  "Removing WSAPoll input events 0x{:x} because Windows doesn't support them",
                  result & ~allowed_events);
    }
    result &= allowed_events;
#endif

    UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events);

    return result;
}

PollEvents TranslatePollRevents(short revents) {
    PollEvents result{};
    const auto translate = [&result, &revents](short host, PollEvents guest) {
        if ((revents & host) != 0) {
            revents &= static_cast<short>(~host);
            result |= guest;
        }
    };

    translate(POLLIN, PollEvents::In);
    translate(POLLPRI, PollEvents::Pri);
    translate(POLLOUT, PollEvents::Out);
    translate(POLLERR, PollEvents::Err);
    translate(POLLHUP, PollEvents::Hup);
    translate(POLLNVAL, PollEvents::Nval);
    translate(POLLRDNORM, PollEvents::RdNorm);
    translate(POLLRDBAND, PollEvents::RdBand);
    translate(POLLWRBAND, PollEvents::WrBand);

    UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents);

    return result;
}

} // Anonymous namespace

NetworkInstance::NetworkInstance() {
    Initialize();
}

NetworkInstance::~NetworkInstance() {
    Finalize();
}

void CancelPendingSocketOperations() {
    InterruptSocketOperations();
}

void RestartSocketOperations() {
    AcknowledgeInterrupt();
}

std::optional<IPv4Address> GetHostIPv4Address() {
    const auto network_interface = Network::GetSelectedNetworkInterface();
    if (!network_interface.has_value()) {
        // Only print the error once to avoid log spam
        static bool print_error = true;
        if (print_error) {
            LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
            print_error = false;
        }

        return {};
    }

    return TranslateIPv4(network_interface->ip_address);
}

std::string IPv4AddressToString(IPv4Address ip_addr) {
    std::array<char, INET_ADDRSTRLEN> buf = {};
    ASSERT(inet_ntop(AF_INET, &ip_addr, buf.data(), sizeof(buf)) == buf.data());
    return std::string(buf.data());
}

u32 IPv4AddressToInteger(IPv4Address ip_addr) {
    return static_cast<u32>(ip_addr[0]) << 24 | static_cast<u32>(ip_addr[1]) << 16 |
           static_cast<u32>(ip_addr[2]) << 8 | static_cast<u32>(ip_addr[3]);
}

Common::Expected<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(
    const std::string& host, const std::optional<std::string>& service) {
    addrinfo hints{};
    hints.ai_family = AF_INET; // Switch only supports IPv4.
    addrinfo* addrinfo;
    s32 gai_err = getaddrinfo(host.c_str(), service.has_value() ? service->c_str() : nullptr,
                              &hints, &addrinfo);
    if (gai_err != 0) {
        return Common::Unexpected(TranslateGetAddrInfoErrorFromNative(gai_err));
    }
    std::vector<AddrInfo> ret;
    for (auto* current = addrinfo; current; current = current->ai_next) {
        // We should only get AF_INET results due to the hints value.
        ASSERT_OR_EXECUTE(addrinfo->ai_family == AF_INET &&
                              addrinfo->ai_addrlen == sizeof(sockaddr_in),
                          continue;);

        AddrInfo& out = ret.emplace_back();
        out.family = TranslateDomainFromNative(current->ai_family);
        out.socket_type = TranslateTypeFromNative(current->ai_socktype);
        out.protocol = TranslateProtocolFromNative(current->ai_protocol);
        out.addr = TranslateToSockAddrIn(*reinterpret_cast<sockaddr_in*>(current->ai_addr),
                                         current->ai_addrlen);
        if (current->ai_canonname != nullptr) {
            out.canon_name = current->ai_canonname;
        }
    }
    freeaddrinfo(addrinfo);
    return ret;
}

std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
    const size_t num = pollfds.size();

    std::vector<WSAPOLLFD> host_pollfds(pollfds.size());
    std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) {
        WSAPOLLFD result;
        result.fd = fd.socket->GetFD();
        result.events = TranslatePollEvents(fd.events);
        result.revents = 0;
        return result;
    });

    host_pollfds.push_back(WSAPOLLFD{
        .fd = GetInterruptSocket(),
        .events = POLLIN,
        .revents = 0,
    });

    const int result =
        WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), timeout);
    if (result == 0) {
        ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(),
                           [](WSAPOLLFD fd) { return fd.revents == 0; }));
        return {0, Errno::SUCCESS};
    }

    for (size_t i = 0; i < num; ++i) {
        pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents);
    }

    if (result > 0) {
        return {result, Errno::SUCCESS};
    }

    ASSERT(result == SOCKET_ERROR);

    return {-1, GetAndLogLastError()};
}

Socket::~Socket() {
    if (fd == INVALID_SOCKET) {
        return;
    }
    (void)closesocket(fd);
    fd = INVALID_SOCKET;
}

Socket::Socket(Socket&& rhs) noexcept {
    fd = std::exchange(rhs.fd, INVALID_SOCKET);
}

template <typename T>
std::pair<T, Errno> Socket::GetSockOpt(SOCKET fd_so, int option) {
    T value{};
    socklen_t len = sizeof(value);
    const int result = getsockopt(fd_so, SOL_SOCKET, option, reinterpret_cast<char*>(&value), &len);
    if (result != SOCKET_ERROR) {
        ASSERT(len == sizeof(value));
        return {value, Errno::SUCCESS};
    }
    return {value, GetAndLogLastError()};
}

template <typename T>
Errno Socket::SetSockOpt(SOCKET fd_so, int option, T value) {
    const int result =
        setsockopt(fd_so, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
    if (result != SOCKET_ERROR) {
        return Errno::SUCCESS;
    }
    return GetAndLogLastError();
}

Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
    fd = socket(TranslateDomainToNative(domain), TranslateTypeToNative(type),
                TranslateProtocolToNative(protocol));
    if (fd != INVALID_SOCKET) {
        return Errno::SUCCESS;
    }

    return GetAndLogLastError();
}

std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
    sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);

    const bool wait_for_accept = !is_non_blocking;
    if (wait_for_accept) {
        std::vector<WSAPOLLFD> host_pollfds{
            WSAPOLLFD{fd, POLLIN, 0},
            WSAPOLLFD{GetInterruptSocket(), POLLIN, 0},
        };

        while (true) {
            const int pollres =
                WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), -1);
            if (host_pollfds[1].revents != 0) {
                // Interrupt signaled before a client could be accepted, break
                return {AcceptResult{}, Errno::AGAIN};
            }
            if (pollres > 0) {
                break;
            }
        }
    }

    const SOCKET new_socket = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen);

    if (new_socket == INVALID_SOCKET) {
        return {AcceptResult{}, GetAndLogLastError()};
    }

    AcceptResult result{
        .socket = std::make_unique<Socket>(new_socket),
        .sockaddr_in = TranslateToSockAddrIn(addr, addrlen),
    };

    return {std::move(result), Errno::SUCCESS};
}

Errno Socket::Connect(SockAddrIn addr_in) {
    const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in);
    if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != SOCKET_ERROR) {
        return Errno::SUCCESS;
    }

    return GetAndLogLastError();
}

std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
    sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);
    if (getpeername(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen) == SOCKET_ERROR) {
        return {SockAddrIn{}, GetAndLogLastError()};
    }

    return {TranslateToSockAddrIn(addr, addrlen), Errno::SUCCESS};
}

std::pair<SockAddrIn, Errno> Socket::GetSockName() {
    sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);
    if (getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen) == SOCKET_ERROR) {
        return {SockAddrIn{}, GetAndLogLastError()};
    }

    return {TranslateToSockAddrIn(addr, addrlen), Errno::SUCCESS};
}

Errno Socket::Bind(SockAddrIn addr) {
    const sockaddr addr_in = TranslateFromSockAddrIn(addr);
    if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) {
        return Errno::SUCCESS;
    }

    return GetAndLogLastError();
}

Errno Socket::Listen(s32 backlog) {
    if (listen(fd, backlog) != SOCKET_ERROR) {
        return Errno::SUCCESS;
    }

    return GetAndLogLastError();
}

Errno Socket::Shutdown(ShutdownHow how) {
    int host_how = 0;
    switch (how) {
    case ShutdownHow::RD:
        host_how = SD_RECEIVE;
        break;
    case ShutdownHow::WR:
        host_how = SD_SEND;
        break;
    case ShutdownHow::RDWR:
        host_how = SD_BOTH;
        break;
    default:
        UNIMPLEMENTED_MSG("Unimplemented flag how={}", how);
        return Errno::SUCCESS;
    }
    if (shutdown(fd, host_how) != SOCKET_ERROR) {
        return Errno::SUCCESS;
    }

    return GetAndLogLastError();
}

std::pair<s32, Errno> Socket::Recv(int flags, std::span<u8> message) {
    ASSERT(flags == 0);
    ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));

    const auto result =
        recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0);
    if (result != SOCKET_ERROR) {
        return {static_cast<s32>(result), Errno::SUCCESS};
    }

    return {-1, GetAndLogLastError()};
}

std::pair<s32, Errno> Socket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
    ASSERT(flags == 0);
    ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));

    sockaddr_in addr_in{};
    socklen_t addrlen = sizeof(addr_in);
    socklen_t* const p_addrlen = addr ? &addrlen : nullptr;
    sockaddr* const p_addr_in = addr ? reinterpret_cast<sockaddr*>(&addr_in) : nullptr;

    const auto result = recvfrom(fd, reinterpret_cast<char*>(message.data()),
                                 static_cast<int>(message.size()), 0, p_addr_in, p_addrlen);
    if (result != SOCKET_ERROR) {
        if (addr) {
            *addr = TranslateToSockAddrIn(addr_in, addrlen);
        }
        return {static_cast<s32>(result), Errno::SUCCESS};
    }

    return {-1, GetAndLogLastError()};
}

std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) {
    ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
    ASSERT(flags == 0);

    int native_flags = 0;
#if YUZU_UNIX
    native_flags |= MSG_NOSIGNAL; // do not send us SIGPIPE
#endif
    const auto result = send(fd, reinterpret_cast<const char*>(message.data()),
                             static_cast<int>(message.size()), native_flags);
    if (result != SOCKET_ERROR) {
        return {static_cast<s32>(result), Errno::SUCCESS};
    }

    return {-1, GetAndLogLastError(CallType::Send)};
}

std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
                                     const SockAddrIn* addr) {
    ASSERT(flags == 0);

    const sockaddr* to = nullptr;
    const int to_len = addr ? sizeof(sockaddr) : 0;
    sockaddr host_addr_in;

    if (addr) {
        host_addr_in = TranslateFromSockAddrIn(*addr);
        to = &host_addr_in;
    }

    const auto result = sendto(fd, reinterpret_cast<const char*>(message.data()),
                               static_cast<int>(message.size()), 0, to, to_len);
    if (result != SOCKET_ERROR) {
        return {static_cast<s32>(result), Errno::SUCCESS};
    }

    return {-1, GetAndLogLastError(CallType::Send)};
}

Errno Socket::Close() {
    [[maybe_unused]] const int result = closesocket(fd);
    ASSERT(result == 0);
    fd = INVALID_SOCKET;

    return Errno::SUCCESS;
}

std::pair<Errno, Errno> Socket::GetPendingError() {
    auto [pending_err, getsockopt_err] = GetSockOpt<int>(fd, SO_ERROR);
    return {TranslateNativeError(pending_err), getsockopt_err};
}

Errno Socket::SetLinger(bool enable, u32 linger) {
    return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger));
}

Errno Socket::SetReuseAddr(bool enable) {
    return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
}

Errno Socket::SetKeepAlive(bool enable) {
    return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0);
}

Errno Socket::SetBroadcast(bool enable) {
    return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
}

Errno Socket::SetSndBuf(u32 value) {
    return SetSockOpt(fd, SO_SNDBUF, value);
}

Errno Socket::SetRcvBuf(u32 value) {
    return SetSockOpt(fd, SO_RCVBUF, value);
}

Errno Socket::SetSndTimeo(u32 value) {
    return SetSockOpt(fd, SO_SNDTIMEO, value);
}

Errno Socket::SetRcvTimeo(u32 value) {
    return SetSockOpt(fd, SO_RCVTIMEO, value);
}

Errno Socket::SetNonBlock(bool enable) {
    if (EnableNonBlock(fd, enable)) {
        is_non_blocking = enable;
        return Errno::SUCCESS;
    }
    return GetAndLogLastError();
}

bool Socket::IsOpened() const {
    return fd != INVALID_SOCKET;
}

void Socket::HandleProxyPacket(const ProxyPacket& packet) {
    LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!");
}

} // namespace Network