File indexing completed on 2024-05-12 05:35:40

0001 /*
0002     SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
0003     SPDX-FileCopyrightText: 2023 Jeremy Whiting <jpwhiting@kde.org>
0004     SPDX-FileCopyrightText: 2023 Niccolò Venerandi <niccolo@venerandi.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "devicemodel.h"
0010 
0011 #include <KLocalizedString>
0012 #include <QTimer>
0013 
0014 #include <SDL2/SDL.h>
0015 #include <SDL2/SDL_joystick.h>
0016 
0017 #include "gamepad.h"
0018 #include "logging.h"
0019 
0020 DeviceModel::DeviceModel()
0021 {
0022     auto timer = new QTimer(this);
0023     connect(timer, &QTimer::timeout, this, &DeviceModel::poll);
0024     timer->start(1);
0025 }
0026 
0027 Gamepad *DeviceModel::device(int index) const
0028 {
0029     if (index < 0 || index >= m_devices.count())
0030         return nullptr;
0031 
0032     const int sdlIndex = m_devices.keys().at(index);
0033     return m_devices.value(sdlIndex);
0034 }
0035 
0036 int DeviceModel::rowCount(const QModelIndex &parent) const
0037 {
0038     Q_UNUSED(parent)
0039     return m_devices.count();
0040 }
0041 
0042 QVariant DeviceModel::data(const QModelIndex &index, int role) const
0043 {
0044     if (!checkIndex(index)) {
0045         return {};
0046     }
0047 
0048     if (role == Qt::DisplayRole) {
0049         const int sdlIndex = m_devices.keys().at(index.row());
0050 
0051         return i18nc("Device name and path", "%1 (%2)", m_devices.value(sdlIndex)->name(), m_devices.value(sdlIndex)->path());
0052     }
0053 
0054     return {};
0055 }
0056 
0057 void DeviceModel::poll()
0058 {
0059     SDL_Event event{};
0060     while (SDL_PollEvent(&event)) {
0061         switch (event.type) {
0062         case SDL_CONTROLLERDEVICEADDED:
0063             addDevice(event.cdevice.which);
0064             break;
0065         case SDL_CONTROLLERDEVICEREMOVED:
0066             removeDevice(event.cdevice.which);
0067             break;
0068         case SDL_CONTROLLERBUTTONDOWN:
0069         case SDL_CONTROLLERBUTTONUP:
0070             m_devices.value(event.cbutton.which)->onButtonEvent(event.cbutton);
0071             break;
0072         case SDL_CONTROLLERAXISMOTION:
0073             m_devices.value(event.caxis.which)->onAxisEvent(event.caxis);
0074             break;
0075         }
0076     }
0077 }
0078 
0079 void DeviceModel::addDevice(const int deviceIndex)
0080 {
0081     const auto joystick = SDL_JoystickOpen(deviceIndex);
0082     const auto id = SDL_JoystickInstanceID(joystick);
0083 
0084     if (m_devices.contains(id)) {
0085         qCWarning(KCM_GAMECONTROLLER) << "Got a duplicate add event, ignoring. Index: " << deviceIndex;
0086         return;
0087     }
0088 
0089     const auto gamepad = SDL_GameControllerOpen(deviceIndex);
0090     if (SDL_GameControllerTypeForIndex(deviceIndex) == SDL_CONTROLLER_TYPE_VIRTUAL) {
0091         qCWarning(KCM_GAMECONTROLLER) << "Skipping gamepad since it is virtual. Index: " << deviceIndex;
0092         return;
0093     }
0094 
0095     beginInsertRows(QModelIndex(), m_devices.count(), m_devices.count());
0096     m_devices.insert(id, new Gamepad(joystick, gamepad, this));
0097     endInsertRows();
0098 
0099     Q_EMIT devicesChanged();
0100 }
0101 
0102 void DeviceModel::removeDevice(const int deviceIndex)
0103 {
0104     if (!m_devices.contains(deviceIndex)) {
0105         qCWarning(KCM_GAMECONTROLLER) << "Invalid device index from removal event, ignoring";
0106         return;
0107     }
0108 
0109     const int index = m_devices.keys().indexOf(deviceIndex);
0110 
0111     beginRemoveRows(QModelIndex(), index, index);
0112     m_devices.value(deviceIndex)->deleteLater();
0113     m_devices.remove(deviceIndex);
0114     endRemoveRows();
0115 
0116     Q_EMIT devicesChanged();
0117 }
0118 
0119 int DeviceModel::count() const
0120 {
0121     return m_devices.size();
0122 }
0123 
0124 #include "moc_devicemodel.cpp"