/* Copyright 2024 flyinghead This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ #include "dreamlink.h" #ifdef USE_DREAMCASTCONTROLLER #include "dreamconn.h" #include "dreampicoport.h" #include "hw/maple/maple_devs.h" #include "ui/gui.h" #include #include #include #include #include #include #include #include #include #include #include #if defined(__linux__) || (defined(__APPLE__) && defined(TARGET_OS_MAC)) #include #endif #if defined(_WIN32) #include #include #endif void createDreamLinkDevices(std::shared_ptr dreamlink, bool gameStart, bool stateLoaded); void tearDownDreamLinkDevices(std::shared_ptr dreamlink); bool DreamLinkGamepad::isDreamcastController(int deviceIndex) { char guid_str[33] {}; SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(deviceIndex), guid_str, sizeof(guid_str)); NOTICE_LOG(INPUT, "GUID: %s VID:%c%c%c%c PID:%c%c%c%c", guid_str, guid_str[10], guid_str[11], guid_str[8], guid_str[9], guid_str[18], guid_str[19], guid_str[16], guid_str[17]); // DreamConn VID:4457 PID:4443 // Dreamcast Controller USB VID:1209 PID:2f07 const char* pid_vid_guid_str = guid_str + 8; if (memcmp(DreamConn::VID_PID_GUID, pid_vid_guid_str, 16) == 0 || memcmp(DreamPicoPort::VID_PID_GUID, pid_vid_guid_str, 16) == 0) { NOTICE_LOG(INPUT, "Dreamcast controller found!"); return true; } return false; } DreamLinkGamepad::DreamLinkGamepad(int maple_port, int joystick_idx, SDL_Joystick* sdl_joystick) : SDLGamepad(maple_port, joystick_idx, sdl_joystick) { char guid_str[33] {}; SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(joystick_idx), guid_str, sizeof(guid_str)); device_guid = guid_str; // DreamConn VID:4457 PID:4443 // Dreamcast Controller USB VID:1209 PID:2f07 if (memcmp(DreamConn::VID_PID_GUID, guid_str + 8, 16) == 0) { dreamlink = std::make_shared(maple_port); } else if (memcmp(DreamPicoPort::VID_PID_GUID, guid_str + 8, 16) == 0) { dreamlink = std::make_shared(maple_port, joystick_idx, sdl_joystick); } if (dreamlink) { _name = dreamlink->getName(); int defaultBus = dreamlink->getDefaultBus(); if (defaultBus >= 0 && defaultBus < 4) { set_maple_port(defaultBus); } std::string uniqueId = dreamlink->getUniqueId(); if (!uniqueId.empty()) { this->_unique_id = uniqueId; } } EventManager::listen(Event::Start, handleEvent, this); EventManager::listen(Event::LoadState, handleEvent, this); EventManager::listen(Event::Terminate, handleEvent, this); loadMapping(); } DreamLinkGamepad::~DreamLinkGamepad() { EventManager::unlisten(Event::Start, handleEvent, this); EventManager::unlisten(Event::LoadState, handleEvent, this); EventManager::unlisten(Event::Terminate, handleEvent, this); if (dreamlink) { tearDownDreamLinkDevices(dreamlink); dreamlink.reset(); // Make sure settings are open in case disconnection happened mid-game if (!gui_is_open()) { gui_open_settings(); } } } void DreamLinkGamepad::set_maple_port(int port) { if (dreamlink) { if (port < 0 || port >= 4) { dreamlink->disconnect(); } else if (dreamlink->getBus() != port) { dreamlink->changeBus(port); if (is_registered()) { dreamlink->connect(); } } } SDLGamepad::set_maple_port(port); } void DreamLinkGamepad::registered() { if (dreamlink) { dreamlink->connect(); // Create DreamLink Maple Devices here just in case game is already running createDreamLinkDevices(dreamlink, false, false); } } void DreamLinkGamepad::handleEvent(Event event, void *arg) { DreamLinkGamepad *gamepad = static_cast(arg); if (gamepad->dreamlink != nullptr) { if (event != Event::Terminate) { createDreamLinkDevices(gamepad->dreamlink, event == Event::Start, event == Event::LoadState); } else { gamepad->dreamlink->gameTermination(); } } } void DreamLinkGamepad::resetMappingToDefault(bool arcade, bool gamepad) { SDLGamepad::resetMappingToDefault(arcade, gamepad); if (input_mapper) { if (dreamlink) { dreamlink->setDefaultMapping(input_mapper); } setBaseDefaultMapping(input_mapper); } } const char *DreamLinkGamepad::get_button_name(u32 code) { if (dreamlink) { const char* name = dreamlink->getButtonName(code); if (name) { return name; } } return SDLGamepad::get_button_name(code); } const char *DreamLinkGamepad::get_axis_name(u32 code) { if (dreamlink) { const char* name = dreamlink->getAxisName(code); if (name) { return name; } } return SDLGamepad::get_axis_name(code); } std::shared_ptr DreamLinkGamepad::getDefaultMapping() { std::shared_ptr mapping = SDLGamepad::getDefaultMapping(); if (mapping) { if (dreamlink) { dreamlink->setDefaultMapping(mapping); } setBaseDefaultMapping(mapping); } return mapping; } void DreamLinkGamepad::setBaseDefaultMapping(const std::shared_ptr& mapping) const { const u32 leftTrigger = mapping->get_axis_code(maple_port(), DreamcastKey::DC_AXIS_LT).first; const u32 rightTrigger = mapping->get_axis_code(maple_port(), DreamcastKey::DC_AXIS_RT).first; const u32 startCode = mapping->get_button_code(maple_port(), DreamcastKey::DC_BTN_START); if (leftTrigger != InputMapping::InputDef::INVALID_CODE && rightTrigger != InputMapping::InputDef::INVALID_CODE && startCode != InputMapping::InputDef::INVALID_CODE) { mapping->set_button(DreamcastKey::EMU_BTN_MENU, InputMapping::ButtonCombo{ InputMapping::InputSet{ InputMapping::InputDef{leftTrigger, InputMapping::InputDef::InputType::AXIS_POS}, InputMapping::InputDef{rightTrigger, InputMapping::InputDef::InputType::AXIS_POS}, InputMapping::InputDef{startCode, InputMapping::InputDef::InputType::BUTTON} }, false }); } } #else // USE_DREAMCASTCONTROLLER bool DreamLinkGamepad::isDreamcastController(int deviceIndex) { return false; } DreamLinkGamepad::DreamLinkGamepad(int maple_port, int joystick_idx, SDL_Joystick* sdl_joystick) : SDLGamepad(maple_port, joystick_idx, sdl_joystick) { } DreamLinkGamepad::~DreamLinkGamepad() { } void DreamLinkGamepad::set_maple_port(int port) { SDLGamepad::set_maple_port(port); } void DreamLinkGamepad::registered() { } void DreamLinkGamepad::resetMappingToDefault(bool arcade, bool gamepad) { SDLGamepad::resetMappingToDefault(arcade, gamepad); } const char *DreamLinkGamepad::get_button_name(u32 code) { return SDLGamepad::get_button_name(code); } const char *DreamLinkGamepad::get_axis_name(u32 code) { return SDLGamepad::get_axis_name(code); } std::shared_ptr DreamLinkGamepad::getDefaultMapping() { return SDLGamepad::getDefaultMapping(); } void DreamLinkGamepad::setBaseDefaultMapping(const std::shared_ptr& mapping) const { } #endif