2015-05-24 04:55:12 +00:00
|
|
|
// Copyright 2010 Dolphin Emulator Project
|
2021-07-05 01:22:19 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2014-02-10 18:54:46 +00:00
|
|
|
|
2017-11-09 20:14:21 +00:00
|
|
|
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
|
|
|
|
2017-11-10 20:29:25 +00:00
|
|
|
#include <algorithm>
|
2016-06-25 19:46:39 +00:00
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
#include "Common/Assert.h"
|
2017-11-09 20:14:21 +00:00
|
|
|
#include "Common/Logging/Log.h"
|
2020-01-22 00:50:05 +00:00
|
|
|
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
2010-04-02 02:48:24 +00:00
|
|
|
|
2017-11-10 17:56:13 +00:00
|
|
|
#ifdef CIFACE_USE_WIN32
|
|
|
|
#include "InputCommon/ControllerInterface/Win32/Win32.h"
|
2010-04-02 02:48:24 +00:00
|
|
|
#endif
|
|
|
|
#ifdef CIFACE_USE_XLIB
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "InputCommon/ControllerInterface/Xlib/XInput2.h"
|
2013-07-10 06:49:58 +00:00
|
|
|
#endif
|
2010-04-02 09:41:43 +00:00
|
|
|
#ifdef CIFACE_USE_OSX
|
2016-08-08 16:38:22 +00:00
|
|
|
#include "InputCommon/ControllerInterface/Quartz/Quartz.h"
|
2010-04-02 09:41:43 +00:00
|
|
|
#endif
|
2010-04-02 02:48:24 +00:00
|
|
|
#ifdef CIFACE_USE_SDL
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "InputCommon/ControllerInterface/SDL/SDL.h"
|
2010-04-02 02:48:24 +00:00
|
|
|
#endif
|
2013-04-15 04:02:53 +00:00
|
|
|
#ifdef CIFACE_USE_ANDROID
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "InputCommon/ControllerInterface/Android/Android.h"
|
2013-04-15 04:02:53 +00:00
|
|
|
#endif
|
2015-06-29 00:17:35 +00:00
|
|
|
#ifdef CIFACE_USE_EVDEV
|
|
|
|
#include "InputCommon/ControllerInterface/evdev/evdev.h"
|
|
|
|
#endif
|
2015-10-25 03:20:03 +00:00
|
|
|
#ifdef CIFACE_USE_PIPES
|
|
|
|
#include "InputCommon/ControllerInterface/Pipes/Pipes.h"
|
|
|
|
#endif
|
2019-10-26 16:05:16 +00:00
|
|
|
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
|
|
|
#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h"
|
2019-09-06 15:09:30 +00:00
|
|
|
#endif
|
2023-04-19 23:49:50 +00:00
|
|
|
#ifdef CIFACE_USE_STEAMDECK
|
|
|
|
#include "InputCommon/ControllerInterface/SteamDeck/SteamDeck.h"
|
|
|
|
#endif
|
2010-04-02 02:48:24 +00:00
|
|
|
|
2010-10-12 19:42:29 +00:00
|
|
|
ControllerInterface g_controller_interface;
|
|
|
|
|
2021-05-15 08:32:00 +00:00
|
|
|
// We need to save which input channel we are in by thread, so we can access the correct input
|
|
|
|
// update values in different threads by input channel. We start from InputChannel::Host on all
|
|
|
|
// threads as hotkeys are updated from a worker thread, but UI can read from the main thread. This
|
|
|
|
// will never interfere with game threads.
|
2020-04-21 04:16:07 +00:00
|
|
|
static thread_local ciface::InputChannel tls_input_channel = ciface::InputChannel::Host;
|
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
static thread_local bool tls_is_updating_devices = false;
|
|
|
|
|
2018-10-03 07:34:27 +00:00
|
|
|
void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2010-10-12 19:42:29 +00:00
|
|
|
if (m_is_init)
|
2010-04-02 02:48:24 +00:00
|
|
|
return;
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
std::lock_guard lk_population(m_devices_population_mutex);
|
|
|
|
|
2018-10-03 07:34:27 +00:00
|
|
|
m_wsi = wsi;
|
2019-01-02 14:19:42 +00:00
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
m_populating_devices_counter = 1;
|
2019-01-02 14:19:42 +00:00
|
|
|
|
2017-11-10 17:56:13 +00:00
|
|
|
#ifdef CIFACE_USE_WIN32
|
2024-03-11 07:09:31 +00:00
|
|
|
m_input_backends.emplace_back(ciface::Win32::CreateInputBackend(this));
|
2010-06-12 12:57:28 +00:00
|
|
|
#endif
|
2010-04-02 02:48:24 +00:00
|
|
|
#ifdef CIFACE_USE_XLIB
|
2024-03-11 06:42:32 +00:00
|
|
|
m_input_backends.emplace_back(ciface::XInput2::CreateInputBackend(this));
|
2013-07-10 06:49:58 +00:00
|
|
|
#endif
|
2010-04-02 09:41:43 +00:00
|
|
|
#ifdef CIFACE_USE_OSX
|
2024-03-11 06:31:30 +00:00
|
|
|
m_input_backends.emplace_back(ciface::Quartz::CreateInputBackend(this));
|
2010-04-02 09:41:43 +00:00
|
|
|
#endif
|
2010-04-02 02:48:24 +00:00
|
|
|
#ifdef CIFACE_USE_SDL
|
2022-10-22 21:13:35 +00:00
|
|
|
m_input_backends.emplace_back(ciface::SDL::CreateInputBackend(this));
|
2010-04-02 02:48:24 +00:00
|
|
|
#endif
|
2013-04-15 04:02:53 +00:00
|
|
|
#ifdef CIFACE_USE_ANDROID
|
2024-03-11 07:36:07 +00:00
|
|
|
m_input_backends.emplace_back(ciface::Android::CreateInputBackend(this));
|
2013-04-15 04:02:53 +00:00
|
|
|
#endif
|
2015-06-29 00:17:35 +00:00
|
|
|
#ifdef CIFACE_USE_EVDEV
|
2022-10-22 22:10:38 +00:00
|
|
|
m_input_backends.emplace_back(ciface::evdev::CreateInputBackend(this));
|
2015-06-29 00:17:35 +00:00
|
|
|
#endif
|
2015-10-25 03:20:03 +00:00
|
|
|
#ifdef CIFACE_USE_PIPES
|
2024-03-11 06:46:33 +00:00
|
|
|
m_input_backends.emplace_back(ciface::Pipes::CreateInputBackend(this));
|
2019-09-06 15:09:30 +00:00
|
|
|
#endif
|
2019-10-26 16:05:16 +00:00
|
|
|
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
2022-10-22 22:33:21 +00:00
|
|
|
m_input_backends.emplace_back(ciface::DualShockUDPClient::CreateInputBackend(this));
|
2015-10-25 03:20:03 +00:00
|
|
|
#endif
|
2023-04-19 23:49:50 +00:00
|
|
|
#ifdef CIFACE_USE_STEAMDECK
|
|
|
|
m_input_backends.emplace_back(ciface::SteamDeck::CreateInputBackend(this));
|
|
|
|
#endif
|
2010-04-02 02:48:24 +00:00
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
// Don't allow backends to add devices before the first RefreshDevices() as they will be cleaned
|
|
|
|
// there. Or they'd end up waiting on the devices mutex if populated from another thread.
|
|
|
|
m_is_init = true;
|
|
|
|
|
2016-10-16 20:39:05 +00:00
|
|
|
RefreshDevices();
|
2021-05-15 09:06:12 +00:00
|
|
|
|
2021-11-17 20:55:02 +00:00
|
|
|
// Devices writes are already protected by m_devices_population_mutex but this won't hurt
|
|
|
|
m_devices_mutex.lock();
|
2021-05-15 09:06:12 +00:00
|
|
|
const bool devices_empty = m_devices.empty();
|
|
|
|
m_devices_mutex.unlock();
|
|
|
|
|
|
|
|
if (m_populating_devices_counter.fetch_sub(1) == 1 && !devices_empty)
|
|
|
|
InvokeDevicesChangedCallbacks();
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
void ControllerInterface::ChangeWindow(void* hwnd, WindowChangeReason reason)
|
2018-10-27 10:06:35 +00:00
|
|
|
{
|
|
|
|
if (!m_is_init)
|
|
|
|
return;
|
|
|
|
|
2020-03-11 13:09:28 +00:00
|
|
|
// This shouldn't use render_surface so no need to update it.
|
|
|
|
m_wsi.render_window = hwnd;
|
2021-05-15 09:06:12 +00:00
|
|
|
|
|
|
|
// No need to re-add devices if this is an application exit request
|
|
|
|
if (reason == WindowChangeReason::Exit)
|
|
|
|
ClearDevices();
|
|
|
|
else
|
|
|
|
RefreshDevices(RefreshReason::WindowChangeOnly);
|
2018-10-27 10:06:35 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
void ControllerInterface::RefreshDevices(RefreshReason reason)
|
2014-10-13 16:45:47 +00:00
|
|
|
{
|
|
|
|
if (!m_is_init)
|
|
|
|
return;
|
|
|
|
|
2021-11-17 20:55:02 +00:00
|
|
|
// We lock m_devices_population_mutex here to make everything simpler.
|
|
|
|
// Multiple devices classes have their own "hotplug" thread, and can add/remove devices at any
|
|
|
|
// time, while actual writes to "m_devices" are safe, the order in which they happen is not. That
|
2023-05-24 19:58:30 +00:00
|
|
|
// means a thread could be adding devices while we are removing them from a different thread,
|
|
|
|
// or removing them as we are populating them (causing missing or duplicate devices).
|
2021-05-15 09:06:12 +00:00
|
|
|
std::lock_guard lk_population(m_devices_population_mutex);
|
2016-10-16 20:39:05 +00:00
|
|
|
|
2021-05-15 09:20:20 +00:00
|
|
|
// If only the window changed, avoid removing and re-adding all devices.
|
|
|
|
// Instead only refresh devices that require the window handle.
|
|
|
|
if (reason == RefreshReason::WindowChangeOnly)
|
|
|
|
{
|
|
|
|
m_populating_devices_counter.fetch_add(1);
|
|
|
|
|
2024-03-11 07:09:31 +00:00
|
|
|
for (auto& backend : m_input_backends)
|
|
|
|
backend->HandleWindowChange();
|
2021-05-15 09:20:20 +00:00
|
|
|
|
|
|
|
if (m_populating_devices_counter.fetch_sub(1) == 1)
|
|
|
|
InvokeDevicesChangedCallbacks();
|
2024-03-11 07:28:18 +00:00
|
|
|
|
2021-05-15 09:20:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
m_populating_devices_counter.fetch_add(1);
|
|
|
|
|
2019-03-09 15:57:37 +00:00
|
|
|
// Make sure shared_ptr<Device> objects are released before repopulating.
|
2021-05-15 09:06:12 +00:00
|
|
|
ClearDevices();
|
|
|
|
|
|
|
|
// Some of these calls won't immediately populate devices, but will do it async
|
|
|
|
// with their own PlatformPopulateDevices().
|
|
|
|
// This means that devices might end up in different order, unless we override their priority.
|
|
|
|
// It also means they might appear as "disconnected" in the Qt UI for a tiny bit of time.
|
InputCommon: fix default input config default device not being loaded/found
Fixes bug: https://bugs.dolphin-emu.org/issues/12744
Before https://github.com/dolphin-emu/dolphin/commit/e1e3db13baabefa89991388d37db0bb260c4f535
the ControllerInterface m_devices_mutex was "wrongfully" locked for the whole Initialize() call, which included the first device population refresh,
this has the unwanted (accidental) consequence of often preventing the different pads (GC Pad, Wii Contollers, ...) input configs from loading
until that mutex was released (the input config defaults loading was blocked in EmulatedController::LoadDefaults()), which meant that the devices
population would often have the time to finish adding its first device, which would then be selected as default device (by design, the first device
added to the CI is the default default device, usually the "Keyboard and Mouse" device).
After the commit mentioned above removed the unnecessary m_devices_mutex calls, the default default device would fail to load (be found)
causing the default input mappings, which are specifically written for the default default device on every platform, to not be bound to any
physical device input, breaking input on new dolphin installations (until a user tried to customize the default device manually).
Default devices are now always added synchronously to avoid the problem, and so they should in the future (I added comments and warnings to help with that)
2021-11-27 12:31:04 +00:00
|
|
|
// This helps the emulation and host thread to not stall when repopulating devices for any reason.
|
|
|
|
// Every platform that adds a device that is meant to be used as default device should try to not
|
|
|
|
// do it async, to not risk the emulated controllers default config loading not finding a default
|
|
|
|
// device.
|
2019-03-09 15:57:37 +00:00
|
|
|
|
2022-10-22 21:13:35 +00:00
|
|
|
for (auto& backend : m_input_backends)
|
|
|
|
backend->PopulateDevices();
|
|
|
|
|
2021-05-15 09:10:00 +00:00
|
|
|
WiimoteReal::PopulateDevices();
|
2017-11-04 14:37:03 +00:00
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
if (m_populating_devices_counter.fetch_sub(1) == 1)
|
|
|
|
InvokeDevicesChangedCallbacks();
|
2014-10-13 16:45:47 +00:00
|
|
|
}
|
|
|
|
|
2020-12-13 00:30:27 +00:00
|
|
|
void ControllerInterface::PlatformPopulateDevices(std::function<void()> callback)
|
|
|
|
{
|
|
|
|
if (!m_is_init)
|
|
|
|
return;
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
std::lock_guard lk_population(m_devices_population_mutex);
|
2020-12-13 00:30:27 +00:00
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
m_populating_devices_counter.fetch_add(1);
|
2020-12-13 00:30:27 +00:00
|
|
|
|
2021-11-17 20:55:02 +00:00
|
|
|
callback();
|
2021-05-15 09:06:12 +00:00
|
|
|
|
|
|
|
if (m_populating_devices_counter.fetch_sub(1) == 1)
|
|
|
|
InvokeDevicesChangedCallbacks();
|
2020-12-13 00:30:27 +00:00
|
|
|
}
|
|
|
|
|
2019-01-02 14:19:42 +00:00
|
|
|
// Remove all devices and call library cleanup functions
|
2010-10-12 19:42:29 +00:00
|
|
|
void ControllerInterface::Shutdown()
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2014-01-31 00:51:21 +00:00
|
|
|
if (!m_is_init)
|
2010-04-02 02:48:24 +00:00
|
|
|
return;
|
|
|
|
|
2019-01-02 14:19:42 +00:00
|
|
|
// Prevent additional devices from being added during shutdown.
|
|
|
|
m_is_init = false;
|
2021-05-15 09:06:12 +00:00
|
|
|
// Additional safety measure to avoid InvokeDevicesChangedCallbacks()
|
|
|
|
m_populating_devices_counter = 1;
|
2019-01-02 14:19:42 +00:00
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
// Update control references so shared_ptr<Device>s are freed up BEFORE we shutdown the backends.
|
|
|
|
ClearDevices();
|
2019-01-02 14:19:42 +00:00
|
|
|
|
2022-10-22 22:10:38 +00:00
|
|
|
// Empty the container of input backends to deconstruct and deinitialize them.
|
2022-10-22 21:13:35 +00:00
|
|
|
m_input_backends.clear();
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
// Make sure no devices had been added within Shutdown() in the time
|
|
|
|
// between checking they checked atomic m_is_init bool and we changed it.
|
2021-11-17 20:55:02 +00:00
|
|
|
// We couldn't have locked m_devices_population_mutex for the whole Shutdown()
|
|
|
|
// as it could cause deadlocks. Note that this is still not 100% safe as some backends are
|
2021-05-15 09:06:12 +00:00
|
|
|
// shut down in other places, possibly adding devices after we have shut down, but the chances of
|
|
|
|
// that happening are basically zero.
|
|
|
|
ClearDevices();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ControllerInterface::ClearDevices()
|
|
|
|
{
|
|
|
|
std::lock_guard lk_population(m_devices_population_mutex);
|
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard lk(m_devices_mutex);
|
|
|
|
|
|
|
|
if (m_devices.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (const auto& d : m_devices)
|
|
|
|
{
|
|
|
|
// Set outputs to ZERO before destroying device
|
|
|
|
for (ciface::Core::Device::Output* o : d->Outputs())
|
|
|
|
o->SetState(0);
|
|
|
|
}
|
|
|
|
|
2021-11-17 20:55:02 +00:00
|
|
|
// Devices could still be alive after this as there might be shared ptrs around holding them.
|
|
|
|
// The InvokeDevicesChangedCallbacks() underneath should always clean all of them (it needs to).
|
2021-05-15 09:06:12 +00:00
|
|
|
m_devices.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
InvokeDevicesChangedCallbacks();
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
bool ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device)
|
2016-06-12 15:08:04 +00:00
|
|
|
{
|
2019-01-02 14:19:42 +00:00
|
|
|
// If we are shutdown (or in process of shutting down) ignore this request:
|
|
|
|
if (!m_is_init)
|
2021-05-15 09:06:12 +00:00
|
|
|
return false;
|
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
ASSERT_MSG(CONTROLLERINTERFACE, !tls_is_updating_devices,
|
|
|
|
"Devices shouldn't be added within input update calls, there is a risk of deadlock "
|
|
|
|
"if another thread was already here");
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
std::lock_guard lk_population(m_devices_population_mutex);
|
2019-01-02 14:19:42 +00:00
|
|
|
|
2016-07-14 08:25:52 +00:00
|
|
|
{
|
2020-01-22 00:52:58 +00:00
|
|
|
std::lock_guard lk(m_devices_mutex);
|
2019-03-09 15:57:37 +00:00
|
|
|
|
|
|
|
const auto is_id_in_use = [&device, this](int id) {
|
|
|
|
return std::any_of(m_devices.begin(), m_devices.end(), [&device, &id](const auto& d) {
|
|
|
|
return d->GetSource() == device->GetSource() && d->GetName() == device->GetName() &&
|
|
|
|
d->GetId() == id;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto preferred_id = device->GetPreferredId();
|
|
|
|
if (preferred_id.has_value() && !is_id_in_use(*preferred_id))
|
2017-11-09 20:14:21 +00:00
|
|
|
{
|
2019-03-09 15:57:37 +00:00
|
|
|
// Use the device's preferred ID if available.
|
|
|
|
device->SetId(*preferred_id);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Find the first available ID to use.
|
|
|
|
int id = 0;
|
|
|
|
while (is_id_in_use(id))
|
|
|
|
++id;
|
|
|
|
|
|
|
|
device->SetId(id);
|
2017-11-09 20:14:21 +00:00
|
|
|
}
|
|
|
|
|
2021-05-04 20:37:06 +00:00
|
|
|
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "Added device: {}", device->GetQualifiedName());
|
2017-11-09 20:14:21 +00:00
|
|
|
m_devices.emplace_back(std::move(device));
|
2021-05-15 09:14:11 +00:00
|
|
|
|
|
|
|
// We can't (and don't want) to control the order in which devices are added, but we
|
|
|
|
// need their order to be consistent, and we need the same one to always be the first, where
|
|
|
|
// present (the keyboard and mouse device usually). This is because when defaulting a
|
|
|
|
// controller profile, it will automatically select the first device in the list as its default.
|
|
|
|
std::stable_sort(m_devices.begin(), m_devices.end(),
|
|
|
|
[](const std::shared_ptr<ciface::Core::Device>& a,
|
|
|
|
const std::shared_ptr<ciface::Core::Device>& b) {
|
|
|
|
// It would be nice to sort devices by Source then Name then ID but it's
|
|
|
|
// better to leave them sorted by the add order, which also avoids breaking
|
|
|
|
// the order on other platforms that are less tested.
|
|
|
|
return a->GetSortPriority() > b->GetSortPriority();
|
|
|
|
});
|
2016-07-14 08:25:52 +00:00
|
|
|
}
|
2017-11-04 14:37:03 +00:00
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
if (!m_populating_devices_counter)
|
2017-11-04 14:37:03 +00:00
|
|
|
InvokeDevicesChangedCallbacks();
|
2021-05-15 09:06:12 +00:00
|
|
|
return true;
|
2016-06-12 15:08:04 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
void ControllerInterface::RemoveDevice(std::function<bool(const ciface::Core::Device*)> callback,
|
|
|
|
bool force_devices_release)
|
2016-07-14 15:45:59 +00:00
|
|
|
{
|
2021-05-15 09:06:12 +00:00
|
|
|
// If we are shutdown (or in process of shutting down) ignore this request:
|
|
|
|
if (!m_is_init)
|
|
|
|
return;
|
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
ASSERT_MSG(CONTROLLERINTERFACE, !tls_is_updating_devices,
|
|
|
|
"Devices shouldn't be removed within input update calls, there is a risk of deadlock "
|
|
|
|
"if another thread was already here");
|
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
std::lock_guard lk_population(m_devices_population_mutex);
|
|
|
|
|
|
|
|
bool any_removed;
|
2017-11-09 20:14:21 +00:00
|
|
|
{
|
2020-01-22 00:52:58 +00:00
|
|
|
std::lock_guard lk(m_devices_mutex);
|
2024-07-21 15:26:35 +00:00
|
|
|
const size_t erased = std::erase_if(m_devices, [&callback](const auto& dev) {
|
2017-11-09 20:14:21 +00:00
|
|
|
if (callback(dev.get()))
|
|
|
|
{
|
2021-05-04 20:37:06 +00:00
|
|
|
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "Removed device: {}", dev->GetQualifiedName());
|
2017-11-09 20:14:21 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
2024-07-21 15:26:35 +00:00
|
|
|
any_removed = erased != 0;
|
2017-11-09 20:14:21 +00:00
|
|
|
}
|
2017-11-04 14:37:03 +00:00
|
|
|
|
2021-05-15 09:06:12 +00:00
|
|
|
if (any_removed && (!m_populating_devices_counter || force_devices_release))
|
2017-11-04 14:37:03 +00:00
|
|
|
InvokeDevicesChangedCallbacks();
|
2016-07-14 15:45:59 +00:00
|
|
|
}
|
|
|
|
|
2019-01-02 14:19:42 +00:00
|
|
|
// Update input for all devices if lock can be acquired without waiting.
|
2014-11-13 08:55:14 +00:00
|
|
|
void ControllerInterface::UpdateInput()
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2021-05-15 09:06:12 +00:00
|
|
|
// This should never happen
|
|
|
|
ASSERT(m_is_init);
|
|
|
|
if (!m_is_init)
|
|
|
|
return;
|
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
// We add the devices to remove while we still have the "m_devices_mutex" locked.
|
|
|
|
// This guarantees that:
|
|
|
|
// -We won't try to lock "m_devices_population_mutex" while it was already locked and waiting
|
|
|
|
// for "m_devices_mutex", which would result in dead lock.
|
|
|
|
// -We don't keep shared ptrs on devices and thus unwillingly keep them alive even if somebody
|
|
|
|
// is currently trying to remove them (and needs them destroyed on the spot).
|
|
|
|
// -If somebody else destroyed them in the meantime, we'll know which ones have been destroyed.
|
|
|
|
std::vector<std::weak_ptr<ciface::Core::Device>> devices_to_remove;
|
2023-03-09 00:58:55 +00:00
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
{
|
|
|
|
// TODO: if we are an emulation input channel, we should probably always lock.
|
|
|
|
// Prefer outdated values over blocking UI or CPU thread (this avoids short but noticeable frame
|
|
|
|
// drops)
|
|
|
|
if (!m_devices_mutex.try_lock())
|
|
|
|
return;
|
2023-03-09 00:58:55 +00:00
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
std::lock_guard lk_devices(m_devices_mutex, std::adopt_lock);
|
2023-03-09 00:58:55 +00:00
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
tls_is_updating_devices = true;
|
2022-10-22 21:13:35 +00:00
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
for (auto& backend : m_input_backends)
|
|
|
|
backend->UpdateInput(devices_to_remove);
|
2022-10-22 21:13:35 +00:00
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
for (const auto& d : m_devices)
|
|
|
|
{
|
|
|
|
// Theoretically we could avoid updating input on devices that don't have any references to
|
|
|
|
// them, but in practice a few devices types could break in different ways, so we don't
|
|
|
|
if (d->UpdateInput() == ciface::Core::DeviceRemoval::Remove)
|
|
|
|
devices_to_remove.push_back(d);
|
|
|
|
}
|
2022-10-22 21:13:35 +00:00
|
|
|
|
2023-05-24 19:58:30 +00:00
|
|
|
tls_is_updating_devices = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (devices_to_remove.size() > 0)
|
2016-07-15 09:34:18 +00:00
|
|
|
{
|
2023-05-24 19:58:30 +00:00
|
|
|
RemoveDevice([&](const ciface::Core::Device* device) {
|
|
|
|
return std::any_of(devices_to_remove.begin(), devices_to_remove.end(),
|
|
|
|
[device](const std::weak_ptr<ciface::Core::Device>& d) {
|
|
|
|
return d.lock().get() == device;
|
|
|
|
});
|
|
|
|
});
|
2016-07-15 09:34:18 +00:00
|
|
|
}
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 04:16:07 +00:00
|
|
|
void ControllerInterface::SetCurrentInputChannel(ciface::InputChannel input_channel)
|
|
|
|
{
|
|
|
|
tls_input_channel = input_channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
ciface::InputChannel ControllerInterface::GetCurrentInputChannel()
|
|
|
|
{
|
|
|
|
return tls_input_channel;
|
|
|
|
}
|
|
|
|
|
2024-03-11 06:31:05 +00:00
|
|
|
WindowSystemInfo ControllerInterface::GetWindowSystemInfo() const
|
|
|
|
{
|
|
|
|
return m_wsi;
|
|
|
|
}
|
|
|
|
|
2020-01-24 06:06:39 +00:00
|
|
|
void ControllerInterface::SetAspectRatioAdjustment(float value)
|
|
|
|
{
|
|
|
|
m_aspect_ratio_adjustment = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Vec2 ControllerInterface::GetWindowInputScale() const
|
|
|
|
{
|
|
|
|
const auto ar = m_aspect_ratio_adjustment.load();
|
|
|
|
|
|
|
|
if (ar > 1)
|
|
|
|
return {1.f, ar};
|
|
|
|
else
|
|
|
|
return {1 / ar, 1.f};
|
|
|
|
}
|
|
|
|
|
2022-07-15 18:31:44 +00:00
|
|
|
void ControllerInterface::SetMouseCenteringRequested(bool center)
|
|
|
|
{
|
|
|
|
m_requested_mouse_centering = center;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ControllerInterface::IsMouseCenteringRequested() const
|
|
|
|
{
|
|
|
|
return m_requested_mouse_centering.load();
|
|
|
|
}
|
|
|
|
|
2017-11-04 14:36:30 +00:00
|
|
|
// Register a callback to be called when a device is added or removed (as from the input backends'
|
|
|
|
// hotplug thread), or when devices are refreshed
|
2019-01-10 15:02:38 +00:00
|
|
|
// Returns a handle for later removing the callback.
|
|
|
|
ControllerInterface::HotplugCallbackHandle
|
|
|
|
ControllerInterface::RegisterDevicesChangedCallback(std::function<void()> callback)
|
2016-06-13 09:11:47 +00:00
|
|
|
{
|
2021-05-15 09:06:12 +00:00
|
|
|
std::lock_guard lk(m_callbacks_mutex);
|
2017-11-04 14:36:30 +00:00
|
|
|
m_devices_changed_callbacks.emplace_back(std::move(callback));
|
2019-01-10 15:02:38 +00:00
|
|
|
return std::prev(m_devices_changed_callbacks.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unregister a device callback.
|
|
|
|
void ControllerInterface::UnregisterDevicesChangedCallback(const HotplugCallbackHandle& handle)
|
|
|
|
{
|
2021-05-15 09:06:12 +00:00
|
|
|
std::lock_guard lk(m_callbacks_mutex);
|
2019-01-10 15:02:38 +00:00
|
|
|
m_devices_changed_callbacks.erase(handle);
|
2016-06-13 09:11:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke all callbacks that were registered
|
2017-11-04 14:36:30 +00:00
|
|
|
void ControllerInterface::InvokeDevicesChangedCallbacks() const
|
2016-06-13 09:11:47 +00:00
|
|
|
{
|
2021-05-15 08:25:20 +00:00
|
|
|
m_callbacks_mutex.lock();
|
|
|
|
const auto devices_changed_callbacks = m_devices_changed_callbacks;
|
|
|
|
m_callbacks_mutex.unlock();
|
|
|
|
for (const auto& callback : devices_changed_callbacks)
|
2016-06-13 09:11:47 +00:00
|
|
|
callback();
|
|
|
|
}
|