├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── doc ├── CMakeLists.txt └── Doxyfile.in ├── install.sh ├── scripts ├── controlblock.service ├── insertLine.sh ├── testRPiGPIO.sh ├── travis-ci.sh └── uninstall.cmake ├── src ├── controlblock │ ├── app │ │ ├── CMakeLists.txt │ │ ├── ControlBlock.cpp │ │ ├── ControlBlock.h │ │ ├── Logger.cpp │ │ ├── Logger.h │ │ ├── PowerSwitch.cpp │ │ └── PowerSwitch.h │ ├── config │ │ ├── CMakeLists.txt │ │ ├── ControlBlockConfiguration.cpp │ │ ├── ControlBlockConfiguration.h │ │ ├── IControlBlockConfiguration.h │ │ ├── SingleConfiguration.cpp │ │ └── SingleConfiguration.h │ ├── gamepads │ │ ├── ArcadeGamepad.cpp │ │ ├── ArcadeGamepad.h │ │ ├── CMakeLists.txt │ │ ├── GamepadFactory.cpp │ │ ├── GamepadFactory.h │ │ ├── GenesisGamepad.cpp │ │ ├── GenesisGamepad.h │ │ ├── IGamepadFactory.h │ │ ├── InputDevice.h │ │ ├── MAMEGamepad.cpp │ │ ├── MAMEGamepad.h │ │ ├── NESGamepad.cpp │ │ ├── NESGamepad.h │ │ ├── NONEGamepad.cpp │ │ ├── NONEGamepad.h │ │ ├── SNESGamepad.cpp │ │ ├── SNESGamepad.h │ │ ├── SaturnGamepad.cpp │ │ └── SaturnGamepad.h │ ├── hal │ │ ├── CMakeLists.txt │ │ ├── DigitalIO.cpp │ │ ├── DigitalIO.h │ │ ├── HALFactory.cpp │ │ ├── HALFactory.h │ │ ├── IDigitalIO.h │ │ ├── mcp23s17pi.cpp │ │ └── mcp23s17pi.h │ ├── main.cpp │ └── uinput │ │ ├── CMakeLists.txt │ │ ├── IUInputDevice.h │ │ ├── IUInputFactory.h │ │ ├── UInputDevice.cpp │ │ ├── UInputDevice.h │ │ ├── UInputEventCodes.h │ │ ├── UInputFactory.cpp │ │ ├── UInputFactory.h │ │ ├── UInputGamepadArcade.cpp │ │ ├── UInputGamepadArcade.h │ │ ├── UInputGamepadGenesis.cpp │ │ ├── UInputGamepadGenesis.h │ │ ├── UInputGamepadNES.cpp │ │ ├── UInputGamepadNES.h │ │ ├── UInputGamepadSNES.cpp │ │ ├── UInputGamepadSNES.h │ │ ├── UInputGamepadSaturn.cpp │ │ ├── UInputGamepadSaturn.h │ │ ├── UInputKeyboard.cpp │ │ └── UInputKeyboard.h └── lib │ └── mcp23s17 │ ├── CMakeLists.txt │ ├── mcp23s17.cpp │ └── mcp23s17.h ├── supplementary ├── 4playerCB1.jpg ├── 4playerCB2.jpg ├── 4playerCB3.jpg ├── 4playerCB4.jpg ├── ControlBlockLayout.idraw ├── ControlBlockLayoutArcade.png ├── ControlBlockLayoutGenesis.png ├── ControlBlockLayoutMAME.png ├── ControlBlockLayoutSNES.png ├── controlblockconfig.cfg └── controlblockswitchoff.sh ├── test ├── CMakeLists.txt └── controlblock │ ├── app │ ├── ControlBlockTest.cpp │ └── PowerSwitchTest.cpp │ ├── config │ ├── ControlBlockConfigurationMock.h │ └── SingleConfigurationMock.h │ ├── gamepads │ ├── ArcadeGamepadTest.cpp │ ├── GamepadFactoryMock.h │ ├── GenesisGamepadTest.cpp │ ├── InputDeviceMock.h │ ├── MAMEGamepadTest.cpp │ └── SNESGamepadTest.cpp │ ├── hal │ ├── DigitalInMock.h │ └── DigitalOutMock.h │ └── uinput │ ├── UInputDeviceMock.h │ └── UInputFactoryMock.h ├── uninstall.sh └── workspace.code-workspace /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/cmake,macos,eclipse,c++ 2 | 3 | build/ 4 | 5 | ### JetBrains ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | .idea 10 | doc/htmlDoc/ 11 | 12 | # User-specific stuff: 13 | .idea/workspace.xml 14 | .idea/tasks.xml 15 | 16 | # Sensitive or high-churn files: 17 | .idea/dataSources/ 18 | .idea/dataSources.ids 19 | .idea/dataSources.xml 20 | .idea/dataSources.local.xml 21 | .idea/sqlDataSources.xml 22 | .idea/dynamic.xml 23 | .idea/uiDesigner.xml 24 | 25 | # Gradle: 26 | .idea/gradle.xml 27 | .idea/libraries 28 | 29 | # Mongo Explorer plugin: 30 | .idea/mongoSettings.xml 31 | 32 | ## File-based project format: 33 | *.iws 34 | 35 | ## Plugin-specific files: 36 | 37 | # IntelliJ 38 | /out/ 39 | 40 | # mpeltonen/sbt-idea plugin 41 | .idea_modules/ 42 | 43 | # JIRA plugin 44 | atlassian-ide-plugin.xml 45 | 46 | # Crashlytics plugin (for Android Studio and IntelliJ) 47 | com_crashlytics_export_strings.xml 48 | crashlytics.properties 49 | crashlytics-build.properties 50 | fabric.properties 51 | 52 | ### JetBrains Patch ### 53 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 54 | 55 | # *.iml 56 | # modules.xml 57 | # .idea/misc.xml 58 | # *.ipr 59 | 60 | ### CMake ### 61 | CMakeCache.txt 62 | CMakeFiles 63 | CMakeScripts 64 | Makefile 65 | cmake_install.cmake 66 | install_manifest.txt 67 | CTestTestfile.cmake 68 | 69 | 70 | ### macOS ### 71 | *.DS_Store 72 | .AppleDouble 73 | .LSOverride 74 | 75 | # Icon must end with two \r 76 | Icon 77 | # Thumbnails 78 | ._* 79 | # Files that might appear in the root of a volume 80 | .DocumentRevisions-V100 81 | .fseventsd 82 | .Spotlight-V100 83 | .TemporaryItems 84 | .Trashes 85 | .VolumeIcon.icns 86 | .com.apple.timemachine.donotpresent 87 | # Directories potentially created on remote AFP share 88 | .AppleDB 89 | .AppleDesktop 90 | Network Trash Folder 91 | Temporary Items 92 | .apdisk 93 | 94 | 95 | ### Eclipse ### 96 | 97 | .metadata 98 | bin/ 99 | tmp/ 100 | *.tmp 101 | *.bak 102 | *.swp 103 | *~.nib 104 | local.properties 105 | .settings/ 106 | .loadpath 107 | .recommenders 108 | 109 | # Eclipse Core 110 | .project 111 | 112 | # External tool builders 113 | .externalToolBuilders/ 114 | 115 | # Locally stored "Eclipse launch configurations" 116 | *.launch 117 | 118 | # PyDev specific (Python IDE for Eclipse) 119 | *.pydevproject 120 | 121 | # CDT-specific (C/C++ Development Tooling) 122 | .cproject 123 | 124 | # JDT-specific (Eclipse Java Development Tools) 125 | .classpath 126 | 127 | # Java annotation processor (APT) 128 | .factorypath 129 | 130 | # PDT-specific (PHP Development Tools) 131 | .buildpath 132 | 133 | # sbteclipse plugin 134 | .target 135 | 136 | # Tern plugin 137 | .tern-project 138 | 139 | # TeXlipse plugin 140 | .texlipse 141 | 142 | # STS (Spring Tool Suite) 143 | .springBeans 144 | 145 | # Code Recommenders 146 | .recommenders/ 147 | 148 | 149 | ### C++ ### 150 | # Prerequisites 151 | *.d 152 | 153 | # Compiled Object files 154 | *.slo 155 | *.lo 156 | *.o 157 | *.obj 158 | 159 | # Precompiled Headers 160 | *.gch 161 | *.pch 162 | 163 | # Compiled Dynamic libraries 164 | *.so 165 | *.dylib 166 | *.dll 167 | 168 | # Fortran module files 169 | *.mod 170 | *.smod 171 | 172 | # Compiled Static libraries 173 | *.lai 174 | *.la 175 | *.a 176 | *.lib 177 | 178 | # Executables 179 | *.exe 180 | *.out 181 | *.app 182 | 183 | .idea/ControlBlockService2.iml 184 | .idea/deployment.xml 185 | .idea/modules.xml 186 | .idea/webServers.xml 187 | cmake-build-debug/ 188 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/lib/jsoncpp"] 2 | path = src/lib/jsoncpp 3 | url = https://github.com/open-source-parsers/jsoncpp.git 4 | [submodule "test/lib/googletest"] 5 | path = test/lib/googletest 6 | url = https://github.com/google/googletest.git 7 | [submodule "src/lib/plog"] 8 | path = src/lib/plog 9 | url = https://github.com/SergiusTheBest/plog.git 10 | [submodule "src/lib/fmt"] 11 | path = src/lib/fmt 12 | url = https://github.com/fmtlib/fmt.git 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Use new trusty images, should yield newer compilers and packages 2 | sudo: required 3 | dist: precise 4 | language: cpp 5 | 6 | compiler: gcc 7 | env: 8 | global: 9 | - COMPILER=g++-4.9 10 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 11 | # via the "travis encrypt" command using the project repo's public key 12 | - secure: "bX8OzZ4CN4jDUtX3wls5RauoqtV5zH3HDhG/u3K9ss6PkfYYynpTmUQWqJlAryb2pJHRpjmIwgkUuV54gUEBh4PRPSqII/dUO7iWJ1YegpOhoRk4ri9LVsZl7FeGIWyRV7dLbZxd+mWgNJa+fC1/q29H4cCeOmrhEW6iOGHyhIn4HHKUA9kAzbHsGTgHspDDp3R3VGihO/c3f4chjCYtKzAZV5iIDESYDy0gWw0BCSnWWfk+XEQ/SOwXILcGZjcyeDUSNUV8GYfLppkDz6CQXdeLuM3Y3Xnveq+lBSt7eouZgCtfdnKEN11/8BKoNetcSI1LUwzyPqiEvLk9w3molequO7YgOFk9juS3SzWl3JoSnbebhAKKfOENviesYZKArVVr5QjyIj6VtVpzUVhP6ir9Zs0H61HZW0Gg1psZnSbd5gFDhYqV2x9XDZrZ+Rn/zayQR+dZF3vjwGYAUIdUp3Pcgb/gIpb1uQrXb1dLVc7Uf+aF+TnaO5OECEBb69R/vK1wJ6wD2G9JXOn2Dw41sMVwq/ivx+DsQXHl69fwUK3hFllcgfsVU50GYSHGQUWHWBGL12mPRvWd/nnBYz0y22kzpO7jl4j9V4sdXbMIsZl8GNzMyOEO9jd1wbfhYLtn28jdJVS+FIasuZ/vlzCWPGWn5cv/ndcYzQr2g6F6RlI=" 13 | 14 | addons: 15 | coverity_scan: 16 | project: 17 | name: "petrockblog/ControlBlockService2" 18 | description: "Build submitted via Travis CI" 19 | notification_email: contact@petrockblock.com 20 | build_command_prepend: "cmake ." 21 | build_command: "make" 22 | branch_pattern: master 23 | 24 | before_install: 25 | - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- 26 | - sudo apt-get update -qq 27 | 28 | script: 29 | - "bash -ex ./scripts/travis-ci.sh" 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.5) 2 | 3 | project(ControlBlockService2) 4 | 5 | set(CMAKE_CXX_STANDARD 14) 6 | 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") 8 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -g") 9 | set(EXECUTABLE_NAME "controlblock") 10 | 11 | include_directories(src/controlblock) 12 | include_directories(src/lib/jsoncpp/include) 13 | include_directories(src/lib/mcp23s17) 14 | include_directories(src/lib/plog/include) 15 | include_directories(src/lib/fmt/include) 16 | 17 | add_subdirectory(src/lib/jsoncpp) 18 | add_subdirectory(src/lib/mcp23s17) 19 | add_subdirectory(src/lib/fmt) 20 | 21 | set(PLOG_BUILD_SAMPLES OFF) 22 | 23 | add_subdirectory(src/controlblock/app) 24 | add_subdirectory(src/controlblock/config) 25 | add_subdirectory(src/controlblock/hal) 26 | add_subdirectory(src/controlblock/gamepads) 27 | add_subdirectory(src/controlblock/uinput) 28 | 29 | add_subdirectory(doc) # Doxygen documentation target 30 | 31 | add_executable(${EXECUTABLE_NAME} "src/controlblock/main.cpp") 32 | target_link_libraries(${EXECUTABLE_NAME} controlblock-app controlblock-config controlblock-hal controlblock-gamepads controlblock-uinput mcp23s17Lib fmt gpiodcxx) 33 | 34 | install(FILES ${CMAKE_BINARY_DIR}/${EXECUTABLE_NAME} DESTINATION /usr/bin 35 | PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ 36 | GROUP_EXECUTE GROUP_READ 37 | ) 38 | install(FILES ${CMAKE_BINARY_DIR}/../supplementary/controlblockconfig.cfg DESTINATION /etc 39 | PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ 40 | GROUP_READ 41 | ) 42 | install(FILES ${CMAKE_BINARY_DIR}/../supplementary/controlblockswitchoff.sh DESTINATION /etc 43 | PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ 44 | GROUP_EXECUTE GROUP_READ 45 | ) 46 | 47 | add_custom_target(uninstall 48 | "${CMAKE_COMMAND}" -P "${CMAKE_SOURCE_DIR}/scripts/uninstall.cmake" 49 | ) 50 | 51 | add_custom_target(installservice 52 | COMMAND chmod +x insertLine.sh 53 | COMMAND ./insertLine.sh 54 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/scripts 55 | COMMENT "Installing service." 56 | ) 57 | 58 | add_custom_target(uninstallservice 59 | systemctl disable --now controlblock.service 60 | COMMAND rm /etc/systemd/system/controlblock.service 61 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/../scripts 62 | COMMENT "Uninstalling service." 63 | ) 64 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 2 | https://github.com/petrockblog/ControlBlock2 3 | 4 | Permission to use, copy, modify and distribute the program in both binary and 5 | source form, for non-commercial purposes, is hereby granted without fee, 6 | providing that this license information and copyright notice appear with 7 | all copies and any derived work. 8 | 9 | This software is provided 'as-is', without any express or implied 10 | warranty. In no event shall the authors be held liable for any damages 11 | arising from the use of this software. 12 | 13 | This program is freeware for PERSONAL USE only. Commercial users must 14 | seek permission of the copyright holders first. Commercial use includes 15 | charging money for the program or software derived from the program. 16 | 17 | The copyright holders request that bug fixes and improvements to the code 18 | should be forwarded to them so everyone can benefit from the modifications 19 | in future versions. -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" OFF) 2 | 3 | IF (BUILD_DOCUMENTATION) 4 | FIND_PACKAGE(Doxygen) 5 | IF (NOT DOXYGEN_FOUND) 6 | MESSAGE(FATAL_ERROR "Doxygen is needed to build the documentation.") 7 | ENDIF () 8 | 9 | SET(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) 10 | SET(doxyfile ${PROJECT_BINARY_DIR}/Doxyfile) 11 | SET(doxy_html_index_file ${CMAKE_CURRENT_BINARY_DIR}/htmlDoc/index.html) 12 | SET(doxy_output_root ${CMAKE_CURRENT_SOURCE_DIR}/htmlDoc/) # Pasted into Doxyfile.in 13 | SET(doxy_input ${PROJECT_SOURCE_DIR}/src/controlblock) # Pasted into Doxyfile.in 14 | SET(doxy_extra_files "") # Pasted into Doxyfile.in 15 | # SET(doxy_extra_files ${CMAKE_CURRENT_SOURCE_DIR}/mainpage.dox) # Pasted into Doxyfile.in 16 | 17 | CONFIGURE_FILE(${doxyfile_in} ${doxyfile} @ONLY) 18 | 19 | ADD_CUSTOM_COMMAND( 20 | OUTPUT ${doxy_html_index_file} 21 | COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} 22 | # The following should be ${doxyfile} only but it 23 | # will break the dependency. 24 | # The optimal solution would be creating a 25 | # custom_command for ${doxyfile} generation 26 | # but I still have to figure out how... 27 | MAIN_DEPENDENCY ${doxyfile} ${doxyfile_in} 28 | DEPENDS ${EXECUTABLE_NAME} ${doxy_extra_files} 29 | COMMENT "Generating HTML documentation" 30 | ) 31 | 32 | ADD_CUSTOM_TARGET(doc ALL DEPENDS ${doxy_html_index_file}) 33 | 34 | # INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc) 35 | ENDIF () -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Function to check if the script is run with sudo 4 | check_sudo() { 5 | if [[ "$(id -u)" -ne 0 ]]; then 6 | echo "Script must be run under sudo from the user you want to install for. Try 'sudo $0'" 7 | exit 1 8 | fi 9 | } 10 | 11 | # Function to update boot configuration 12 | update_boot_config() { 13 | local FILE="/boot/config.txt" 14 | local LINE_TO_ENSURE="usb_max_current_enable=1" 15 | local LINE_TO_REPLACE="usb_max_current_enable=0" 16 | 17 | if grep -Fxq "$LINE_TO_ENSURE" "$FILE"; then 18 | echo "/boot/config.txt is up-to-date, no action taken." 19 | elif grep -Fxq "$LINE_TO_REPLACE" "$FILE"; then 20 | sed -i "s/^$LINE_TO_REPLACE/$LINE_TO_ENSURE/" "$FILE" 21 | echo "/boot/config.txt updated." 22 | else 23 | echo "$LINE_TO_ENSURE" | sudo tee -a "$FILE" 24 | echo "/boot/config.txt updated." 25 | fi 26 | } 27 | 28 | # Function to ensure all needed OS packages are installed 29 | install_packages() { 30 | apt-get install -y git cmake g++ doxygen libc6 libc6-dev libgpiod-dev gpiod || { 31 | echo "Error during installation of APT packages" 32 | exit 1 33 | } 34 | } 35 | 36 | # Function to ensure we are within the ControlBlockService2 directory 37 | ensure_directory() { 38 | local currentDirectory=${PWD##*/} 39 | if [[ $currentDirectory != "ControlBlockService2" ]]; then 40 | if [[ -d ControlBlockService2 ]]; then 41 | rm -rf ControlBlockService2 42 | fi 43 | git clone --recursive https://github.com/petrockblog/ControlBlockService2 44 | cd ControlBlockService2 || exit 45 | fi 46 | } 47 | 48 | # Function to ensure that the submodule data is available 49 | update_submodules() { 50 | git submodule update --init --recursive 51 | } 52 | 53 | # Function to create a folder for build artifacts and change into that folder 54 | prepare_build_directory() { 55 | if [[ -d build ]]; then 56 | rm -rf build 57 | fi 58 | mkdir build || { 59 | echo "Error while creating build folder" 60 | exit 1 61 | } 62 | pushd build || { 63 | echo "Error while changing into the folder build" 64 | exit 1 65 | } 66 | } 67 | 68 | # Function to create Makefiles and build the driver 69 | build_driver() { 70 | cmake .. || { 71 | echo "Error while generating Makefiles" 72 | exit 1 73 | } 74 | make || { 75 | echo "Error during building binary" 76 | exit 1 77 | } 78 | } 79 | 80 | # Function to install the binary and the driver as a service 81 | install_driver() { 82 | make install || { 83 | echo "Error during installation of binary" 84 | exit 1 85 | } 86 | make installservice || { 87 | echo "Error during installation of service" 88 | exit 1 89 | } 90 | sleep 3 91 | } 92 | 93 | # Function to perform sanity checks 94 | perform_sanity_checks() { 95 | if [[ ! -f /usr/bin/controlblock ]]; then 96 | echo "[ERROR] The ControlBlock driver binary is not installed" 97 | else 98 | echo "[SUCCESS] The ControlBlock driver binary is installed" 99 | fi 100 | 101 | local isServiceRunning 102 | isServiceRunning=$(pgrep -x controlblock) 103 | if [[ -n $isServiceRunning ]]; then 104 | echo "[SUCCESS] The ControlBlock service is running" 105 | else 106 | echo "[ERROR] The ControlBlock service is not running" 107 | fi 108 | } 109 | 110 | # Main script execution 111 | check_sudo 112 | install_packages 113 | ensure_directory 114 | update_submodules 115 | prepare_build_directory 116 | build_driver 117 | install_driver 118 | perform_sanity_checks 119 | update_boot_config 120 | 121 | echo "You can find the configuration file at /etc/controlblockconfig.cfg" 122 | -------------------------------------------------------------------------------- /scripts/controlblock.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=ControlBlock Service 3 | Documentation=https://www.petrockblock.com 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/bin/controlblock 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /scripts/insertLine.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function addSPIBcmModule() { 4 | echo -e "Making sure that SPI interface is enabled" 5 | grep -qxF 'dtparam=spi=on' /boot/config.txt || echo 'dtparam=spi=on' >> /boot/config.txt 6 | modprobe spi_bcm2835 7 | } 8 | 9 | # enable spi kernel module 10 | touch /etc/modprobe.d/raspi-blacklist.conf 11 | sed -i -e "s|^blacklist spi-bcm2708|#blacklist spi-bcm2708|g" /etc/modprobe.d/raspi-blacklist.conf 12 | dtparam spi=on 13 | addSPIBcmModule 14 | 15 | # install ControlBlockService files 16 | install -m 0755 controlblock.service /etc/systemd/system/ 17 | systemctl enable --now controlblock.service 18 | -------------------------------------------------------------------------------- /scripts/testRPiGPIO.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "***********************************************************************************" 3 | echo "* Test Script to Check for the Correct Functionality of the ControlBlock GPIOs *" 4 | echo "* www.petrockblock.com *" 5 | echo "* *" 6 | echo "* Preparation: Remove the ControlBlock and any other hardware connected to the *" 7 | echo "* GPIO pins. Also stop the ControlBlock service via the command *" 8 | echo "* sudo service controlblockservice stop *" 9 | echo "* *" 10 | echo "* To check for the correct functionality of the pins that the ControlBlock uses *" 11 | echo "* connect a jumper wire to GND (pin 6) and cautiously connect it with these pins: *" 12 | echo "* 11, 12, 19, 21, 23, 24. For every of these pins a message of the form *" 13 | echo "* \"Found GPIO x\" must be printed to the output. *" 14 | echo "* Do not get confused with the printed GPIO numbers. That are the original BCM *" 15 | echo "* pin numbers. *" 16 | echo "* *" 17 | echo "* You can exit this bash script by pressing Ctrl-C. *" 18 | echo "***********************************************************************************" 19 | echo "" 20 | 21 | pinNumbers=(8 9 10 11 17 18) 22 | 23 | # trap ctrl-c and call ctrl_c() 24 | trap ctrl_c INT 25 | function ctrl_c() { 26 | echo "** Trapped CTRL-C" 27 | 28 | # Clean up 29 | for index in pinNumbers; do 30 | echo "${index}" > /sys/class/gpio/unexport 31 | done 32 | exit 130 33 | } 34 | 35 | # Note that the GPIO numbers that you program here refer to the pins 36 | # of the BCM2835 and *not* the numbers on the pin header. 37 | # So, if you want to activate GPIO7 on the header you should be 38 | # using GPIO4 in this script. Likewise if you want to activate GPIO0 39 | # on the header you should be using GPIO17 here. 40 | function setPinAsInput() { 41 | local pin=$1 42 | # Set up GPIO 4 and set to output 43 | echo "${pin}" > /sys/class/gpio/export 44 | sleep 0.25 45 | echo "in" > /sys/class/gpio/gpio${pin}/direction 46 | sleep 0.25 47 | echo "high" > /sys/class/gpio/gpio${pin}/direction 48 | sleep 0.25 49 | } 50 | 51 | # Set up GPIO 7 and set to input 52 | for index in ${pinNumbers[@]}; do 53 | setPinAsInput ${index} 54 | done 55 | 56 | while true; do 57 | for index in ${pinNumbers[@]}; do 58 | pinValue="/sys/class/gpio/gpio${index}/value" 59 | value=`cat ${pinValue}` 60 | if [[ "${value}" == "0" ]]; then 61 | echo "Found GPIO ${index}" 62 | sleep 0.25 63 | fi 64 | done 65 | done 66 | -------------------------------------------------------------------------------- /scripts/travis-ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Based on a test script from avsm/ocaml repo https://github.com/avsm/ocaml 3 | # based on the script from https://www.tomaz.me/2013/12/02/running-travis-ci-tests-on-arm.html 4 | 5 | CHROOT_DIR=/tmp/arm-chroot 6 | MIRROR=http://archive.raspbian.org/raspbian 7 | 8 | VERSION=jessie 9 | CHROOT_ARCH=armhf 10 | 11 | # Debian package dependencies for the host 12 | HOST_DEPENDENCIES="debootstrap qemu-user-static binfmt-support sbuild" 13 | 14 | # Debian package dependencies for the chrooted environment 15 | GUEST_DEPENDENCIES="build-essential git m4 sudo cmake doxygen g++-4.9 gcc-4.9 python" 16 | 17 | function setup_arm_chroot { 18 | # Host dependencies 19 | sudo apt-get install -qq -y ${HOST_DEPENDENCIES} 20 | 21 | # Create chrooted environment 22 | sudo mkdir -p ${CHROOT_DIR} 23 | pushd /usr/share/debootstrap/scripts; sudo ln -s sid jessie; popd 24 | sudo debootstrap --foreign --no-check-gpg --include=fakeroot,build-essential \ 25 | --arch=${CHROOT_ARCH} ${VERSION} ${CHROOT_DIR} ${MIRROR} 26 | sudo cp /usr/bin/qemu-arm-static ${CHROOT_DIR}/usr/bin/ 27 | sudo chroot ${CHROOT_DIR} ./debootstrap/debootstrap --second-stage 28 | sudo sbuild-createchroot --arch=${CHROOT_ARCH} --foreign --setup-only \ 29 | ${VERSION} ${CHROOT_DIR} ${MIRROR} 30 | 31 | # Create file with environment variables which will be used inside chrooted 32 | # environment 33 | echo "export ARCH=${ARCH}" > envvars.sh 34 | echo "export TRAVIS_BUILD_DIR=${TRAVIS_BUILD_DIR}" >> envvars.sh 35 | chmod a+x envvars.sh 36 | 37 | # Install dependencies inside chroot 38 | sudo chroot ${CHROOT_DIR} apt-get update 39 | sudo chroot ${CHROOT_DIR} apt-get --allow-unauthenticated install \ 40 | -qq -y ${GUEST_DEPENDENCIES} 41 | 42 | # Create build dir and copy travis build files to our chroot environment 43 | sudo mkdir -p ${CHROOT_DIR}/${TRAVIS_BUILD_DIR} 44 | sudo rsync -av ${TRAVIS_BUILD_DIR}/ ${CHROOT_DIR}/${TRAVIS_BUILD_DIR}/ 45 | 46 | # Indicate chroot environment has been set up 47 | sudo touch ${CHROOT_DIR}/.chroot_is_done 48 | 49 | # Call ourselves again which will cause tests to run 50 | sudo chroot ${CHROOT_DIR} bash -c "cd ${TRAVIS_BUILD_DIR} && ./scripts/travis-ci.sh" 51 | } 52 | 53 | if [ -e "/.chroot_is_done" ]; then 54 | # We are inside ARM chroot 55 | echo "Running inside chrooted environment" 56 | 57 | . ./envvars.sh 58 | 59 | echo "Running tests" 60 | echo "Environment: $(uname -a)" 61 | 62 | mkdir build && cd build 63 | cmake .. 64 | make 65 | make test 66 | make install 67 | make installservice 68 | make uninstallservice 69 | make uninstall 70 | 71 | else 72 | if [ "${ARCH}" = "arm" ]; then 73 | # ARM test run, need to set up chrooted environment first 74 | echo "Setting up chrooted ARM environment" 75 | setup_arm_chroot 76 | fi 77 | fi 78 | -------------------------------------------------------------------------------- /scripts/uninstall.cmake: -------------------------------------------------------------------------------- 1 | set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") 2 | 3 | if(NOT EXISTS ${MANIFEST}) 4 | message(FATAL_ERROR "Cannot find install manifest: '${MANIFEST}'") 5 | endif() 6 | 7 | file(STRINGS ${MANIFEST} files) 8 | foreach(file ${files}) 9 | if(EXISTS ${file}) 10 | message(STATUS "Removing file: '${file}'") 11 | 12 | exec_program( 13 | ${CMAKE_COMMAND} ARGS "-E remove ${file}" 14 | OUTPUT_VARIABLE stdout 15 | RETURN_VALUE result 16 | ) 17 | 18 | if(NOT "${result}" STREQUAL 0) 19 | message(FATAL_ERROR "Failed to remove file: '${file}'.") 20 | endif() 21 | else() 22 | MESSAGE(STATUS "File '${file}' does not exist.") 23 | endif() 24 | endforeach(file) 25 | -------------------------------------------------------------------------------- /src/controlblock/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(controlblock-app STATIC 2 | ControlBlock.cpp 3 | PowerSwitch.cpp 4 | Logger.cpp 5 | ) 6 | -------------------------------------------------------------------------------- /src/controlblock/app/ControlBlock.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Mueller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include 24 | 25 | #include "ControlBlock.h" 26 | #include "hal/DigitalIO.h" 27 | #include "Logger.h" 28 | 29 | ControlBlock::ControlBlock(IUInputFactory &uiFactoryRef, 30 | IControlBlockConfiguration &configRef, 31 | IGamepadFactory &gamepadFactory) : 32 | m_numberOfGamepads(0u) { 33 | configRef.loadConfiguration(); 34 | 35 | // initialize the controllers 36 | auto counter = 0; 37 | for (auto index = 0; index < MAX_NUMBER_OF_CONTROLBLOCKS; index++) { 38 | if (configRef.getConfiguration(index).isEnabled()) { 39 | // initialize MCP23S17 and DigitalIO instances 40 | const uint8_t address = configRef.getConfiguration(index).getDeviceAddress(); 41 | const uint8_t mcpIndex1 = index * 2; 42 | const uint8_t mcpIndex2 = (index * 2) + 1; 43 | mcp23s17[mcpIndex1] = new MCP23S17PI(MCP23S17PI::CHIPSELECT_0, address); 44 | mcp23s17[mcpIndex2] = new MCP23S17PI(MCP23S17PI::CHIPSELECT_0, (address | 0x01)); 45 | digitalIO[index] = new DigitalIO(*mcp23s17[mcpIndex1], *mcp23s17[mcpIndex2]); 46 | 47 | // configure GPIOs according to the gamepad type 48 | const InputDevice::GamepadType_e type = configRef.getConfiguration(index).getGamepadType(); 49 | configureDevice(digitalIO[index], type); 50 | 51 | // create gamepad instances 52 | gamepads[counter] = gamepadFactory.createGamepad(type, *digitalIO[index]); 53 | InputDevice::Channel_e channel = getInputDeviceChannel(counter); 54 | gamepads[counter]->initialize(channel); 55 | counter++; 56 | if (!configRef.getConfiguration(index).isOnlyOneGamepadEnabled()) { 57 | gamepads[counter] = gamepadFactory.createGamepad(type, *digitalIO[index]); 58 | InputDevice::Channel_e counterChannel = getInputDeviceChannel(counter); 59 | gamepads[counter]->initialize(counterChannel); 60 | counter++; 61 | } 62 | } 63 | } 64 | m_numberOfGamepads = counter; 65 | 66 | // initialize the power switch 67 | if (configRef.getConfiguration(0).isEnabled() && configRef.getConfiguration(0).isPowerSwitchEnabled()) { 68 | powerSwitch = std::make_unique(*digitalIO[0], PowerSwitch::ShutdownActivated::ACTIVATED); 69 | } 70 | } 71 | 72 | ControlBlock::~ControlBlock() { 73 | for (auto mcp : mcp23s17) { 74 | if (mcp != nullptr) { 75 | delete mcp; 76 | } 77 | } 78 | for (auto digIO : digitalIO) { 79 | if (digIO != nullptr) { 80 | delete digIO; 81 | } 82 | } 83 | for (auto pad : gamepads) { 84 | if (pad != nullptr) { 85 | delete pad; 86 | } 87 | } 88 | } 89 | 90 | void ControlBlock::update() { 91 | try { 92 | for (int counter = 0; counter < m_numberOfGamepads; counter++) { 93 | gamepads[counter]->update(); 94 | } 95 | } 96 | catch (std::exception& exc) { 97 | std::cout << "ControlBlock.cpp: Error while updating the gamepad devices. Error number: " << exc.what() << std::endl; 98 | } 99 | 100 | try { 101 | if (powerSwitch != nullptr) { 102 | powerSwitch->update(); 103 | } 104 | } 105 | catch (std::exception& exc) { 106 | std::cout << "ControlBlock.cpp: Error while updating the power switch instance. Error number: " << exc.what() 107 | << std::endl; 108 | } 109 | } 110 | 111 | InputDevice::Channel_e ControlBlock::getInputDeviceChannel(int counterValue) { 112 | assert(counterValue < (MAX_NUMBER_OF_INPUTDEVICES)); 113 | 114 | InputDevice::Channel_e channel; 115 | switch (counterValue) { 116 | case 0:channel = InputDevice::CHANNEL_1; 117 | break; 118 | case 1:channel = InputDevice::CHANNEL_2; 119 | break; 120 | case 2:channel = InputDevice::CHANNEL_3; 121 | break; 122 | case 3:channel = InputDevice::CHANNEL_4; 123 | break; 124 | default: 125 | throw std::runtime_error("ControlBlock.cpp: Unknown counter value"); 126 | } 127 | return channel; 128 | } 129 | 130 | void ControlBlock::configureDevice(IDigitalIO *digitalIO, InputDevice::GamepadType_e type) { 131 | assert(digitalIO != nullptr); 132 | 133 | switch (type) { 134 | case InputDevice::GAMEPAD_ARCADE:digitalIO->configureDevice(IDigitalIO::DIO_DEVICE_ALLIN); 135 | break; 136 | case InputDevice::GAMEPAD_MAME:digitalIO->configureDevice(IDigitalIO::DIO_DEVICE_SNES); 137 | break; 138 | case InputDevice::GAMEPAD_SNES:digitalIO->configureDevice(IDigitalIO::DIO_DEVICE_SNES); 139 | break; 140 | case InputDevice::GAMEPAD_NES:digitalIO->configureDevice(IDigitalIO::DIO_DEVICE_SNES); 141 | break; 142 | case InputDevice::GAMEPAD_GENESIS:digitalIO->configureDevice(IDigitalIO::DIO_DEVICE_GENESIS); 143 | break; 144 | case InputDevice::GAMEPAD_SATURN:digitalIO->configureDevice(IDigitalIO::DIO_DEVICE_GENESIS); 145 | break; 146 | case InputDevice::GAMEPAD_NONE: 147 | // do nothing 148 | break; 149 | default: 150 | Logger::logMessage("ControlBlock.cpp: Unknown type"); 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /src/controlblock/app/ControlBlock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCK_H 24 | #define CONTROLBLOCK_H 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "PowerSwitch.h" 31 | #include "gamepads/IGamepadFactory.h" 32 | #include "uinput/IUInputFactory.h" 33 | #include "hal/IDigitalIO.h" 34 | 35 | /** 36 | * This class models the functionalities of the ControlBlock 37 | */ 38 | class ControlBlock 39 | { 40 | public: 41 | /** 42 | * Constructor 43 | * @param uiFactoryRef - Reference with UInputFactory interface 44 | * @param digitalInRef - Reference with IDigitalIn interface 45 | * @param digitalOutRef - Reference with IDigitalOut interface 46 | * @param configRef - Reference with IControlBlockConfiguration interface 47 | * @param gamepadFactory 48 | */ 49 | ControlBlock(IUInputFactory& uiFactoryRef, IControlBlockConfiguration& configRef, IGamepadFactory& gamepadFactory); 50 | 51 | /** 52 | * Destructor 53 | */ 54 | ~ControlBlock(); 55 | 56 | ControlBlock(const ControlBlock& other) = delete; 57 | ControlBlock& operator=(const ControlBlock&) = delete; 58 | 59 | /** 60 | * Updates the state of every configured gamepad and of the power switch 61 | */ 62 | void update(); 63 | 64 | private: 65 | static const uint8_t MAX_NUMBER_OF_CONTROLBLOCKS = 2u; 66 | static const uint8_t MAX_NUMBER_OF_INPUTDEVICES = 2u * MAX_NUMBER_OF_CONTROLBLOCKS; 67 | 68 | uint8_t m_numberOfGamepads; 69 | std::unique_ptr powerSwitch; 70 | MCP23S17PI* mcp23s17[MAX_NUMBER_OF_INPUTDEVICES]; 71 | IDigitalIO* digitalIO[MAX_NUMBER_OF_CONTROLBLOCKS]; 72 | 73 | InputDevice* gamepads[MAX_NUMBER_OF_INPUTDEVICES]; 74 | static InputDevice::Channel_e getInputDeviceChannel(int counterValue); 75 | static void configureDevice(IDigitalIO* digitalIO, InputDevice::GamepadType_e type); 76 | 77 | }; 78 | 79 | #endif // CONTROLBLOCK_H 80 | -------------------------------------------------------------------------------- /src/controlblock/app/Logger.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Florian Müller on 03.12.19. 3 | // 4 | 5 | #include 6 | 7 | #include "Logger.h" 8 | 9 | 10 | Logger::Logger() = default; 11 | 12 | Logger::~Logger() = default; 13 | 14 | void Logger::init() { 15 | plog::init(plog::debug, "/tmp/controlblock.log"); 16 | } 17 | void Logger::logMessage(const std::string& message) { 18 | PLOGD << message; 19 | } 20 | -------------------------------------------------------------------------------- /src/controlblock/app/Logger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Florian Müller on 03.12.19. 3 | // 4 | 5 | #ifndef CONTROLBLOCKSERVICE2_SRC_CONTROLBLOCK_APP_LOGGER_H_ 6 | #define CONTROLBLOCKSERVICE2_SRC_CONTROLBLOCK_APP_LOGGER_H_ 7 | 8 | #include 9 | 10 | class Logger { 11 | public: 12 | Logger(); 13 | ~Logger(); 14 | 15 | static void init(); 16 | static void logMessage(const std::string& message); 17 | 18 | }; 19 | 20 | #endif //CONTROLBLOCKSERVICE2_SRC_CONTROLBLOCK_APP_LOGGER_H_ 21 | -------------------------------------------------------------------------------- /src/controlblock/app/PowerSwitch.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Mueller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "fmt/format.h" 28 | #include "PowerSwitch.h" 29 | #include "Logger.h" 30 | 31 | PowerSwitch::PowerSwitch(IDigitalIO &digitalIOReference, ShutdownActivated doShutdownValue) : doShutdown(doShutdownValue), 32 | isShutdownInitiatedValue(false), 33 | digitalIO(digitalIOReference) 34 | { 35 | digitalIO.configureDevice(IDigitalIO::DIO_DEVICE_POWERSWITCH); 36 | 37 | const bool kIsRPi5 = isRPi5(); 38 | Logger::logMessage(fmt::format("Determined RPi model {}", kIsRPi5 ? ">=5" : "<5")); 39 | ::gpiod::chip chip(kIsRPi5 ? "gpiochip4" : "gpiochip0"); 40 | 41 | powerSwitchIn_port_ = std::make_shared<::gpiod::line>(chip.get_line(18)); 42 | powerSwitchIn_port_->request({"gpiochip0", ::gpiod::line_request::DIRECTION_INPUT, 0}, 0); 43 | 44 | powerSwitchOut_port_ = std::make_shared<::gpiod::line>(chip.get_line(17)); 45 | setPowerSignal(PowerState::ON); 46 | 47 | Logger::logMessage(fmt::format("Created PowerSwitch. doShutdown: {}", doShutdownValue)); 48 | } 49 | 50 | void PowerSwitch::update() 51 | { 52 | if ((doShutdown == ShutdownActivated::ACTIVATED) && (getShutdownSignal() == ShutdownSignal::ACTIVATED) && (!isShutdownInitiatedValue)) 53 | { 54 | const std::string kShutdownCommand{"/etc/controlblockswitchoff.sh &"}; 55 | Logger::logMessage("Initiating shutdown. Calling"); 56 | system(kShutdownCommand.c_str()); 57 | isShutdownInitiatedValue = true; 58 | } 59 | } 60 | 61 | bool PowerSwitch::isShutdownInitiated() const 62 | { 63 | return isShutdownInitiatedValue; 64 | } 65 | 66 | void PowerSwitch::setPowerSignal(PowerState state) 67 | { 68 | if (state == PowerState::OFF) 69 | { 70 | powerSwitchOut_port_->request({"gpiochip0", ::gpiod::line_request::DIRECTION_OUTPUT, 0}, 0); 71 | Logger::logMessage("Disabled power signal."); 72 | } 73 | else 74 | { 75 | powerSwitchOut_port_->request({"gpiochip0", ::gpiod::line_request::DIRECTION_OUTPUT, 0}, 1); 76 | Logger::logMessage("Enabled power signal."); 77 | } 78 | } 79 | 80 | PowerSwitch::ShutdownSignal PowerSwitch::getShutdownSignal() 81 | { 82 | ShutdownSignal signal; 83 | if (!powerSwitchIn_port_->get_value()) 84 | { 85 | signal = ShutdownSignal::DEACTIVATED; 86 | } 87 | else 88 | { 89 | signal = ShutdownSignal::ACTIVATED; 90 | } 91 | return signal; 92 | } 93 | 94 | bool PowerSwitch::isRPi5() 95 | { 96 | // Determine Raspberry Pi version 97 | std::ifstream cpuinfo("/proc/cpuinfo"); 98 | std::string line; 99 | std::regex raspberryPiVersionPattern("Raspberry Pi 5"); 100 | std::smatch match; 101 | bool isRaspberryPi5 = false; 102 | 103 | while (std::getline(cpuinfo, line)) 104 | { 105 | if (std::regex_search(line, match, raspberryPiVersionPattern)) 106 | { 107 | isRaspberryPi5 = true; 108 | break; 109 | } 110 | } 111 | 112 | return isRaspberryPi5; 113 | } -------------------------------------------------------------------------------- /src/controlblock/app/PowerSwitch.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef POWERSWITCH_H 24 | #define POWERSWITCH_H 25 | 26 | #include 27 | #include "hal/IDigitalIO.h" 28 | 29 | /** 30 | * This class models the power switch functionalities of the ControlBlock. 31 | */ 32 | class PowerSwitch 33 | { 34 | public: 35 | /** 36 | * Power state identifiers 37 | */ 38 | enum class PowerState 39 | { 40 | OFF, //!< Power state is off 41 | ON //!< Power state is on 42 | }; 43 | 44 | /** 45 | * Shutdown signal identifiers 46 | */ 47 | enum class ShutdownSignal 48 | { 49 | DEACTIVATED, //!< Shutdown signal is not set 50 | ACTIVATED //!< Shutdown signal is set 51 | }; 52 | 53 | /** 54 | * Indicates whether the power switch functionality is enabled or not 55 | */ 56 | enum class ShutdownActivated 57 | { 58 | DEACTIVATED, //!< Power switch is disabled 59 | ACTIVATED //!< Power switch is enabled 60 | }; 61 | 62 | /** 63 | * Constructor 64 | * @param digitalInReference - Reference with IDigitalIn interface 65 | * @param digitalOutReference - Reference with IDigitalOut interface 66 | * @param doShutdownValue - Power switch function is enabled or not 67 | */ 68 | explicit PowerSwitch(IDigitalIO& digitalIOReference, ShutdownActivated doShutdownValue); 69 | 70 | /** 71 | * Destructor 72 | */ 73 | ~PowerSwitch() = default; 74 | 75 | /** 76 | * Checks the shutdown signal. If the signal is logical high then the switch-off script 77 | * is executed. 78 | */ 79 | void update(); 80 | 81 | /** 82 | * Returns the information whether the switch-off script was called or not. 83 | * @return 84 | * - true, if the switch-off script was called, 85 | * - false, otherwise. 86 | */ 87 | bool isShutdownInitiated() const; 88 | 89 | private: 90 | ShutdownActivated doShutdown; 91 | bool isShutdownInitiatedValue; 92 | IDigitalIO& digitalIO; 93 | std::shared_ptr<::gpiod::line> powerSwitchIn_port_; 94 | std::shared_ptr<::gpiod::line> powerSwitchOut_port_; 95 | 96 | void setPowerSignal(PowerState state); 97 | ShutdownSignal getShutdownSignal(); 98 | bool isRPi5(); 99 | 100 | }; 101 | 102 | #endif // POWERSWITCH_H 103 | -------------------------------------------------------------------------------- /src/controlblock/config/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(controlblock-config STATIC 2 | ControlBlockConfiguration.cpp 3 | SingleConfiguration.cpp 4 | ) 5 | 6 | target_link_libraries(controlblock-config jsoncpp_lib_static) 7 | -------------------------------------------------------------------------------- /src/controlblock/config/ControlBlockConfiguration.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "json/json.h" 27 | 28 | #include "ControlBlockConfiguration.h" 29 | 30 | ControlBlockConfiguration::ControlBlockConfiguration() : 31 | hasLoadedConfiguration(false), 32 | singleConfiguration{nullptr} 33 | { 34 | } 35 | 36 | ControlBlockConfiguration::~ControlBlockConfiguration() 37 | { 38 | if (hasLoadedConfiguration) { 39 | for (auto & index : singleConfiguration) { 40 | if (index != nullptr) { 41 | delete index; 42 | } 43 | } 44 | } 45 | } 46 | 47 | void ControlBlockConfiguration::loadConfiguration() 48 | { 49 | Json::Value root; // will contains the root value after parsing. 50 | try { 51 | Json::Reader reader; 52 | 53 | std::ifstream configStream(CONFIGFILEPATH); 54 | std::string config_doc((std::istreambuf_iterator(configStream)), std::istreambuf_iterator()); 55 | 56 | bool parsingSuccessful = reader.parse(config_doc, root); 57 | if (!parsingSuccessful) { 58 | // report to the user the failure and their locations in the document. 59 | throw std::runtime_error("ControlBlockConfiguration: Failed to parse configuration"); 60 | } 61 | 62 | singleConfiguration[0] = new SingleConfiguration(root["controlblocks"][0]["enabled"].asBool(), 63 | (uint8_t) (root["controlblocks"][0]["address"]["SJ2"].asInt() << 2 64 | | root["controlblocks"][0]["address"]["SJ1"].asInt() << 1), 65 | root["controlblocks"][0]["gamepadtype"].asString(), root["controlblocks"][0]["powerswitchOn"].asBool(), 66 | root["controlblocks"][0]["onlyOneGamepad"].asBool()); 67 | singleConfiguration[1] = new SingleConfiguration(root["controlblocks"][1]["enabled"].asBool(), 68 | (uint8_t) (root["controlblocks"][1]["address"]["SJ2"].asInt() << 2 69 | | root["controlblocks"][1]["address"]["SJ1"].asInt() << 1), 70 | root["controlblocks"][1]["gamepadtype"].asString(), root["controlblocks"][1]["powerswitchOn"].asBool(), 71 | root["controlblocks"][1]["onlyOneGamepad"].asBool()); 72 | } 73 | catch (std::exception& exc) { 74 | std::cout << "ControlBlockConfiguration: Error while initializing ControlBlockConfiguration instance. Error number: " << errno 75 | << std::endl; 76 | } 77 | hasLoadedConfiguration = true; 78 | } 79 | 80 | SingleConfiguration& ControlBlockConfiguration::getConfiguration(int controlBlockID) 81 | { 82 | assert(controlBlockID < MAX_CONTROLBLOCK_ID); 83 | assert(hasLoadedConfiguration); 84 | 85 | return *singleConfiguration[controlBlockID]; 86 | } 87 | -------------------------------------------------------------------------------- /src/controlblock/config/ControlBlockConfiguration.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKCONFIGURATION_H 24 | #define CONTROLBLOCKCONFIGURATION_H 25 | 26 | #include "IControlBlockConfiguration.h" 27 | 28 | /** 29 | * This class models the user configuration of the ControlBlock 30 | */ 31 | class ControlBlockConfiguration: public IControlBlockConfiguration 32 | { 33 | public: 34 | /** 35 | * Constructor. Loads the information from the given configuration file. 36 | * It is assumed that the config file follows a certain JSON schema. 37 | * @param configFile - Path and file name of the configuration file 38 | */ 39 | ControlBlockConfiguration(); 40 | 41 | /** 42 | * Default destructor 43 | */ 44 | ~ControlBlockConfiguration(); 45 | 46 | /** 47 | * Loads the configuration from \ref CONFIGFILEPATH 48 | */ 49 | void loadConfiguration() override; 50 | 51 | /** 52 | * Returns the configuration for a given ControlBlock ID 53 | * @param controlBlockID - The ID of the ControlBlock 54 | * @return Reference to the configuration of the given ControlBlock ID 55 | */ 56 | SingleConfiguration& getConfiguration(int controlBlockID) override; 57 | 58 | private: 59 | static const int MAX_CONTROLBLOCK_ID = 2u; 60 | 61 | const std::string CONFIGFILEPATH{"/etc/controlblockconfig.cfg"}; 62 | 63 | bool hasLoadedConfiguration; 64 | 65 | SingleConfiguration* singleConfiguration[MAX_CONTROLBLOCK_ID]; 66 | }; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/controlblock/config/IControlBlockConfiguration.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_ICONTROLBLOCKCONFIGURATION_H 24 | #define CONTROLBLOCKSERVICE2_ICONTROLBLOCKCONFIGURATION_H 25 | 26 | #include "SingleConfiguration.h" 27 | 28 | class IControlBlockConfiguration 29 | { 30 | public: 31 | virtual void loadConfiguration() = 0; 32 | virtual SingleConfiguration& getConfiguration(int controlBlockID) = 0; 33 | 34 | }; 35 | 36 | #endif //CONTROLBLOCKSERVICE2_ICONTROLBLOCKCONFIGURATION_H 37 | -------------------------------------------------------------------------------- /src/controlblock/config/SingleConfiguration.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "../app/Logger.h" 24 | #include "fmt/format.h" 25 | #include "SingleConfiguration.h" 26 | 27 | SingleConfiguration::SingleConfiguration(bool enabled, uint8_t address, const std::string& pType, bool pwrSwitch, 28 | bool oneGp) : 29 | isEnabledValue(enabled), 30 | deviceAddress(address), 31 | isPowerSwitchEnabledValue(pwrSwitch), 32 | isOnlyOneGamepadEnabledValue(oneGp) 33 | { 34 | if (pType == "arcade") 35 | { 36 | padType = InputDevice::GAMEPAD_ARCADE; 37 | } 38 | else if (pType == "mame") 39 | { 40 | padType = InputDevice::GAMEPAD_MAME; 41 | } 42 | else if (pType == "snes") 43 | { 44 | padType = InputDevice::GAMEPAD_SNES; 45 | } 46 | else if (pType == "nes") 47 | { 48 | padType = InputDevice::GAMEPAD_NES; 49 | } 50 | else if (pType == "none") 51 | { 52 | padType = InputDevice::GAMEPAD_NONE; 53 | } 54 | else if (pType == "genesis") 55 | { 56 | padType = InputDevice::GAMEPAD_GENESIS; 57 | } 58 | else if (pType == "saturn") 59 | { 60 | padType = InputDevice::GAMEPAD_SATURN; 61 | } 62 | const std::string configMessage = fmt::format("Created configuration. isEnabled: {}, address: {}, pad type: {}, power switch: {}, only one gamepad: {}", enabled, address, pType, pwrSwitch, oneGp); 63 | Logger::logMessage(configMessage); 64 | } 65 | 66 | SingleConfiguration::~SingleConfiguration() 67 | = default; 68 | 69 | bool SingleConfiguration::isEnabled() 70 | { 71 | return isEnabledValue; 72 | } 73 | 74 | uint8_t SingleConfiguration::getDeviceAddress() 75 | { 76 | return deviceAddress; 77 | } 78 | 79 | InputDevice::GamepadType_e SingleConfiguration::getGamepadType() 80 | { 81 | return padType; 82 | } 83 | 84 | bool SingleConfiguration::isPowerSwitchEnabled() 85 | { 86 | return isPowerSwitchEnabledValue; 87 | } 88 | 89 | bool SingleConfiguration::isOnlyOneGamepadEnabled() 90 | { 91 | return isOnlyOneGamepadEnabledValue; 92 | } 93 | -------------------------------------------------------------------------------- /src/controlblock/config/SingleConfiguration.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef SINGLECONFIGURATION_H 24 | #define SINGLECONFIGURATION_H 25 | 26 | #include 27 | #include "gamepads/InputDevice.h" 28 | 29 | /** 30 | * Instances of this class represent the configuration of a single ControlBlock 31 | */ 32 | class SingleConfiguration 33 | { 34 | public: 35 | /** 36 | * Constructor 37 | * @param enabled - The enable status 38 | * @param address - The hardware address 39 | * @param type - The type of the gamepad 40 | * @param pwrSwitch - Whether the power switch functionality is enabled (=true) or not (=false) 41 | * @param oneGp - Whether one (=true) or two (=false) gamepads should be registered 42 | */ 43 | SingleConfiguration(bool enabled, uint8_t address, const std::string& type, bool pwrSwitch, bool oneGp); 44 | 45 | /** 46 | * Destructor 47 | */ 48 | virtual ~SingleConfiguration(); 49 | 50 | /** 51 | * Returns whether the ControlBlock is enabled or not 52 | * @return 53 | * - true, if the ControlBlock is enabled, 54 | * - false, otherwise. 55 | */ 56 | virtual bool isEnabled(); 57 | 58 | /** 59 | * Returns the device address of the ControlBlock 60 | * @return The address of the ControlBlock 61 | */ 62 | virtual uint8_t getDeviceAddress(); 63 | 64 | /** 65 | * Returns the type of gamepad 66 | * @return The type of gamepad 67 | */ 68 | virtual InputDevice::GamepadType_e getGamepadType(); 69 | 70 | /** 71 | * Returns whether the power switch functionality is enabled or not 72 | * @return 73 | * - true, if the power switch functionality is enabled, 74 | * - false, otherwise. 75 | */ 76 | virtual bool isPowerSwitchEnabled(); 77 | 78 | /** 79 | * Returns whether only one gamepad should be enabled or not 80 | * @return 81 | * - true, if only one gamepad should be enabled, 82 | * - false, otherwise. 83 | */ 84 | virtual bool isOnlyOneGamepadEnabled(); 85 | 86 | private: 87 | bool isEnabledValue; 88 | uint8_t deviceAddress; 89 | InputDevice::GamepadType_e padType; 90 | bool isPowerSwitchEnabledValue; 91 | bool isOnlyOneGamepadEnabledValue; 92 | }; 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/ArcadeGamepad.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include 24 | #include "ArcadeGamepad.h" 25 | 26 | ArcadeGamepad::ArcadeGamepad(IUInputFactory& uiFactory, IDigitalIO& digitalIORef) : 27 | channel(InputDevice::CHANNEL_UNDEFINED), 28 | digitalIO(digitalIORef), 29 | gamepad(uiFactory.getUInputDevice(IUInputDevice::TYPE_GAMEPAD_ARCADE)) 30 | { 31 | } 32 | 33 | void ArcadeGamepad::initialize(InputDevice::Channel_e channel) 34 | { 35 | digitalIO.configureDevice(IDigitalIO::DIO_DEVICE_ALLIN); 36 | this->channel = channel; 37 | } 38 | 39 | void ArcadeGamepad::update() 40 | { 41 | if ((channel == InputDevice::CHANNEL_1) || (channel == InputDevice::CHANNEL_3)) { 42 | // axes 43 | if (digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_LEFT) == IDigitalIO::DIO_LEVEL_HIGH) { 44 | gamepad->setKeyState(ABS_X, 0, EV_ABS); 45 | } 46 | else if (digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_RIGHT) == IDigitalIO::DIO_LEVEL_HIGH) { 47 | gamepad->setKeyState(ABS_X, 4, EV_ABS); 48 | } 49 | else { 50 | gamepad->setKeyState(ABS_X, 2, EV_ABS); 51 | } 52 | if (digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_UP) == IDigitalIO::DIO_LEVEL_HIGH) { 53 | gamepad->setKeyState(ABS_Y, 0, EV_ABS); 54 | } 55 | else if (digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_DOWN) == IDigitalIO::DIO_LEVEL_HIGH) { 56 | gamepad->setKeyState(ABS_Y, 4, EV_ABS); 57 | } 58 | else { 59 | gamepad->setKeyState(ABS_Y, 2, EV_ABS); 60 | } 61 | 62 | // buttons 63 | gamepad->setKeyState(BTN_A, 64 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW1) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 65 | gamepad->setKeyState(BTN_B, 66 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW2) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 67 | gamepad->setKeyState(BTN_C, 68 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW3) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 69 | gamepad->setKeyState(BTN_X, 70 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW4) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 71 | gamepad->setKeyState(BTN_Y, 72 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW5) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 73 | gamepad->setKeyState(BTN_Z, 74 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW6) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 75 | gamepad->setKeyState(BTN_TL, 76 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW7) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 77 | gamepad->setKeyState(BTN_TR, 78 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW8) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 79 | gamepad->setKeyState(BTN_START, 80 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_START) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 81 | EV_KEY); 82 | gamepad->setKeyState(BTN_SELECT, 83 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_COIN) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 84 | EV_KEY); 85 | gamepad->setKeyState(BTN_TL2, 86 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_A) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 87 | gamepad->setKeyState(BTN_TR2, 88 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_B) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 89 | 90 | gamepad->sync(); 91 | } 92 | else if ((channel == InputDevice::CHANNEL_2) || (channel == InputDevice::CHANNEL_4)) { 93 | // axes 94 | if (digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_LEFT) == IDigitalIO::DIO_LEVEL_HIGH) { 95 | gamepad->setKeyState(ABS_X, 0, EV_ABS); 96 | } 97 | else if (digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_RIGHT) == IDigitalIO::DIO_LEVEL_HIGH) { 98 | gamepad->setKeyState(ABS_X, 4, EV_ABS); 99 | } 100 | else { 101 | gamepad->setKeyState(ABS_X, 2, EV_ABS); 102 | } 103 | 104 | if (digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_UP) == IDigitalIO::DIO_LEVEL_HIGH) { 105 | gamepad->setKeyState(ABS_Y, 0, EV_ABS); 106 | } 107 | else if (digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_DOWN) == IDigitalIO::DIO_LEVEL_HIGH) { 108 | gamepad->setKeyState(ABS_Y, 4, EV_ABS); 109 | } 110 | else { 111 | gamepad->setKeyState(ABS_Y, 2, EV_ABS); 112 | } 113 | 114 | // buttons 115 | gamepad->setKeyState(BTN_A, 116 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW1) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 117 | gamepad->setKeyState(BTN_B, 118 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW2) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 119 | gamepad->setKeyState(BTN_C, 120 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW3) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 121 | gamepad->setKeyState(BTN_X, 122 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW4) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 123 | gamepad->setKeyState(BTN_Y, 124 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW5) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 125 | gamepad->setKeyState(BTN_Z, 126 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW6) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 127 | gamepad->setKeyState(BTN_TL, 128 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW7) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 129 | gamepad->setKeyState(BTN_TR, 130 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW8) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 131 | gamepad->setKeyState(BTN_START, 132 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_START) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 133 | EV_KEY); 134 | gamepad->setKeyState(BTN_SELECT, 135 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_COIN) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 136 | EV_KEY); 137 | gamepad->setKeyState(BTN_TL2, 138 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_A) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 139 | gamepad->setKeyState(BTN_TR2, 140 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_B) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 141 | 142 | gamepad->sync(); 143 | } 144 | else { 145 | throw std::runtime_error("Unknown channel."); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/ArcadeGamepad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef ARCADEGAMEPAD_H 24 | #define ARCADEGAMEPAD_H 25 | 26 | #include "InputDevice.h" 27 | #include "uinput/IUInputDevice.h" 28 | #include "hal/IDigitalIO.h" 29 | #include "uinput/IUInputFactory.h" 30 | 31 | /** 32 | * This class implements a gamepad for arcade controls. 33 | * It polls a four-way joystick and various buttons. 34 | */ 35 | class ArcadeGamepad: public InputDevice 36 | { 37 | public: 38 | /** 39 | * Constructor 40 | * @param uiFactory - Reference with IUInputFactory interface 41 | * @param digitalInRef - Reference with IigitalIn interface 42 | */ 43 | ArcadeGamepad(IUInputFactory& uiFactory, IDigitalIO& digitalIORef); 44 | 45 | /** 46 | * Destructor 47 | */ 48 | ~ArcadeGamepad() = default; 49 | 50 | /** 51 | * See \ref InputDevice::initialize() 52 | */ 53 | void initialize(InputDevice::Channel_e channel) override; 54 | 55 | /** 56 | * See \ref InputDevice::update() 57 | */ 58 | void update() override; 59 | 60 | private: 61 | InputDevice::Channel_e channel; 62 | IUInputDevice* gamepad; 63 | IDigitalIO& digitalIO; 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(controlblock-gamepads STATIC 2 | ArcadeGamepad.cpp 3 | MAMEGamepad.cpp 4 | NONEGamepad.cpp 5 | SNESGamepad.cpp 6 | NESGamepad.cpp 7 | GenesisGamepad.cpp 8 | SaturnGamepad.cpp 9 | GamepadFactory.cpp 10 | ) 11 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/GamepadFactory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "GamepadFactory.h" 3 | #include "ArcadeGamepad.h" 4 | #include "SNESGamepad.h" 5 | #include "NESGamepad.h" 6 | #include "MAMEGamepad.h" 7 | #include "GenesisGamepad.h" 8 | #include "SaturnGamepad.h" 9 | #include "NONEGamepad.h" 10 | 11 | GamepadFactory::GamepadFactory(IUInputFactory& uiFactoryRef) : 12 | uiFactory(uiFactoryRef) 13 | { 14 | } 15 | 16 | InputDevice* GamepadFactory::createGamepad(InputDevice::GamepadType_e gamepadType, IDigitalIO& digitalIO) 17 | { 18 | switch (gamepadType) 19 | { 20 | case InputDevice::GAMEPAD_ARCADE: 21 | return new ArcadeGamepad(uiFactory, digitalIO); 22 | case InputDevice::GAMEPAD_SNES: 23 | return new SNESGamepad(uiFactory, digitalIO); 24 | case InputDevice::GAMEPAD_NES: 25 | return new NESGamepad(uiFactory, digitalIO); 26 | case InputDevice::GAMEPAD_MAME: 27 | return new MAMEGamepad(uiFactory, digitalIO); 28 | case InputDevice::GAMEPAD_GENESIS: 29 | return new GenesisGamepad(uiFactory, digitalIO); 30 | case InputDevice::GAMEPAD_SATURN: 31 | return new SaturnGamepad(uiFactory, digitalIO); 32 | case InputDevice::GAMEPAD_NONE: 33 | return new NONEGamepad(); 34 | default: 35 | throw std::runtime_error("GamepadFactory: Error while configuring gamepad type"); 36 | } 37 | } -------------------------------------------------------------------------------- /src/controlblock/gamepads/GamepadFactory.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTROLBLOCKSERVICE2_GAMEPADFACTORY_H 2 | #define CONTROLBLOCKSERVICE2_GAMEPADFACTORY_H 3 | 4 | #include 5 | #include "IGamepadFactory.h" 6 | #include "InputDevice.h" 7 | #include "uinput/IUInputFactory.h" 8 | #include "hal/IDigitalIO.h" 9 | 10 | class GamepadFactory: public IGamepadFactory 11 | { 12 | public: 13 | explicit GamepadFactory(IUInputFactory& uiFactoryRef); 14 | InputDevice* createGamepad(InputDevice::GamepadType_e gamepadType, IDigitalIO& digitalIO) override; 15 | 16 | private: 17 | IUInputFactory& uiFactory; 18 | 19 | }; 20 | 21 | #endif //CONTROLBLOCKSERVICE2_GAMEPADFACTORY_H 22 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/GenesisGamepad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_GENESISGAMEPAD_H 24 | #define CONTROLBLOCKSERVICE2_GENESISGAMEPAD_H 25 | 26 | #include 27 | #include "InputDevice.h" 28 | #include "uinput/IUInputDevice.h" 29 | #include "uinput/IUInputFactory.h" 30 | #include "hal/IDigitalIO.h" 31 | 32 | /** 33 | * Models a Genesis/Megadrive gamepad. Polls the controller and sends the corresponding 34 | * user inputs to the uinput device. 35 | */ 36 | class GenesisGamepad: public InputDevice 37 | { 38 | public: 39 | /** 40 | * Constructor 41 | * @param uiFactory 42 | * @param digitalInRef 43 | * @param digitalOutRef 44 | */ 45 | GenesisGamepad(IUInputFactory& uiFactory, IDigitalIO& digitalIORef); 46 | 47 | /** 48 | * Default Destructor 49 | */ 50 | virtual ~GenesisGamepad() = default; 51 | 52 | /** 53 | * Implements \ref InputDevice::initialize 54 | */ 55 | void initialize(InputDevice::Channel_e channel) override; 56 | 57 | /** 58 | * Implement \ref InputDevice::update() 59 | */ 60 | void update() override; 61 | 62 | private: 63 | struct Input 64 | { 65 | int player; 66 | IDigitalIO::DIO_Channel_e inputChannel; 67 | int lowFlag; 68 | int highFlag; 69 | int pulse3Flag; 70 | }; 71 | 72 | // Controller Button Flags 73 | static const uint32_t ON = 0x0001; 74 | static const uint32_t UP = 0x0002; 75 | static const uint32_t DOWN = 0x0004; 76 | static const uint32_t LEFT = 0x0008; 77 | static const uint32_t RIGHT = 0x0010; 78 | static const uint32_t START = 0x0020; 79 | static const uint32_t A = 0x0040; 80 | static const uint32_t B = 0x0080; 81 | static const uint32_t C = 0x0100; 82 | static const uint32_t X = 0x0200; 83 | static const uint32_t Y = 0x0400; 84 | static const uint32_t Z = 0x0800; 85 | static const uint32_t MODE = 0x1000; 86 | 87 | static const IDigitalIO::DIO_Channel_e CHN_SELECT[]; 88 | static const Input inputMap[]; 89 | 90 | static const uint32_t SHORTDELAY = 20u; 91 | static const uint32_t LONGDELAY = 1000u; 92 | 93 | InputDevice::Channel_e channel; 94 | IUInputDevice* gamepad; 95 | IDigitalIO& digitalIO; 96 | 97 | bool isInSixButtonMode; 98 | uint32_t currentState; 99 | uint32_t lastState; 100 | uint32_t playerIndex; 101 | 102 | void readButtons(); 103 | void resetState(); 104 | void read3buttons(int player); 105 | void read6buttons(int player); 106 | void sendStates(); 107 | }; 108 | 109 | #endif //CONTROLBLOCKSERVICE2_GENESISGAMEPAD_H 110 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/IGamepadFactory.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_IGAMEPADFACTORY_H 24 | #define CONTROLBLOCKSERVICE2_IGAMEPADFACTORY_H 25 | 26 | #include "InputDevice.h" 27 | #include "hal/DigitalIO.h" 28 | 29 | class IGamepadFactory 30 | { 31 | public: 32 | virtual InputDevice* createGamepad(InputDevice::GamepadType_e gamepadType, IDigitalIO& digitalIO) = 0; 33 | }; 34 | 35 | #endif //CONTROLBLOCKSERVICE2_IGAMEPADFACTORY_H 36 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/InputDevice.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef INPUTDEVICE_H 24 | #define INPUTDEVICE_H 25 | 26 | /** 27 | * Interface for an input device supported by the ControlBlock 28 | */ 29 | class InputDevice 30 | { 31 | public: 32 | /** 33 | * Gamepad types 34 | */ 35 | enum GamepadType_e 36 | { 37 | GAMEPAD_ARCADE = 0, //!< Arcade gamepad type 38 | GAMEPAD_MAME, //!< MAME gamepad type 39 | GAMEPAD_SNES, //!< SNES gamepad type 40 | GAMEPAD_NES, //!< SNES gamepad type 41 | GAMEPAD_GENESIS, //!< Sega Genesis gamepad type 42 | GAMEPAD_SATURN, //!< Sega Saturn gamepad type 43 | GAMEPAD_NONE //!< Neutral gamepad type 44 | }; 45 | 46 | /** 47 | * Channel identifiers 48 | */ 49 | enum Channel_e 50 | { 51 | CHANNEL_1 = 0, //!< Channel 1 52 | CHANNEL_2, //!< Channel 2 53 | CHANNEL_3, //!< Channel 3 54 | CHANNEL_4, //!< Channel 4 55 | CHANNEL_UNDEFINED //!< Undefined channel 56 | }; 57 | 58 | /** 59 | * Initializes the input and output interfaces specifically for the gamepad type 60 | * and the given channel 61 | * @param channel - THe channel for which the gamepad should be initialized 62 | */ 63 | virtual void initialize(Channel_e channel) = 0; 64 | 65 | /** 66 | * Update the gamepad state. 67 | */ 68 | virtual void update() = 0; 69 | }; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/MAMEGamepad.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include 24 | #include "MAMEGamepad.h" 25 | 26 | MAMEGamepad::MAMEGamepad(IUInputFactory& uiFactory, IDigitalIO& digitalIORef) : 27 | channel(CHANNEL_UNDEFINED), 28 | digitalIO(digitalIORef), 29 | keyboard(uiFactory.getUInputDevice(IUInputDevice::TYPE_KEYBOARD)) 30 | { 31 | } 32 | 33 | void MAMEGamepad::initialize(InputDevice::Channel_e channel) 34 | { 35 | digitalIO.configureDevice(IDigitalIO::DIO_DEVICE_ALLIN); 36 | 37 | this->channel = channel; 38 | } 39 | 40 | void MAMEGamepad::update() 41 | { 42 | if ((channel == InputDevice::CHANNEL_1) || (channel == InputDevice::CHANNEL_3)) { 43 | // axes 44 | keyboard->setKeyState(KEY_LEFT, 45 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_LEFT) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 46 | EV_KEY); 47 | keyboard->setKeyState(KEY_RIGHT, 48 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_RIGHT) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 49 | EV_KEY); 50 | keyboard->setKeyState(KEY_UP, 51 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_UP) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 52 | keyboard->setKeyState(KEY_DOWN, 53 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_DOWN) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 54 | EV_KEY); 55 | 56 | // buttons 57 | keyboard->setKeyState(KEY_LEFTCTRL, 58 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW1) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 59 | EV_KEY); 60 | keyboard->setKeyState(KEY_LEFTALT, 61 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW2) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 62 | EV_KEY); 63 | keyboard->setKeyState(KEY_SPACE, 64 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW3) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 65 | EV_KEY); 66 | keyboard->setKeyState(KEY_LEFTSHIFT, 67 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW4) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 68 | EV_KEY); 69 | keyboard->setKeyState(KEY_Z, 70 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW5) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 71 | EV_KEY); 72 | keyboard->setKeyState(KEY_X, 73 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW6) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 74 | EV_KEY); 75 | keyboard->setKeyState(KEY_C, 76 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW7) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 77 | EV_KEY); 78 | keyboard->setKeyState(KEY_V, 79 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_SW8) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 80 | EV_KEY); 81 | keyboard->setKeyState(KEY_1, 82 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_START) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 83 | EV_KEY); 84 | keyboard->setKeyState(KEY_5, 85 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_COIN) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 86 | EV_KEY); 87 | keyboard->setKeyState(KEY_P, 88 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_A) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 89 | keyboard->setKeyState(KEY_ENTER, 90 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_B) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 91 | 92 | keyboard->sync(); 93 | } 94 | else if ((channel == InputDevice::CHANNEL_2) || (channel == InputDevice::CHANNEL_4)) { 95 | // axes 96 | keyboard->setKeyState(KEY_D, 97 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_LEFT) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 98 | EV_KEY); 99 | keyboard->setKeyState(KEY_G, 100 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_RIGHT) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 101 | EV_KEY); 102 | keyboard->setKeyState(KEY_R, 103 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_UP) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 104 | keyboard->setKeyState(KEY_F, 105 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_DOWN) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 106 | EV_KEY); 107 | 108 | // buttons 109 | keyboard->setKeyState(KEY_A, 110 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW1) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 111 | EV_KEY); 112 | keyboard->setKeyState(KEY_S, 113 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW2) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 114 | EV_KEY); 115 | keyboard->setKeyState(KEY_Q, 116 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW3) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 117 | EV_KEY); 118 | keyboard->setKeyState(KEY_W, 119 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW4) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 120 | EV_KEY); 121 | keyboard->setKeyState(KEY_I, 122 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW5) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 123 | EV_KEY); 124 | keyboard->setKeyState(KEY_K, 125 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW6) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 126 | EV_KEY); 127 | keyboard->setKeyState(KEY_J, 128 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW7) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 129 | EV_KEY); 130 | keyboard->setKeyState(KEY_L, 131 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_SW8) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 132 | EV_KEY); 133 | keyboard->setKeyState(KEY_2, 134 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_START) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 135 | EV_KEY); 136 | keyboard->setKeyState(KEY_6, 137 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_COIN) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, 138 | EV_KEY); 139 | keyboard->setKeyState(KEY_TAB, 140 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_A) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 141 | keyboard->setKeyState(KEY_ESC, 142 | digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P2_B) == IDigitalIO::DIO_LEVEL_LOW ? 0 : 1, EV_KEY); 143 | 144 | keyboard->sync(); 145 | } 146 | else { 147 | throw std::runtime_error("Unknown channel"); 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/MAMEGamepad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef MAMEGAMEPAD_H 24 | #define MAMEGAMEPAD_H 25 | 26 | #include "InputDevice.h" 27 | #include "hal/IDigitalIO.h" 28 | #include "uinput/IUInputFactory.h" 29 | 30 | class MAMEGamepad: public InputDevice 31 | { 32 | public: 33 | MAMEGamepad(IUInputFactory& uiFactory, IDigitalIO& digitalIORef); 34 | ~MAMEGamepad() = default; 35 | 36 | void initialize(InputDevice::Channel_e channel) override; 37 | void update() override; 38 | 39 | private: 40 | InputDevice::Channel_e channel; 41 | IDigitalIO& digitalIO; 42 | IUInputDevice* keyboard; 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/NESGamepad.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "NESGamepad.h" 28 | 29 | NESGamepad::NESGamepad(IUInputFactory& uiFactoryRef, IDigitalIO& digitalIORef) : 30 | uiFactory(uiFactoryRef), 31 | digitalIO(digitalIORef), 32 | isInitialized(false), 33 | channel(InputDevice::CHANNEL_UNDEFINED) 34 | { 35 | gamepad = uiFactory.getUInputDevice(IUInputDevice::TYPE_GAMEPAD_NES); 36 | keyboard = uiFactory.getUInputDevice(IUInputDevice::TYPE_KEYBOARD); 37 | } 38 | 39 | void NESGamepad::initialize(InputDevice::Channel_e channel) 40 | { 41 | // assert(digitalIn != NULL); 42 | // assert(digitalOut != NULL); 43 | 44 | this->channel = channel; 45 | 46 | if (channel == InputDevice::CHANNEL_1) { 47 | digitalIO.setLevel(DIO_CHANNEL_P1P2_STROBE, IDigitalIO::DIO_LEVEL_LOW); // player 1,2 strobe/latch 48 | digitalIO.setLevel(DIO_CHANNEL_P1P2_CLOCK, IDigitalIO::DIO_LEVEL_LOW); // player 1,2 clock 49 | digitalIO.setLevel(DIO_CHANNEL_P1_VCC, IDigitalIO::DIO_LEVEL_HIGH); // player 1 VCC 50 | } 51 | else { 52 | digitalIO.setLevel(DIO_CHANNEL_P2_VCC, IDigitalIO::DIO_LEVEL_HIGH); // player 2 VCC 53 | // strobe and clock were already initialized with player 1 initialization. 54 | } 55 | 56 | isInitialized = true; 57 | } 58 | 59 | void NESGamepad::update() 60 | { 61 | assert(isInitialized); 62 | 63 | uint16_t state = getNESControllerState(); 64 | 65 | if (channel == InputDevice::CHANNEL_1) { 66 | IDigitalIO::DIO_Level_e levelButton1 = digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_B); 67 | if (levelButton1 == IDigitalIO::DIO_LEVEL_HIGH) 68 | { 69 | state |= GPAD_NES_SELECT; 70 | state |= GPAD_NES_START; 71 | keyboard->setKeyState(KEY_ESC, 0, EV_KEY); 72 | } 73 | else 74 | { 75 | keyboard->setKeyState(KEY_ESC, 1, EV_KEY); 76 | } 77 | 78 | IDigitalIO::DIO_Level_e levelButton2 = digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_A); 79 | if (levelButton2 == IDigitalIO::DIO_LEVEL_HIGH) 80 | { 81 | state |= GPAD_NES_SELECT; 82 | state |= GPAD_NES_B; 83 | keyboard->setKeyState(KEY_F1, 0, EV_KEY); 84 | } 85 | else 86 | { 87 | keyboard->setKeyState(KEY_F1, 1, EV_KEY); 88 | } 89 | keyboard->sync(); 90 | } 91 | 92 | // left-right axis 93 | if ((state & GPAD_NES_LEFT) == GPAD_NES_LEFT) { 94 | gamepad->setKeyState(ABS_X, 0, EV_ABS); 95 | } 96 | else if ((state & GPAD_NES_RIGHT) == GPAD_NES_RIGHT) { 97 | gamepad->setKeyState(ABS_X, 4, EV_ABS); 98 | } 99 | else { 100 | gamepad->setKeyState(ABS_X, 2, EV_ABS); 101 | } 102 | 103 | // up-down axis 104 | if ((state & GPAD_NES_UP) == GPAD_NES_UP) { 105 | gamepad->setKeyState(ABS_Y, 0, EV_ABS); 106 | } 107 | else if ((state & GPAD_NES_DOWN) == GPAD_NES_DOWN) { 108 | gamepad->setKeyState(ABS_Y, 4, EV_ABS); 109 | } 110 | else { 111 | gamepad->setKeyState(ABS_Y, 2, EV_ABS); 112 | } 113 | 114 | // buttons 115 | gamepad->setKeyState(BTN_B, (state & GPAD_NES_B) == GPAD_NES_B ? 1 : 0, EV_KEY); 116 | gamepad->setKeyState(BTN_A, (state & GPAD_NES_A) == GPAD_NES_A ? 1 : 0, EV_KEY); 117 | gamepad->setKeyState(BTN_START, (state & GPAD_NES_START) == GPAD_NES_START ? 1 : 0, EV_KEY); 118 | gamepad->setKeyState(BTN_SELECT, (state & GPAD_NES_SELECT) == GPAD_NES_SELECT ? 1 : 0, EV_KEY); 119 | gamepad->sync(); 120 | } 121 | 122 | uint16_t NESGamepad::getNESControllerState() 123 | { 124 | const std::chrono::microseconds dura(STROBEDELAY_US); 125 | uint16_t state = 0u; 126 | 127 | digitalIO.setLevel(DIO_CHANNEL_P1P2_STROBE, IDigitalIO::DIO_LEVEL_HIGH); 128 | std::this_thread::sleep_for(2 * dura); 129 | digitalIO.setLevel(DIO_CHANNEL_P1P2_STROBE, IDigitalIO::DIO_LEVEL_LOW); 130 | std::this_thread::sleep_for(2 * dura); 131 | 132 | for (uint8_t i = 0u; i < NUMBER_OF_BUTTONS; i++) { 133 | IDigitalIO::DIO_Level_e curpin; 134 | if ((channel == InputDevice::CHANNEL_1) || (channel == InputDevice::CHANNEL_3)) { 135 | curpin = digitalIO.getLevel(DIO_CHANNEL_P1_DATA); 136 | } 137 | else if ((channel == InputDevice::CHANNEL_2) || (channel == InputDevice::CHANNEL_4)) { 138 | curpin = digitalIO.getLevel(DIO_CHANNEL_P2_DATA); 139 | } 140 | else { 141 | throw std::runtime_error("Unknown channel"); 142 | } 143 | 144 | if (curpin == IDigitalIO::DIO_LEVEL_HIGH) { 145 | state |= (1u << i); 146 | } 147 | 148 | digitalIO.setLevel(DIO_CHANNEL_P1P2_CLOCK, IDigitalIO::DIO_LEVEL_HIGH); 149 | std::this_thread::sleep_for(dura); 150 | digitalIO.setLevel(DIO_CHANNEL_P1P2_CLOCK, IDigitalIO::DIO_LEVEL_LOW); 151 | std::this_thread::sleep_for(dura); 152 | } 153 | 154 | return state; 155 | } 156 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/NESGamepad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef NESGAMEPAD_H 24 | #define NESGAMEPAD_H 25 | 26 | #include 27 | #include 28 | #include "hal/DigitalIO.h" 29 | #include "InputDevice.h" 30 | 31 | class NESGamepad: public InputDevice 32 | { 33 | public: 34 | /* bit masks for checking the button states for SNES controllers */ 35 | static const uint16_t GPAD_NES_A = 0x01; 36 | static const uint16_t GPAD_NES_B = 0x02; 37 | static const uint16_t GPAD_NES_SELECT = 0x04; 38 | static const uint16_t GPAD_NES_START = 0x08; 39 | static const uint16_t GPAD_NES_UP = 0x10; 40 | static const uint16_t GPAD_NES_DOWN = 0x20; 41 | static const uint16_t GPAD_NES_LEFT = 0x40; 42 | static const uint16_t GPAD_NES_RIGHT = 0x80; 43 | static const uint16_t GPAD_NES_RESET = 0x1000; 44 | 45 | NESGamepad(IUInputFactory& uiFactoryRef, IDigitalIO& digitalIORef); 46 | ~NESGamepad() = default; 47 | 48 | void initialize(InputDevice::Channel_e channel) override; 49 | void update() override; 50 | 51 | private: 52 | static const uint32_t STROBEDELAY_US = 4u; 53 | static const uint32_t NUMBER_OF_BUTTONS = 8u; 54 | 55 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P1_VCC = IDigitalIO::DIO_CHANNEL_P2_RIGHT; 56 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P2_VCC = IDigitalIO::DIO_CHANNEL_P2_LEFT; 57 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P1P2_STROBE = IDigitalIO::DIO_CHANNEL_P2_UP; 58 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P1P2_CLOCK = IDigitalIO::DIO_CHANNEL_P2_DOWN; 59 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P1_DATA = IDigitalIO::DIO_CHANNEL_P2_SW1; 60 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P2_DATA = IDigitalIO::DIO_CHANNEL_P2_SW2; 61 | 62 | IUInputFactory& uiFactory; 63 | IDigitalIO& digitalIO; 64 | 65 | bool isInitialized; 66 | InputDevice::Channel_e channel; 67 | IUInputDevice* gamepad; 68 | IUInputDevice* keyboard; 69 | 70 | uint16_t getNESControllerState(); 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/NONEGamepad.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "NONEGamepad.h" 24 | 25 | NONEGamepad::NONEGamepad() = default; 26 | 27 | NONEGamepad::~NONEGamepad() = default; 28 | 29 | void NONEGamepad::initialize(InputDevice::Channel_e channel) 30 | { 31 | } 32 | 33 | void NONEGamepad::update() 34 | { 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/NONEGamepad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef NONEGAMEPAD_H 24 | #define NONEGAMEPAD_H 25 | 26 | #include "InputDevice.h" 27 | 28 | class NONEGamepad: public InputDevice 29 | { 30 | public: 31 | NONEGamepad(); 32 | ~NONEGamepad(); 33 | 34 | void initialize(InputDevice::Channel_e channel) override; 35 | void update() override; 36 | 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/SNESGamepad.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "SNESGamepad.h" 28 | 29 | SNESGamepad::SNESGamepad(IUInputFactory& uiFactoryRef, IDigitalIO& digitalIORef) : 30 | uiFactory(uiFactoryRef), 31 | digitalIO(digitalIORef), 32 | isInitialized(false), 33 | channel(InputDevice::CHANNEL_UNDEFINED) 34 | { 35 | gamepad = uiFactory.getUInputDevice(IUInputDevice::TYPE_GAMEPAD_SNES); 36 | keyboard = uiFactory.getUInputDevice(IUInputDevice::TYPE_KEYBOARD); 37 | } 38 | 39 | void SNESGamepad::initialize(InputDevice::Channel_e channel) 40 | { 41 | this->channel = channel; 42 | 43 | if (channel == InputDevice::CHANNEL_1) { 44 | digitalIO.setLevel(DIO_CHANNEL_P1P2_STROBE, IDigitalIO::DIO_LEVEL_LOW); // player 1,2 strobe/latch 45 | digitalIO.setLevel(DIO_CHANNEL_P1P2_CLOCK, IDigitalIO::DIO_LEVEL_LOW); // player 1,2 clock 46 | digitalIO.setLevel(DIO_CHANNEL_P1_VCC, IDigitalIO::DIO_LEVEL_HIGH); // player 1 VCC 47 | } 48 | else { 49 | digitalIO.setLevel(DIO_CHANNEL_P2_VCC, IDigitalIO::DIO_LEVEL_HIGH); // player 2 VCC 50 | // strobe and clock were already initialized with player 1 initialization. 51 | } 52 | 53 | isInitialized = true; 54 | } 55 | 56 | void SNESGamepad::update() 57 | { 58 | assert(isInitialized); 59 | 60 | uint16_t state = getSNESControllerState(); 61 | 62 | if (channel == InputDevice::CHANNEL_1) { 63 | IDigitalIO::DIO_Level_e levelButton1 = digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_B); 64 | if (levelButton1 == IDigitalIO::DIO_LEVEL_HIGH) 65 | { 66 | state |= GPAD_SNES_SELECT; 67 | state |= GPAD_SNES_START; 68 | keyboard->setKeyState(KEY_ESC, 0, EV_KEY); 69 | } 70 | else 71 | { 72 | keyboard->setKeyState(KEY_ESC, 1, EV_KEY); 73 | } 74 | 75 | IDigitalIO::DIO_Level_e levelButton2 = digitalIO.getLevel(IDigitalIO::DIO_CHANNEL_P1_A); 76 | if (levelButton2 == IDigitalIO::DIO_LEVEL_HIGH) 77 | { 78 | state |= GPAD_SNES_SELECT; 79 | state |= GPAD_SNES_B; 80 | keyboard->setKeyState(KEY_F1, 0, EV_KEY); 81 | } 82 | else 83 | { 84 | keyboard->setKeyState(KEY_F1, 1, EV_KEY); 85 | } 86 | keyboard->sync(); 87 | } 88 | 89 | // left-right axis 90 | if ((state & GPAD_SNES_LEFT) == GPAD_SNES_LEFT) { 91 | gamepad->setKeyState(ABS_X, 0, EV_ABS); 92 | } 93 | else if ((state & GPAD_SNES_RIGHT) == GPAD_SNES_RIGHT) { 94 | gamepad->setKeyState(ABS_X, 4, EV_ABS); 95 | } 96 | else { 97 | gamepad->setKeyState(ABS_X, 2, EV_ABS); 98 | } 99 | 100 | // up-down axis 101 | if ((state & GPAD_SNES_UP) == GPAD_SNES_UP) { 102 | gamepad->setKeyState(ABS_Y, 0, EV_ABS); 103 | } 104 | else if ((state & GPAD_SNES_DOWN) == GPAD_SNES_DOWN) { 105 | gamepad->setKeyState(ABS_Y, 4, EV_ABS); 106 | } 107 | else { 108 | gamepad->setKeyState(ABS_Y, 2, EV_ABS); 109 | } 110 | 111 | // buttons 112 | gamepad->setKeyState(BTN_A, (state & GPAD_SNES_A) == GPAD_SNES_A ? 1 : 0, EV_KEY); 113 | gamepad->setKeyState(BTN_B, (state & GPAD_SNES_B) == GPAD_SNES_B ? 1 : 0, EV_KEY); 114 | gamepad->setKeyState(BTN_X, (state & GPAD_SNES_X) == GPAD_SNES_X ? 1 : 0, EV_KEY); 115 | gamepad->setKeyState(BTN_Y, (state & GPAD_SNES_Y) == GPAD_SNES_Y ? 1 : 0, EV_KEY); 116 | gamepad->setKeyState(BTN_TL, (state & GPAD_SNES_L) == GPAD_SNES_L ? 1 : 0, EV_KEY); 117 | gamepad->setKeyState(BTN_TR, (state & GPAD_SNES_R) == GPAD_SNES_R ? 1 : 0, EV_KEY); 118 | gamepad->setKeyState(BTN_START, (state & GPAD_SNES_START) == GPAD_SNES_START ? 1 : 0, EV_KEY); 119 | gamepad->setKeyState(BTN_SELECT, (state & GPAD_SNES_SELECT) == GPAD_SNES_SELECT ? 1 : 0, EV_KEY); 120 | gamepad->sync(); 121 | } 122 | 123 | uint16_t SNESGamepad::getSNESControllerState() 124 | { 125 | const std::chrono::microseconds dura(STROBEDELAY_US); 126 | uint16_t state = 0u; 127 | 128 | digitalIO.setLevel(DIO_CHANNEL_P1P2_STROBE, IDigitalIO::DIO_LEVEL_HIGH); 129 | std::this_thread::sleep_for(2 * dura); 130 | digitalIO.setLevel(DIO_CHANNEL_P1P2_STROBE, IDigitalIO::DIO_LEVEL_LOW); 131 | std::this_thread::sleep_for(2 * dura); 132 | 133 | for (uint8_t i = 0u; i < NUMBER_OF_BUTTONS; i++) { 134 | IDigitalIO::DIO_Level_e curpin; 135 | if ((channel == InputDevice::CHANNEL_1) || (channel == InputDevice::CHANNEL_3)) { 136 | curpin = digitalIO.getLevel(DIO_CHANNEL_P1_DATA); 137 | } 138 | else if ((channel == InputDevice::CHANNEL_2) || (channel == InputDevice::CHANNEL_4)){ 139 | curpin = digitalIO.getLevel(DIO_CHANNEL_P2_DATA); 140 | } 141 | else { 142 | throw std::runtime_error("Unknown channel."); 143 | } 144 | 145 | if (curpin == IDigitalIO::DIO_LEVEL_HIGH) { 146 | state |= (1u << i); 147 | } 148 | 149 | digitalIO.setLevel(DIO_CHANNEL_P1P2_CLOCK, IDigitalIO::DIO_LEVEL_HIGH); 150 | std::this_thread::sleep_for(dura); 151 | digitalIO.setLevel(DIO_CHANNEL_P1P2_CLOCK, IDigitalIO::DIO_LEVEL_LOW); 152 | std::this_thread::sleep_for(dura); 153 | } 154 | 155 | return state; 156 | } 157 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/SNESGamepad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef SNESGAMEPAD_H 24 | #define SNESGAMEPAD_H 25 | 26 | #include 27 | #include 28 | #include "hal/DigitalIO.h" 29 | #include "InputDevice.h" 30 | 31 | class SNESGamepad: public InputDevice 32 | { 33 | public: 34 | /* bit masks for checking the button states for SNES controllers */ 35 | static const uint16_t GPAD_SNES_B = 0x01; 36 | static const uint16_t GPAD_SNES_Y = 0x02; 37 | static const uint16_t GPAD_SNES_SELECT = 0x04; 38 | static const uint16_t GPAD_SNES_START = 0x08; 39 | static const uint16_t GPAD_SNES_UP = 0x10; 40 | static const uint16_t GPAD_SNES_DOWN = 0x20; 41 | static const uint16_t GPAD_SNES_LEFT = 0x40; 42 | static const uint16_t GPAD_SNES_RIGHT = 0x80; 43 | static const uint16_t GPAD_SNES_A = 0x100; 44 | static const uint16_t GPAD_SNES_X = 0x200; 45 | static const uint16_t GPAD_SNES_L = 0x400; 46 | static const uint16_t GPAD_SNES_R = 0x800; 47 | static const uint16_t GPAD_SNES_RESET = 0x1000; 48 | 49 | SNESGamepad(IUInputFactory& uiFactoryRef, IDigitalIO& digitalIORef); 50 | ~SNESGamepad() = default; 51 | 52 | void initialize(InputDevice::Channel_e channel) override; 53 | void update() override; 54 | 55 | private: 56 | static const uint32_t STROBEDELAY_US = 4u; 57 | static const uint32_t NUMBER_OF_BUTTONS = 12u; 58 | 59 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P1_VCC = IDigitalIO::DIO_CHANNEL_P2_RIGHT; 60 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P2_VCC = IDigitalIO::DIO_CHANNEL_P2_LEFT; 61 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P1P2_STROBE = IDigitalIO::DIO_CHANNEL_P2_UP; 62 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P1P2_CLOCK = IDigitalIO::DIO_CHANNEL_P2_DOWN; 63 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P1_DATA = IDigitalIO::DIO_CHANNEL_P2_SW1; 64 | static const IDigitalIO::DIO_Channel_e DIO_CHANNEL_P2_DATA = IDigitalIO::DIO_CHANNEL_P2_SW2; 65 | 66 | IUInputFactory& uiFactory; 67 | IDigitalIO& digitalIO; 68 | 69 | bool isInitialized; 70 | InputDevice::Channel_e channel; 71 | IUInputDevice* gamepad; 72 | IUInputDevice* keyboard; 73 | 74 | uint16_t getSNESControllerState(); 75 | }; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/controlblock/gamepads/SaturnGamepad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_SATURNGAMEPAD_H 24 | #define CONTROLBLOCKSERVICE2_SATURNGAMEPAD_H 25 | 26 | #include 27 | #include "InputDevice.h" 28 | #include "uinput/IUInputDevice.h" 29 | #include "uinput/IUInputFactory.h" 30 | #include "hal/IDigitalIO.h" 31 | 32 | /** 33 | * Models a Sega Saturn gamepad. Polls the controller and sends the corresponding 34 | * user inputs to the uinput device. 35 | * Followed instructions from https://www.acidmods.com/forum/index.php/topic,42733.0.html 36 | */ 37 | class SaturnGamepad: public InputDevice 38 | { 39 | public: 40 | /** 41 | * Constructor 42 | * @param uiFactory 43 | * @param digitalInRef 44 | * @param digitalOutRef 45 | */ 46 | SaturnGamepad(IUInputFactory& uiFactory, IDigitalIO& digitalIORef); 47 | 48 | /** 49 | * Default Destructor 50 | */ 51 | virtual ~SaturnGamepad() = default; 52 | 53 | /** 54 | * Implements \ref InputDevice::initialize 55 | */ 56 | void initialize(InputDevice::Channel_e channel) override; 57 | 58 | /** 59 | * Implement \ref InputDevice::update() 60 | */ 61 | void update() override; 62 | 63 | private: 64 | // Controller Button Flags 65 | static const uint32_t SATURNBTN_UP = 0x0001; 66 | static const uint32_t SATURNBTN_DOWN = 0x0002; 67 | static const uint32_t SATURNBTN_LEFT = 0x0004; 68 | static const uint32_t SATURNBTN_RIGHT = 0x0008; 69 | static const uint32_t SATURNBTN_A = 0x0010; 70 | static const uint32_t SATURNBTN_B = 0x0020; 71 | static const uint32_t SATURNBTN_C = 0x0040; 72 | static const uint32_t SATURNBTN_X = 0x0080; 73 | static const uint32_t SATURNBTN_Y = 0x0100; 74 | static const uint32_t SATURNBTN_Z = 0x0200; 75 | static const uint32_t SATURNBTN_LT = 0x0400; 76 | static const uint32_t SATURNBTN_RT = 0x0800; 77 | static const uint32_t SATURNBTN_START = 0x1000; 78 | 79 | static const IDigitalIO::DIO_Channel_e CHN_SELECT0[]; 80 | static const IDigitalIO::DIO_Channel_e CHN_SELECT1[]; 81 | 82 | static const uint32_t SHORTDELAY = 20u; 83 | static const uint32_t LONGDELAY = 1000u; 84 | 85 | InputDevice::Channel_e channel; 86 | IUInputDevice* gamepad; 87 | IDigitalIO& digitalIO; 88 | 89 | uint32_t currentState; 90 | uint32_t lastState; 91 | uint32_t playerIndex_; 92 | 93 | void readButtons(); 94 | void sendStates(); 95 | }; 96 | 97 | #endif //CONTROLBLOCKSERVICE2_SATURNGAMEPAD_H 98 | -------------------------------------------------------------------------------- /src/controlblock/hal/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(controlblock-hal STATIC 2 | DigitalIO.cpp 3 | HALFactory.cpp 4 | mcp23s17pi.cpp 5 | ) 6 | -------------------------------------------------------------------------------- /src/controlblock/hal/DigitalIO.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_DIGITALIO_H 24 | #define CONTROLBLOCKSERVICE2_DIGITALIO_H 25 | 26 | #include "hal/IDigitalIO.h" 27 | 28 | class DigitalIO : public IDigitalIO { 29 | public: 30 | DigitalIO(MCP23S17PI& mcp1ref, MCP23S17PI& mcp2ref); 31 | 32 | void configureDevice(DIO_Device mode) override; 33 | 34 | DIO_Level_e getLevel(DIO_Channel_e channel) override; 35 | 36 | /** 37 | * @brief Sets a logical signal level of a given channel 38 | * 39 | * @param channel The channel whose signal level should be set 40 | * @param level The signal level to be set 41 | */ 42 | void setLevel(DIO_Channel_e channel, DIO_Level_e level) override; 43 | 44 | private: 45 | MCP23S17PI& mcp1; 46 | MCP23S17PI& mcp2; 47 | 48 | }; 49 | 50 | #endif //CONTROLBLOCKSERVICE2_DIGITALIO_H 51 | -------------------------------------------------------------------------------- /src/controlblock/hal/HALFactory.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "HALFactory.h" 24 | 25 | HALFactory::HALFactory() : mcp23s17_1(MCP23S17PI::CHIPSELECT_0, 0b000), 26 | mcp23s17_2(MCP23S17PI::CHIPSELECT_0, 0b001), 27 | mcp23s17_3(MCP23S17PI::CHIPSELECT_0, 0b010), 28 | mcp23s17_4(MCP23S17PI::CHIPSELECT_0, 0b011) { 29 | } 30 | 31 | void HALFactory::initialize() { 32 | MCP23S17PI::begin(); 33 | } 34 | 35 | void HALFactory::deinitialize() { 36 | MCP23S17PI::end(); 37 | } 38 | 39 | HALFactory::~HALFactory() = default; 40 | 41 | MCP23S17PI *HALFactory::getMCP23S17(MCPChannel channel) { 42 | if (channel == MCPCHANNEL_1) { 43 | return &mcp23s17_1; 44 | } else if (channel == MCPCHANNEL_2) { 45 | return &mcp23s17_2; 46 | } else if (channel == MCPCHANNEL_3) { 47 | return &mcp23s17_3; 48 | } else { 49 | return &mcp23s17_4; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/controlblock/hal/HALFactory.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef HALFACTORY_H 24 | #define HALFACTORY_H 25 | 26 | #include "hal/mcp23s17pi.h" 27 | 28 | class HALFactory 29 | { 30 | public: 31 | /** 32 | * Indicates individual MCP components 33 | */ 34 | enum MCPChannel 35 | { 36 | MCPCHANNEL_1 = 0, //!< MCPCHANNEL_1 37 | MCPCHANNEL_2, //!< MCPCHANNEL_2 38 | MCPCHANNEL_3, //!< MCPCHANNEL_3 39 | MCPCHANNEL_4, //!< MCPCHANNEL_4 40 | 41 | MCPCHANNEL_NUMBERELEMENTS 42 | }; 43 | 44 | /** 45 | * Destructor 46 | */ 47 | ~HALFactory(); 48 | 49 | /** 50 | * Returns a singleton instance of the HAL factory 51 | * @return 52 | */ 53 | static HALFactory& getInstance() 54 | { 55 | static HALFactory halFactory = HALFactory(); 56 | return halFactory; 57 | } 58 | 59 | static void initialize(); 60 | 61 | static void deinitialize(); 62 | 63 | /** 64 | * Returns the singleton instance that corresponds to 65 | * the given channel 66 | * @param channel - The channel whose MCP instance should be returned 67 | * @return Reference to the MCP instance 68 | */ 69 | MCP23S17PI* getMCP23S17(MCPChannel channel); 70 | 71 | private: 72 | HALFactory(); 73 | 74 | MCP23S17PI mcp23s17_1; 75 | MCP23S17PI mcp23s17_2; 76 | MCP23S17PI mcp23s17_3; 77 | MCP23S17PI mcp23s17_4; 78 | }; 79 | 80 | #endif // HALFACTORY_H 81 | -------------------------------------------------------------------------------- /src/controlblock/hal/IDigitalIO.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_IDIGITALIO_H 24 | #define CONTROLBLOCKSERVICE2_IDIGITALIO_H 25 | 26 | #include "hal/mcp23s17pi.h" 27 | #include 28 | 29 | class IDigitalIO 30 | { 31 | public: 32 | /** 33 | * Digital input channel identifiers 34 | * The mapping to the MCP ICs is as follows: 35 | * 36 | * P1 RIGHT : mcp1 (0) 37 | * P1 LEFT : mcp1 (1) 38 | * P1 UP : mcp1 (2) 39 | * P1 DOWN : mcp1 (3) 40 | * P1 SW1 : mcp1 (4) 41 | * P1 SW2 : mcp1 (5) 42 | * P1 SW3 : mcp1 (6) 43 | * P1 SW4 : mcp1 (7) 44 | * P1 SW5 : mcp2 (0) 45 | * P1 SW6 : mcp2 (1) 46 | * P1 SW7 : mcp2 (2) 47 | * P1 SW8 : mcp2 (3) 48 | * P1 START : mcp2 (4) 49 | * P1 COIN : mcp2 (5) 50 | * P1 A : mcp2 (6) 51 | * P1 B : mcp2 (7) 52 | * P2 RIGHT : mcp1 (15) 53 | * P2 LEFT : mcp1 (14) 54 | * P2 UP : mcp1 (13) 55 | * P2 DOWN : mcp1 (12) 56 | * P2 SW1 : mcp1 (11) 57 | * P2 SW2 : mcp1 (10) 58 | * P2 SW3 : mcp1 (9) 59 | * P2 SW4 : mcp1 (8) 60 | * P2 SW5 : mcp2 (15) 61 | * P2 SW6 : mcp2 (14) 62 | * P2 SW7 : mcp2 (13) 63 | * P2 SW8 : mcp2 (12) 64 | * P2 START : mcp2 (11) 65 | * P2 COIN : mcp2 (10) 66 | * 67 | */ 68 | enum DIO_Channel_e 69 | { 70 | DIO_CHANNEL_P1_RIGHT = 0, 71 | DIO_CHANNEL_P1_LEFT, 72 | DIO_CHANNEL_P1_UP, 73 | DIO_CHANNEL_P1_DOWN, 74 | DIO_CHANNEL_P1_SW1, 75 | DIO_CHANNEL_P1_SW2, 76 | DIO_CHANNEL_P1_SW3, 77 | DIO_CHANNEL_P1_SW4, 78 | DIO_CHANNEL_P1_SW5, 79 | DIO_CHANNEL_P1_SW6, 80 | DIO_CHANNEL_P1_SW7, 81 | DIO_CHANNEL_P1_SW8, 82 | DIO_CHANNEL_P1_START, 83 | DIO_CHANNEL_P1_COIN, 84 | DIO_CHANNEL_P1_A, 85 | DIO_CHANNEL_P1_B, 86 | DIO_CHANNEL_P2_RIGHT, 87 | DIO_CHANNEL_P2_LEFT, 88 | DIO_CHANNEL_P2_UP, 89 | DIO_CHANNEL_P2_DOWN, 90 | DIO_CHANNEL_P2_SW1, 91 | DIO_CHANNEL_P2_SW2, 92 | DIO_CHANNEL_P2_SW3, 93 | DIO_CHANNEL_P2_SW4, 94 | DIO_CHANNEL_P2_SW5, 95 | DIO_CHANNEL_P2_SW6, 96 | DIO_CHANNEL_P2_SW7, 97 | DIO_CHANNEL_P2_SW8, 98 | DIO_CHANNEL_P2_START, 99 | DIO_CHANNEL_P2_COIN, 100 | DIO_CHANNEL_P2_A, 101 | DIO_CHANNEL_P2_B, 102 | DIO_CHANNEL_FROMPOWERSWITCH, 103 | DIO_CHANNEL_TOPOWERSWITCH, 104 | DIO_CHANNEL_GENESIS_P1_SELECT, //!< Select signal for Genesis controller, player 1 105 | DIO_CHANNEL_GENESIS_P2_SELECT, //!< Select signal for Genesis controller, player 2 106 | }; 107 | 108 | /** 109 | * Board identifiers in case of several stacked ControlBLocks 110 | */ 111 | enum BoardNumber_e 112 | { 113 | BOARD_0 = 0, //!< BOARD_0 114 | BOARD_1 //!< BOARD_1 115 | }; 116 | 117 | /** 118 | * Logical input level identifiers 119 | */ 120 | enum DIO_Level_e 121 | { 122 | DIO_LEVEL_LOW = 0, 123 | DIO_LEVEL_HIGH 124 | }; 125 | 126 | /** 127 | * Logical device identifiers for default configurations 128 | */ 129 | enum DIO_Device 130 | { 131 | DIO_DEVICE_ALLIN = 0, //!< Device with all ports set as input ports 132 | DIO_DEVICE_POWERSWITCH, //!< PowerBlock communication pins 133 | DIO_DEVICE_SNES, //!< SNES controller device 134 | DIO_DEVICE_GENESIS, //!< Genesis controller device 135 | DIO_DEVICE_SATURN, 136 | }; 137 | 138 | static const uint8_t TOTAL_NUMBER_OF_CHANNELS = 16u; 139 | 140 | virtual void configureDevice(DIO_Device mode) = 0; 141 | 142 | virtual DIO_Level_e getLevel(DIO_Channel_e channel) = 0; 143 | 144 | /** 145 | * @brief Sets a logical signal level of a given channel 146 | * 147 | * @param channel The channel whose signal level should be set 148 | * @param level The signal level to be set 149 | */ 150 | virtual void setLevel(DIO_Channel_e channel, DIO_Level_e level) = 0; 151 | }; 152 | 153 | #endif //CONTROLBLOCKSERVICE2_IDIGITALIO_H 154 | -------------------------------------------------------------------------------- /src/controlblock/hal/mcp23s17pi.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "mcp23s17pi.h" 24 | #include //Needed for SPI port 25 | #include 26 | 27 | bool MCP23S17PI::isBCM2835Initialized_ = false; 28 | int MCP23S17PI::spi_cs0_fd_ = 0; 29 | 30 | MCP23S17PI::MCP23S17PI(ChipSelectPin chipSelectPin, uint8_t deviceID) : 31 | deviceID_(deviceID), 32 | GPIOA_(0), 33 | GPIOB_(0), 34 | IODIRA_(0), 35 | IODIRB_(0), 36 | GPPUA_(0), 37 | GPPUB_(0) { 38 | writeRegister(MCP23S17PI_IOCON, IOCON_INIT); 39 | } 40 | 41 | MCP23S17PI::~MCP23S17PI() = default; 42 | 43 | void MCP23S17PI::begin() { 44 | if (!isBCM2835Initialized_) { 45 | spi_cs0_fd_ = mcp23s17_open(0, 0); 46 | 47 | isBCM2835Initialized_ = true; 48 | } 49 | } 50 | 51 | void MCP23S17PI::setPinMode(uint8_t pin, Direction dir) { 52 | if (pin < 16) { 53 | uint8_t reg; 54 | uint8_t *data; 55 | 56 | if (pin < 8) { 57 | reg = MCP23S17PI_IODIRA; 58 | data = &IODIRA_; 59 | } else { 60 | reg = MCP23S17PI_IODIRB; 61 | pin &= 0x07; 62 | data = &IODIRB_; 63 | } 64 | 65 | if (DIR_INPUT == dir) { 66 | *data |= (1 << pin); 67 | } else { 68 | *data &= (~(1 << pin)); 69 | } 70 | writeRegister(reg, *data); 71 | } 72 | } 73 | 74 | void MCP23S17PI::setPullupMode(uint8_t pin, Pullup mode) { 75 | if (pin < 16) { 76 | uint8_t reg; 77 | uint8_t *data; 78 | 79 | if (pin < 8) { 80 | reg = MCP23S17PI_GPPUA; 81 | data = &GPPUA_; 82 | } else { 83 | reg = MCP23S17PI_GPPUB; 84 | pin &= 0x07; 85 | data = &GPPUB_; 86 | } 87 | 88 | if (PULLUP_ENABLED == mode) { 89 | *data |= (1 << pin); 90 | } else { 91 | *data &= (~(1 << pin)); 92 | } 93 | writeRegister(reg, *data); 94 | } 95 | } 96 | 97 | MCP23S17PI::Level MCP23S17PI::digitalRead(uint8_t pin) { 98 | if (pin < 8) { 99 | GPIOA_ = readRegister(MCP23S17PI_GPIOA); 100 | 101 | if ((GPIOA_ & (1 << pin)) != 0) { 102 | return LEVEL_HIGH; 103 | } else { 104 | return LEVEL_LOW; 105 | } 106 | } else if (pin < 16) { 107 | GPIOB_ = readRegister(MCP23S17PI_GPIOB); 108 | pin &= 0x07; 109 | if ((GPIOB_ & (1 << pin)) != 0) { 110 | return LEVEL_HIGH; 111 | } else { 112 | return LEVEL_LOW; 113 | } 114 | } else { 115 | throw std::runtime_error("Error while MCP23S17PI::digitalRead call."); 116 | } 117 | } 118 | 119 | void MCP23S17PI::digitalWrite(uint8_t pin, Level level) { 120 | if (pin < 16) { 121 | uint8_t reg; 122 | uint8_t *data; 123 | 124 | if (pin < 8) { 125 | reg = MCP23S17PI_GPIOA; 126 | data = &GPIOA_; 127 | } else { 128 | reg = MCP23S17PI_GPIOB; 129 | pin &= 0x07; 130 | data = &GPIOB_; 131 | } 132 | 133 | if (LEVEL_HIGH == level) { 134 | *data |= 1 << pin; 135 | } else { 136 | *data &= ~(1 << pin); 137 | } 138 | writeRegister(reg, *data); 139 | } 140 | } 141 | 142 | void MCP23S17PI::writeGPIO(uint16_t data) { 143 | writeRegisterWord(MCP23S17PI_GPIOA, data); 144 | } 145 | 146 | uint16_t MCP23S17PI::readGPIO() { 147 | return readRegisterWord(MCP23S17PI_GPIOA); 148 | } 149 | 150 | void MCP23S17PI::writeRegister(uint8_t regAddress, uint8_t data) { 151 | mcp23s17_write_reg(data, regAddress, deviceID_, spi_cs0_fd_); 152 | } 153 | 154 | void MCP23S17PI::writeRegisterWord(const uint8_t ®Address, uint16_t &data) { 155 | writeRegister(regAddress, static_cast(data)); 156 | writeRegister(regAddress + 1u, static_cast(data >> 8u)); 157 | } 158 | 159 | uint8_t MCP23S17PI::readRegister(uint8_t regAddress) { 160 | return mcp23s17_read_reg(regAddress, deviceID_, spi_cs0_fd_); 161 | } 162 | 163 | uint16_t MCP23S17PI::readRegisterWord(uint8_t regAddress) { 164 | char buffer[2]; 165 | 166 | buffer[0] = readRegister(regAddress); 167 | buffer[1] = readRegister(regAddress + 1); 168 | 169 | return (uint16_t)(((uint16_t)(buffer[1]) << 8) | (uint16_t) buffer[0]); 170 | } 171 | 172 | void MCP23S17PI::end() { 173 | close(spi_cs0_fd_); 174 | } 175 | -------------------------------------------------------------------------------- /src/controlblock/hal/mcp23s17pi.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef MCP23S17PI_H 24 | #define MCP23S17PI_H 25 | 26 | #include 27 | //#include "bcm2835.h" 28 | #include "mcp23s17.h" 29 | 30 | /** 31 | * @brief This class abstracts a port expander MCP23S17 32 | */ 33 | class MCP23S17PI { 34 | public: 35 | /** 36 | * @brief Chip select pin identifiers 37 | */ 38 | enum ChipSelectPin { 39 | CHIPSELECT_0 = 0, //!< Chip select pin CS0 40 | CHIPSELECT_1 = 1 //!< Chip select pin CS1 41 | }; 42 | 43 | /** 44 | * Port direction identifiers 45 | */ 46 | enum Direction { 47 | DIR_INPUT = 0, //!< Input direction 48 | DIR_OUTPUT //!< Output direction 49 | }; 50 | 51 | /** 52 | * @brief Logical signal level identifiers 53 | */ 54 | enum Level { 55 | LEVEL_LOW = 0, //!< Logical low 56 | LEVEL_HIGH //!< Logical high 57 | }; 58 | 59 | /** 60 | * Pullup mode identifiers 61 | */ 62 | enum Pullup { 63 | PULLUP_ENABLED = 0, //!< Pullup enabled 64 | PULLUP_DISABLED //!< Pullup disabled 65 | }; 66 | 67 | /** 68 | * @brief Constructor 69 | * 70 | * @param chipSelectPin The chip-select pin of this expander 71 | * @param deviceID The device identifier, i.e., the address, of this expander 72 | */ 73 | explicit MCP23S17PI(ChipSelectPin chipSelectPin, uint8_t deviceID); 74 | 75 | ~MCP23S17PI(); 76 | 77 | /** 78 | * @brief Initializes the communication interface for the MCP23S17 instance 79 | */ 80 | static void begin(); 81 | 82 | /** 83 | * @brief Deinitializes the communication interface for the MCP23S17 instance 84 | */ 85 | static void end(); 86 | 87 | /** 88 | * @brief Sets the port direction of a given pin 89 | * 90 | * @param pin The pin whose direction should be set 91 | * @param dir The port direction to be set 92 | */ 93 | void setPinMode(uint8_t pin, Direction dir); 94 | 95 | /** 96 | * @brief Sets the pullup mode of a given pin 97 | * 98 | * @param pin The pin whose pullup mode should be set 99 | * @param mode The pullup mode to be set 100 | */ 101 | void setPullupMode(uint8_t pin, Pullup mode); 102 | 103 | /** 104 | * @brief Sets a given signal level for a given pin number 105 | * @details The pins numbers are organized in this way: 106 | * /-------\ 107 | * 0 | | 15 108 | * 1 | | 14 109 | * 2 | | 13 110 | * 3 | | 12 111 | * 4 | | 11 112 | * 5 | | 10 113 | * 6 | | 9 114 | * 7 | o| 8 115 | * \-------/ 116 | * 117 | * @param pin The pin number 118 | * @param val The signal level 119 | */ 120 | void digitalWrite(uint8_t pin, Level val); 121 | 122 | /** 123 | * @brief Reads the signal level of a given pin number 124 | * @details The pins numbers are organized in this way: 125 | * /-------\ 126 | * 0 | | 15 127 | * 1 | | 14 128 | * 2 | | 13 129 | * 3 | | 12 130 | * 4 | | 11 131 | * 5 | | 10 132 | * 6 | | 9 133 | * 7 | o| 8 134 | * \-------/ 135 | * 136 | * @param pin The pin number 137 | * @return The signal level 138 | */ 139 | Level digitalRead(uint8_t pin); 140 | 141 | /** 142 | * @brief Sets the port signal levels for all pins given the 16-bit data value 143 | * 144 | * @param data The data to be set. Bit 0 corresponds to pin 0, bit 1 to pin 1, and so on. 145 | */ 146 | void writeGPIO(uint16_t data); 147 | 148 | /** 149 | * @brief Returns the port signal level for all pins in a 16-bit value. 150 | * @return The port levels. Bit 0 corresponds to pin 0, bit 1 to pin 1, and so on. 151 | */ 152 | uint16_t readGPIO(); 153 | 154 | private: 155 | static const uint8_t MCP23S17_DEFAULT_SLAVE_ADDRESS = 0x00; 156 | static const uint8_t MCP23S17PI_IODIRA = 0x00; 157 | static const uint8_t MCP23S17PI_IODIRB = 0x01; 158 | static const uint8_t MCP23S17PI_IPOLA = 0x2; 159 | static const uint8_t MCP23S17PI_IPOLB = 0x3; 160 | static const uint8_t MCP23S17PI_GPIOA = 0x12; 161 | static const uint8_t MCP23S17PI_GPIOB = 0x13; 162 | static const uint8_t MCP23S17PI_OLATA = 0x14; 163 | static const uint8_t MCP23S17PI_OLATB = 0x15; 164 | static const uint8_t MCP23S17PI_IOCON = 0x0A; 165 | static const uint8_t MCP23S17PI_GPPUA = 0x0C; 166 | static const uint8_t MCP23S17PI_GPPUB = 0x0D; 167 | 168 | // Bits in the IOCON register 169 | static const uint8_t IOCON_UNUSED = 0x01; 170 | static const uint8_t IOCON_INTPOL = 0x02; 171 | static const uint8_t IOCON_ODR = 0x04; 172 | static const uint8_t IOCON_HAEN = 0x08; 173 | static const uint8_t IOCON_DISSLW = 0x10; 174 | static const uint8_t IOCON_SEQOP = 0x20; 175 | static const uint8_t IOCON_MIRROR = 0x40; 176 | static const uint8_t IOCON_BANK_MODE = 0x80; 177 | 178 | static const uint8_t MCP23S08_CMD_WRITE = 0x40; 179 | static const uint8_t MCP23S08_CMD_READ = 0x41; 180 | 181 | // Default initialization mode 182 | static const uint8_t IOCON_INIT = 0x28; // IOCON_SEQOP and IOCON_HAEN from above 183 | 184 | static bool isBCM2835Initialized_; 185 | static int spi_cs0_fd_; 186 | 187 | ChipSelectPin chipSelectPin_; 188 | uint8_t deviceID_; 189 | 190 | uint8_t GPIOA_; 191 | uint8_t GPIOB_; 192 | uint8_t IODIRA_; 193 | uint8_t IODIRB_; 194 | uint8_t GPPUA_; 195 | uint8_t GPPUB_; 196 | 197 | void writeRegister(uint8_t regaddress, uint8_t val); 198 | void writeRegisterWord(const uint8_t ®Address, uint16_t &data); 199 | uint8_t readRegister(uint8_t regaddress); 200 | uint16_t readRegisterWord(uint8_t regaddress); 201 | 202 | }; 203 | #endif 204 | -------------------------------------------------------------------------------- /src/controlblock/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "hal/mcp23s17pi.h" 28 | 29 | #include "app/Logger.h" 30 | #include "app/ControlBlock.h" 31 | #include "config/ControlBlockConfiguration.h" 32 | #include "uinput/UInputFactory.h" 33 | #include "gamepads/GamepadFactory.h" 34 | 35 | static const int kWaitingTime_ms = 25; 36 | static volatile sig_atomic_t doRun = 1; 37 | 38 | extern "C" { 39 | void sig_handler(int signo) 40 | { 41 | if ((signo == SIGINT) || (signo == SIGQUIT) || (signo == SIGABRT) || (signo == SIGTERM)) { 42 | printf("[ControlBlockService] Releasing input devices.\n"); 43 | doRun = 0; 44 | } 45 | } 46 | } 47 | 48 | void register_signalhandlers() 49 | { 50 | /* Register signal handlers */ 51 | if (signal(SIGINT, sig_handler) == SIG_ERR) { 52 | printf("\n[ControlBlockService] Cannot catch SIGINT\n"); 53 | } 54 | if (signal(SIGQUIT, sig_handler) == SIG_ERR) { 55 | printf("\n[ControlBlockService] Cannot catch SIGQUIT\n"); 56 | } 57 | if (signal(SIGABRT, sig_handler) == SIG_ERR) { 58 | printf("\n[ControlBlockService] Cannot catch SIGABRT\n"); 59 | } 60 | if (signal(SIGTERM, sig_handler) == SIG_ERR) { 61 | printf("\n[ControlBlockService] Cannot catch SIGTERM\n"); 62 | } 63 | } 64 | 65 | int main(int argc, char** argv) 66 | { 67 | Logger::init(); 68 | Logger::logMessage("Starting ControlBlock driver."); 69 | 70 | register_signalhandlers(); 71 | MCP23S17PI::begin(); 72 | 73 | try { 74 | UInputFactory uiFactory; 75 | ControlBlockConfiguration config; 76 | GamepadFactory gamepadFactory(uiFactory); 77 | 78 | ControlBlock controlBlock{uiFactory, config, gamepadFactory}; 79 | 80 | Logger::logMessage("Starting gamepad polling ... "); 81 | while (doRun) { 82 | controlBlock.update(); 83 | std::this_thread::sleep_for(std::chrono::milliseconds(kWaitingTime_ms)); 84 | } 85 | } 86 | catch (std::exception& exc) { 87 | std::cout << "Error while running main loop. Error number: " << exc.what() << std::endl; 88 | } 89 | 90 | MCP23S17PI::end(); 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /src/controlblock/uinput/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(controlblock-uinput STATIC 2 | UInputGamepadArcade.cpp 3 | UInputDevice.cpp 4 | UInputGamepadSNES.cpp 5 | UInputGamepadNES.cpp 6 | UInputGamepadGenesis.cpp 7 | UInputGamepadSaturn.cpp 8 | UInputKeyboard.cpp 9 | UInputFactory.cpp 10 | ) 11 | -------------------------------------------------------------------------------- /src/controlblock/uinput/IUInputDevice.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_IUINPUTDEVICE_H 24 | #define CONTROLBLOCKSERVICE2_IUINPUTDEVICE_H 25 | 26 | #include "UInputEventCodes.h" 27 | 28 | class IUInputDevice { 29 | public: 30 | enum DeviceType 31 | { 32 | TYPE_GAMEPAD_ARCADE = 0, 33 | TYPE_GAMEPAD_SNES, 34 | TYPE_GAMEPAD_NES, 35 | TYPE_GAMEPAD_GENESIS, 36 | TYPE_GAMEPAD_SATURN, 37 | TYPE_KEYBOARD 38 | }; 39 | 40 | /** 41 | * Constructor 42 | */ 43 | IUInputDevice() = default; 44 | 45 | /** 46 | * Destructor 47 | */ 48 | virtual ~IUInputDevice() = default; 49 | 50 | /** 51 | * Sets the state of a key. To let the changes have an effect the method \ref sync() needs to be called. 52 | * @param keycode - The keycode 53 | * @param keyvalue 54 | * @param evtype 55 | */ 56 | virtual void setKeyState(uint16_t keycode, int16_t keyvalue, uint16_t evtype) = 0; 57 | 58 | /** 59 | * Sends all recent key state changes via \ref setKeyState() to the uinput device. 60 | */ 61 | virtual void sync() = 0; 62 | }; 63 | 64 | #endif //CONTROLBLOCKSERVICE2_IUINPUTDEVICE_H 65 | -------------------------------------------------------------------------------- /src/controlblock/uinput/IUInputFactory.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_IUINPUTFACTORY_H 24 | #define CONTROLBLOCKSERVICE2_IUINPUTFACTORY_H 25 | 26 | #include 27 | #include "IUInputDevice.h" 28 | 29 | class IUInputFactory 30 | { 31 | public: 32 | virtual IUInputDevice* getUInputDevice(IUInputDevice::DeviceType type) = 0; 33 | }; 34 | 35 | #endif //CONTROLBLOCKSERVICE2_IUINPUTFACTORY_H 36 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputDevice.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "UInputDevice.h" 24 | 25 | UInputDevice::UInputDevice() : 26 | m_fileDescriptor(0u) 27 | { 28 | } 29 | 30 | UInputDevice::~UInputDevice() 31 | { 32 | releaseHandle(); 33 | } 34 | 35 | int32_t UInputDevice::getHandle() 36 | { 37 | int32_t handle = open("/dev/uinput", O_WRONLY | O_NDELAY); 38 | if (handle == 0) { 39 | throw std::runtime_error("Unable to open /dev/uinput"); 40 | } 41 | return handle; 42 | } 43 | 44 | void UInputDevice::releaseHandle() 45 | { 46 | ioctl(m_fileDescriptor, UI_DEV_DESTROY); 47 | close(m_fileDescriptor); 48 | } 49 | 50 | void UInputDevice::setKeyState(uint16_t keycode, int16_t keyvalue, uint16_t evtype) 51 | { 52 | struct input_event event; 53 | gettimeofday(&event.time, nullptr); 54 | 55 | event.type = evtype; 56 | event.code = keycode; 57 | event.value = keyvalue; 58 | 59 | if (write(m_fileDescriptor, &event, sizeof(event)) < 0) { 60 | throw std::runtime_error("Simulate key error."); 61 | } 62 | } 63 | 64 | void UInputDevice::sync() 65 | { 66 | struct input_event event; 67 | gettimeofday(&event.time, nullptr); 68 | 69 | event.type = EV_SYN; 70 | event.code = SYN_REPORT; 71 | event.value = 0; 72 | 73 | if (write(m_fileDescriptor, &event, sizeof(event)) < 0) { 74 | throw std::runtime_error("Simulate key error."); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputDevice.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef UINTPUTDEVICE_H 24 | #define UINTPUTDEVICE_H 25 | 26 | #include 27 | #include 28 | #include "IUInputDevice.h" 29 | 30 | extern "C" 31 | { 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | } 39 | 40 | class UInputDevice : public IUInputDevice 41 | { 42 | public: 43 | /** 44 | * Constructor 45 | */ 46 | UInputDevice(); 47 | 48 | /** 49 | * Destructor 50 | */ 51 | ~UInputDevice() override; 52 | 53 | /** 54 | * Sets the state of a key. To let the changes have an effect the method \ref sync() needs to be called. 55 | * @param keycode - The keycode 56 | * @param keyvalue 57 | * @param evtype 58 | */ 59 | void setKeyState(uint16_t keycode, int16_t keyvalue, uint16_t evtype) override; 60 | 61 | /** 62 | * Sends all recent key state changes via \ref setKeyState() to the uinput device. 63 | */ 64 | void sync() override; 65 | 66 | protected: 67 | int32_t m_fileDescriptor; 68 | static int32_t getHandle(); 69 | void releaseHandle(); 70 | }; 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputFactory.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "UInputFactory.h" 24 | 25 | #include "UInputGamepadArcade.h" 26 | #include "UInputKeyboard.h" 27 | #include "UInputGamepadSNES.h" 28 | #include "UInputGamepadNES.h" 29 | #include "UInputGamepadGenesis.h" 30 | #include "UInputGamepadSaturn.h" 31 | 32 | IUInputDevice* UInputFactory::getUInputDevice(IUInputDevice::DeviceType type) 33 | { 34 | switch (type) { 35 | case IUInputDevice::TYPE_GAMEPAD_ARCADE: 36 | return new UInputGamepadArcade(); 37 | case IUInputDevice::TYPE_GAMEPAD_SNES: 38 | return new UInputGamepadSNES(); 39 | case IUInputDevice::TYPE_GAMEPAD_NES: 40 | return new UInputGamepadNES(); 41 | case IUInputDevice::TYPE_GAMEPAD_GENESIS: 42 | return new UInputGamepadGenesis(); 43 | case IUInputDevice::TYPE_GAMEPAD_SATURN: 44 | return new UInputGamepadSaturn(); 45 | case IUInputDevice::TYPE_KEYBOARD: 46 | return new UInputKeyboard(); 47 | default: 48 | throw std::runtime_error("Unknown type."); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputFactory.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_UINPUTFACTORY_H 24 | #define CONTROLBLOCKSERVICE2_UINPUTFACTORY_H 25 | 26 | #include "IUInputFactory.h" 27 | #include "IUInputDevice.h" 28 | 29 | class UInputFactory : public IUInputFactory 30 | { 31 | public: 32 | virtual IUInputDevice* getUInputDevice(IUInputDevice::DeviceType type); 33 | 34 | }; 35 | 36 | #endif //CONTROLBLOCKSERVICE2_UINPUTFACTORY_H 37 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadArcade.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "UInputGamepadArcade.h" 24 | 25 | UInputGamepadArcade::UInputGamepadArcade() 26 | { 27 | m_fileDescriptor = getHandle(); 28 | 29 | struct uinput_user_dev uinp; 30 | memset(&uinp, 0, sizeof(uinp)); 31 | strncpy(uinp.name, "ControlBlock Arcade Gamepad", strlen("ControlBlock Arcade Gamepad")); 32 | uinp.id.version = 4; 33 | uinp.id.bustype = BUS_USB; 34 | uinp.id.product = 1; 35 | uinp.id.vendor = 1; 36 | 37 | // Setup the uinput device 38 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_KEY); 39 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_REL); 40 | 41 | // gamepad, buttons 42 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_A); 43 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_B); 44 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_C); 45 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_X); 46 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_Y); 47 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_Z); 48 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_TL); 49 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_TR); 50 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_TL2); 51 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_TR2); 52 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_START); 53 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_SELECT); 54 | 55 | // gamepad, directions 56 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_ABS); 57 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_X); 58 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_Y); 59 | uinp.absmin[ABS_X] = 0; 60 | uinp.absmax[ABS_X] = 4; 61 | uinp.absmin[ABS_Y] = 0; 62 | uinp.absmax[ABS_Y] = 4; 63 | 64 | /* Create input device into input sub-system */ 65 | write(m_fileDescriptor, &uinp, sizeof(uinp)); 66 | if (ioctl(m_fileDescriptor, UI_DEV_CREATE)) { 67 | printf("[ArcadeGamepad] Unable to create UINPUT device."); 68 | throw std::runtime_error("Unable to create UINPUT device."); 69 | } 70 | 71 | setKeyState(ABS_X, 2, EV_ABS); 72 | setKeyState(ABS_Y, 2, EV_ABS); 73 | sync(); 74 | } 75 | 76 | UInputGamepadArcade::~UInputGamepadArcade() = default; 77 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadArcade.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef UINPUTGAMEPADARCADE_H 24 | #define UINPUTGAMEPADARCADE_H 25 | 26 | #include "UInputDevice.h" 27 | 28 | class UInputGamepadArcade : public UInputDevice 29 | { 30 | public: 31 | /** 32 | * Constructor 33 | */ 34 | UInputGamepadArcade(); 35 | 36 | /** 37 | * Destructor 38 | */ 39 | ~UInputGamepadArcade() override; 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadGenesis.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "UInputGamepadGenesis.h" 24 | 25 | UInputGamepadGenesis::UInputGamepadGenesis() 26 | { 27 | m_fileDescriptor = getHandle(); 28 | 29 | struct uinput_user_dev uinp; 30 | memset(&uinp, 0, sizeof(uinp)); 31 | strncpy(uinp.name, "ControlBlock Genesis Gamepad", strlen("ControlBlock Genesis Gamepad")); 32 | uinp.id.version = 4; 33 | uinp.id.bustype = BUS_USB; 34 | uinp.id.product = 1; 35 | uinp.id.vendor = 1; 36 | 37 | // Setup the uinput device 38 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_KEY); 39 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_REL); 40 | 41 | // gamepad, buttons 42 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_A); 43 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_B); 44 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_C); 45 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_X); 46 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_Y); 47 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_Z); 48 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_START); 49 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_MODE); 50 | 51 | // Add Absolute (D-pad) type 52 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_ABS); 53 | 54 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_X); 55 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_Y); 56 | 57 | // AXIS_MIN for left/up, AXIS_MAX for right/down 58 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_ABS); 59 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_X); 60 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_Y); 61 | uinp.absmin[ABS_X] = 0; 62 | uinp.absmax[ABS_X] = 4; 63 | uinp.absmin[ABS_Y] = 0; 64 | uinp.absmax[ABS_Y] = 4; 65 | 66 | /* Create input device into input sub-system */ 67 | write(m_fileDescriptor, &uinp, sizeof(uinp)); 68 | if (ioctl(m_fileDescriptor, UI_DEV_CREATE)) { 69 | printf("[GenesisGamepad] Unable to create UINPUT device."); 70 | // throw -1; 71 | } 72 | 73 | setKeyState(ABS_X, 2, EV_ABS); 74 | setKeyState(ABS_Y, 2, EV_ABS); 75 | sync(); 76 | } 77 | 78 | UInputGamepadGenesis::~UInputGamepadGenesis() 79 | = default; -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadGenesis.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_UINPUTGAMEPADGENESIS_H 24 | #define CONTROLBLOCKSERVICE2_UINPUTGAMEPADGENESIS_H 25 | 26 | #include "UInputDevice.h" 27 | 28 | class UInputGamepadGenesis : public UInputDevice 29 | { 30 | public: 31 | UInputGamepadGenesis(); 32 | ~UInputGamepadGenesis() override; 33 | 34 | }; 35 | 36 | #endif //CONTROLBLOCKSERVICE2_UINPUTGAMEPADGENESIS_H 37 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadNES.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "UInputGamepadNES.h" 24 | 25 | UInputGamepadNES::UInputGamepadNES() 26 | { 27 | m_fileDescriptor = getHandle(); 28 | 29 | struct uinput_user_dev uinp; 30 | memset(&uinp, 0, sizeof(uinp)); 31 | strncpy(uinp.name, "ControlBlock NES Gamepad", strlen("ControlBlock NES Gamepad")); 32 | uinp.id.version = 4; 33 | uinp.id.bustype = BUS_USB; 34 | uinp.id.product = 1; 35 | uinp.id.vendor = 1; 36 | 37 | // Setup the uinput device 38 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_KEY); 39 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_REL); 40 | 41 | // gamepad, buttons 42 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_A); 43 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_B); 44 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_START); 45 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_SELECT); 46 | 47 | // Add Absolute (D-pad) type 48 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_ABS); 49 | 50 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_X); 51 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_Y); 52 | 53 | // AXIS_MIN for left/up, AXIS_MAX for right/down 54 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_ABS); 55 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_X); 56 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_Y); 57 | uinp.absmin[ABS_X] = 0; 58 | uinp.absmax[ABS_X] = 4; 59 | uinp.absmin[ABS_Y] = 0; 60 | uinp.absmax[ABS_Y] = 4; 61 | 62 | /* Create input device into input sub-system */ 63 | write(m_fileDescriptor, &uinp, sizeof(uinp)); 64 | if (ioctl(m_fileDescriptor, UI_DEV_CREATE)) 65 | { 66 | printf("[UInputGamepadNES] Unable to create UINPUT device."); 67 | // throw -1; 68 | } 69 | 70 | setKeyState(ABS_X, 2, EV_ABS); 71 | setKeyState(ABS_Y, 2, EV_ABS); 72 | sync(); 73 | } 74 | 75 | UInputGamepadNES::~UInputGamepadNES() 76 | = default; 77 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadNES.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef UINPUTGAMEPADNES_H 24 | #define UINPUTGAMEPADNES_H 25 | 26 | #include "UInputDevice.h" 27 | 28 | class UInputGamepadNES : public UInputDevice 29 | { 30 | public: 31 | /** 32 | * Constructor 33 | */ 34 | UInputGamepadNES(); 35 | 36 | /** 37 | * Destructor 38 | */ 39 | ~UInputGamepadNES() override; 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadSNES.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "UInputGamepadSNES.h" 24 | 25 | UInputGamepadSNES::UInputGamepadSNES() 26 | { 27 | m_fileDescriptor = getHandle(); 28 | 29 | struct uinput_user_dev uinp; 30 | memset(&uinp, 0, sizeof(uinp)); 31 | strncpy(uinp.name, "ControlBlock SNES Gamepad", strlen("ControlBlock SNES Gamepad")); 32 | uinp.id.version = 4; 33 | uinp.id.bustype = BUS_USB; 34 | uinp.id.product = 1; 35 | uinp.id.vendor = 1; 36 | 37 | // Setup the uinput device 38 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_KEY); 39 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_REL); 40 | 41 | // gamepad, buttons 42 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_A); 43 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_B); 44 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_X); 45 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_Y); 46 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_TL); 47 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_TR); 48 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_START); 49 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_SELECT); 50 | 51 | // Add Absolute (D-pad) type 52 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_ABS); 53 | 54 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_X); 55 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_Y); 56 | 57 | // AXIS_MIN for left/up, AXIS_MAX for right/down 58 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_ABS); 59 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_X); 60 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_Y); 61 | uinp.absmin[ABS_X] = 0; 62 | uinp.absmax[ABS_X] = 4; 63 | uinp.absmin[ABS_Y] = 0; 64 | uinp.absmax[ABS_Y] = 4; 65 | 66 | /* Create input device into input sub-system */ 67 | write(m_fileDescriptor, &uinp, sizeof(uinp)); 68 | if (ioctl(m_fileDescriptor, UI_DEV_CREATE)) 69 | { 70 | printf("[SNESGamepad] Unable to create UINPUT device."); 71 | // throw -1; 72 | } 73 | 74 | setKeyState(ABS_X, 2, EV_ABS); 75 | setKeyState(ABS_Y, 2, EV_ABS); 76 | sync(); 77 | } 78 | 79 | UInputGamepadSNES::~UInputGamepadSNES() 80 | { 81 | } 82 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadSNES.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef UINPUTGAMEPADSNES_H 24 | #define UINPUTGAMEPADSNES_H 25 | 26 | #include "UInputDevice.h" 27 | 28 | class UInputGamepadSNES : public UInputDevice 29 | { 30 | public: 31 | /** 32 | * Constructor 33 | */ 34 | UInputGamepadSNES(); 35 | 36 | /** 37 | * Destructor 38 | */ 39 | ~UInputGamepadSNES() override; 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadSaturn.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "UInputGamepadSaturn.h" 24 | 25 | UInputGamepadSaturn::UInputGamepadSaturn() 26 | { 27 | m_fileDescriptor = getHandle(); 28 | 29 | struct uinput_user_dev uinp; 30 | memset(&uinp, 0, sizeof(uinp)); 31 | strncpy(uinp.name, "ControlBlock Sega Saturn Gamepad", strlen("ControlBlock Sega Saturn Gamepad")); 32 | uinp.id.version = 4; 33 | uinp.id.bustype = BUS_USB; 34 | uinp.id.product = 1; 35 | uinp.id.vendor = 1; 36 | 37 | // Setup the uinput device 38 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_KEY); 39 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_REL); 40 | 41 | // gamepad, buttons 42 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_A); 43 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_B); 44 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_C); 45 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_X); 46 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_Y); 47 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_Z); 48 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_TL); 49 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_TR); 50 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, BTN_START); 51 | 52 | // Add Absolute (D-pad) type 53 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_ABS); 54 | 55 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_X); 56 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_Y); 57 | 58 | // AXIS_MIN for left/up, AXIS_MAX for right/down 59 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_ABS); 60 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_X); 61 | ioctl(m_fileDescriptor, UI_SET_ABSBIT, ABS_Y); 62 | uinp.absmin[ABS_X] = 0; 63 | uinp.absmax[ABS_X] = 4; 64 | uinp.absmin[ABS_Y] = 0; 65 | uinp.absmax[ABS_Y] = 4; 66 | 67 | /* Create input device into input sub-system */ 68 | write(m_fileDescriptor, &uinp, sizeof(uinp)); 69 | if (ioctl(m_fileDescriptor, UI_DEV_CREATE)) { 70 | printf("[Sega Saturn Gamepad] Unable to create UINPUT device."); 71 | // throw -1; 72 | } 73 | 74 | setKeyState(ABS_X, 2, EV_ABS); 75 | setKeyState(ABS_Y, 2, EV_ABS); 76 | sync(); 77 | } 78 | 79 | UInputGamepadSaturn::~UInputGamepadSaturn() 80 | = default; -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputGamepadSaturn.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_UINPUTGAMEPADSATURN_H 24 | #define CONTROLBLOCKSERVICE2_UINPUTGAMEPADSATURN_H 25 | 26 | #include "UInputDevice.h" 27 | 28 | class UInputGamepadSaturn : public UInputDevice 29 | { 30 | public: 31 | UInputGamepadSaturn(); 32 | ~UInputGamepadSaturn() override; 33 | 34 | }; 35 | 36 | #endif //CONTROLBLOCKSERVICE2_UINPUTGAMEPADSATURN_H 37 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputKeyboard.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "UInputKeyboard.h" 24 | 25 | UInputKeyboard::UInputKeyboard() { 26 | m_fileDescriptor = getHandle(); 27 | 28 | struct uinput_user_dev uinp; 29 | memset(&uinp, 0, sizeof(uinp)); 30 | strncpy(uinp.name, "ControlBlock Keyboard Device", strlen("ControlBlock Keyboard Device")); 31 | uinp.id.version = 4; 32 | uinp.id.bustype = BUS_USB; 33 | uinp.id.product = 1; 34 | uinp.id.vendor = 1; 35 | 36 | // Setup the uinput keyboard device 37 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_KEY); 38 | ioctl(m_fileDescriptor, UI_SET_EVBIT, EV_REL); 39 | for (uint32_t index = 0u; index < 256u; index++) { 40 | ioctl(m_fileDescriptor, UI_SET_KEYBIT, index); 41 | } 42 | 43 | /* Create input device into input sub-system */ 44 | write(m_fileDescriptor, &uinp, sizeof(uinp)); 45 | if (ioctl(m_fileDescriptor, UI_DEV_CREATE)) { 46 | throw std::runtime_error("[ControlBlockService] Unable to create UINPUT device."); 47 | } 48 | } 49 | 50 | UInputKeyboard::~UInputKeyboard() = default; 51 | -------------------------------------------------------------------------------- /src/controlblock/uinput/UInputKeyboard.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef UINPUTKEYBOARD_H 24 | #define UINPUTKEYBOARD_H 25 | 26 | #include "UInputDevice.h" 27 | 28 | class UInputKeyboard : public UInputDevice 29 | { 30 | public: 31 | /** 32 | * Constructor 33 | */ 34 | UInputKeyboard(); 35 | 36 | /** 37 | * Destructor 38 | */ 39 | ~UInputKeyboard() override; 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/lib/mcp23s17/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(mcp23s17Lib 2 | mcp23s17.cpp 3 | ) -------------------------------------------------------------------------------- /src/lib/mcp23s17/mcp23s17.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mcp23s17.h 3 | * @brief A simple static library for controlling an MCP23S17 port 4 | * expander over SPI. 5 | * 6 | * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/21952b.pdf 7 | * 8 | * Copyright (C) 2013 Thomas Preston 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see . 22 | */ 23 | #ifndef _MCP23S17_H 24 | #define _MCP23S17_H 25 | 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | 33 | #define WRITE_CMD 0 34 | #define READ_CMD 1 35 | 36 | // Register addresses 37 | #define IODIRA 0x00 // I/O direction A 38 | #define IODIRB 0x01 // I/O direction B 39 | #define IPOLA 0x02 // I/O polarity A 40 | #define IPOLB 0x03 // I/O polarity B 41 | #define GPINTENA 0x04 // interupt enable A 42 | #define GPINTENB 0x05 // interupt enable B 43 | #define DEFVALA 0x06 // register default value A (interupts) 44 | #define DEFVALB 0x07 // register default value B (interupts) 45 | #define INTCONA 0x08 // interupt control A 46 | #define INTCONB 0x09 // interupt control B 47 | #define IOCON 0x0A // I/O config (also 0x0B) 48 | #define GPPUA 0x0C // port A pullups 49 | #define GPPUB 0x0D // port B pullups 50 | #define INTFA 0x0E // interupt flag A (where the interupt came from) 51 | #define INTFB 0x0F // interupt flag B 52 | #define INTCAPA 0x10 // interupt capture A (value at interupt is saved here) 53 | #define INTCAPB 0x11 // interupt capture B 54 | #define GPIOA 0x12 // port A 55 | #define GPIOB 0x13 // port B 56 | #define OLATA 0x14 // output latch A 57 | #define OLATB 0x15 // output latch B 58 | 59 | // I/O config 60 | #define BANK_OFF 0x00 // addressing mode 61 | #define BANK_ON 0x80 62 | #define INT_MIRROR_ON 0x40 // interupt mirror (INTa|INTb) 63 | #define INT_MIRROR_OFF 0x00 64 | #define SEQOP_OFF 0x20 // incrementing address pointer 65 | #define SEQOP_ON 0x00 66 | #define DISSLW_ON 0x10 // slew rate 67 | #define DISSLW_OFF 0x00 68 | #define HAEN_ON 0x08 // hardware addressing 69 | #define HAEN_OFF 0x00 70 | #define ODR_ON 0x04 // open drain for interupts 71 | #define ODR_OFF 0x00 72 | #define INTPOL_HIGH 0x02 // interupt polarity 73 | #define INTPOL_LOW 0x00 74 | 75 | #define GPIO_INTERRUPT_PIN 25 76 | 77 | /** 78 | * Returns a file descriptor for the SPI device through which the 79 | * MCP23S17 port expander can be accessed. 80 | * 81 | * @param bus The SPI bus. 82 | * @param chip_select The SPI chip select. 83 | */ 84 | int mcp23s17_open(int bus, int chip_select); 85 | 86 | /** 87 | * Returns the 8 bit value from the register specified. Must also specify 88 | * which hardware address and file descriptor to use. 89 | * 90 | * @param reg The register to read from (example: IODIRA, GPIOA). 91 | * @param hw_addr The hardware address of the MCP23S17. 92 | * @param fd The file descriptor returned from "()". 93 | */ 94 | uint8_t mcp23s17_read_reg(uint8_t reg, uint8_t hw_addr, int fd); 95 | 96 | /** 97 | * Writes an 8 bit value to the register specified. Must also specify 98 | * which hardware address and file descriptor to use. 99 | * 100 | * @param data The data byte to be written. 101 | * @param reg The register to write to (example: IODIRA, GPIOA). 102 | * @param hw_addr The hardware address of the MCP23S17. 103 | * @param fd The file descriptor returned from "()". 104 | */ 105 | void mcp23s17_write_reg(uint8_t data, uint8_t reg, uint8_t hw_addr, int fd); 106 | 107 | /** 108 | * Reads a single bit from the register specified. Must also specify 109 | * which hardware address and file descriptor to use. 110 | * 111 | * @param bit_num The bit number to read. 112 | * @param reg The register to read from (example: IODIRA, GPIOA). 113 | * @param hw_addr The hardware address of the MCP23S17. 114 | * @param fd The file descriptor returned from "()". 115 | */ 116 | uint8_t mcp23s17_read_bit(uint8_t bit_num, 117 | uint8_t reg, 118 | uint8_t hw_addr, 119 | int fd); 120 | 121 | /** 122 | * Writes a single bit to the register specified. Must also specify 123 | * which hardware address and file descriptor to use. 124 | * 125 | * @param data The data to write. 126 | * @param bit_num The bit number to write to. 127 | * @param reg The register to write to (example: IODIRA, GPIOA). 128 | * @param hw_addr The hardware address of the MCP23S17. 129 | * @param fd The file descriptor returned from "()". 130 | */ 131 | void mcp23s17_write_bit(uint8_t data, 132 | uint8_t bit_num, 133 | uint8_t reg, 134 | uint8_t hw_addr, 135 | int fd); 136 | 137 | 138 | /** 139 | * Enables interrupts and exports to the GPIO connection from 140 | * the mcp23s17. 141 | * 142 | * @return int 0 on success 143 | */ 144 | int mcp23s17_enable_interrupts(); 145 | 146 | 147 | /** 148 | * Disables interrupts and exports to the GPIO connection from 149 | * the mcp23s17. 150 | * 151 | * @return int 0 on success 152 | */ 153 | int mcp23s17_disable_interrupts(); 154 | 155 | 156 | /** 157 | * Waits for an interrupt from the mcp23s17 or until timeout is 158 | * reached. 159 | * @note This method does NOT reset the interrupt - which is 160 | * done automatically for you by reading the input state 161 | * register. Calling this method twice in a row without 162 | * reading the input register will cause it to always wait 163 | * for your timeout value, regardless of button presses. 164 | * To avoid this, read the input register after every call 165 | * to this method. 166 | * 167 | * @param timeout Maximum ms to wait for input, -1 for forever 168 | * @return the number of file descriptors ready for the 169 | * requested I/O, zero if no file descriptor became 170 | * ready during the requested timeout milliseconds, or 171 | * -1 on error. 172 | */ 173 | int mcp23s17_wait_for_interrupt(int timeout); 174 | 175 | 176 | #ifdef __cplusplus 177 | } 178 | #endif 179 | 180 | #endif -------------------------------------------------------------------------------- /supplementary/4playerCB1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrockblog/ControlBlockService2/5dbf90e15f879122a2063c5d7a58e44ab8b83c18/supplementary/4playerCB1.jpg -------------------------------------------------------------------------------- /supplementary/4playerCB2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrockblog/ControlBlockService2/5dbf90e15f879122a2063c5d7a58e44ab8b83c18/supplementary/4playerCB2.jpg -------------------------------------------------------------------------------- /supplementary/4playerCB3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrockblog/ControlBlockService2/5dbf90e15f879122a2063c5d7a58e44ab8b83c18/supplementary/4playerCB3.jpg -------------------------------------------------------------------------------- /supplementary/4playerCB4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrockblog/ControlBlockService2/5dbf90e15f879122a2063c5d7a58e44ab8b83c18/supplementary/4playerCB4.jpg -------------------------------------------------------------------------------- /supplementary/ControlBlockLayout.idraw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrockblog/ControlBlockService2/5dbf90e15f879122a2063c5d7a58e44ab8b83c18/supplementary/ControlBlockLayout.idraw -------------------------------------------------------------------------------- /supplementary/ControlBlockLayoutArcade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrockblog/ControlBlockService2/5dbf90e15f879122a2063c5d7a58e44ab8b83c18/supplementary/ControlBlockLayoutArcade.png -------------------------------------------------------------------------------- /supplementary/ControlBlockLayoutGenesis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrockblog/ControlBlockService2/5dbf90e15f879122a2063c5d7a58e44ab8b83c18/supplementary/ControlBlockLayoutGenesis.png -------------------------------------------------------------------------------- /supplementary/ControlBlockLayoutMAME.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrockblog/ControlBlockService2/5dbf90e15f879122a2063c5d7a58e44ab8b83c18/supplementary/ControlBlockLayoutMAME.png -------------------------------------------------------------------------------- /supplementary/ControlBlockLayoutSNES.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrockblog/ControlBlockService2/5dbf90e15f879122a2063c5d7a58e44ab8b83c18/supplementary/ControlBlockLayoutSNES.png -------------------------------------------------------------------------------- /supplementary/controlblockconfig.cfg: -------------------------------------------------------------------------------- 1 | { 2 | "controlblocks" : [ 3 | { 4 | "enabled" : true, // Enables (=true) or disables (=false) the ControlBlock 5 | "address" : { // The address information of the first ControlBlock 6 | "SJ1" : 0, // The hardware address solder-jumper SJ1. Options: 0, 1 7 | "SJ2" : 0 // The hardware address solder-jumper SJ2, Options: 0, 1 8 | }, 9 | "gamepadtype" : "arcade", // Sets the gamepad type. Options: "arcade", "mame", "snes", "nes", "genesis", "none" 10 | "onlyOneGamepad" : false, // If true, registers only one gamepad instead of two 11 | "powerswitchOn" : true // Enables (=true) the power switch functionality. Options: true, false 12 | }, 13 | { 14 | "enabled" : false, // Enables (=true) or disables (=false) the second ControlBlock 15 | "address" : { // The address information of the second ControlBlock 16 | "SJ1" : 1, // The hardware address solder-jumper SJ1. Options: 0, 1 17 | "SJ2" : 0 // The hardware address solder-jumper SJ2, Options: 0, 1 18 | }, 19 | "gamepadtype" : "arcade", // Sets the gamepad type. Options: "arcade", "mame", "snes", "nes", "genesis", "none" 20 | "onlyOneGamepad" : false // If true, registers only one gamepad instead of two 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /supplementary/controlblockswitchoff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | shutdown -h now 3 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.5) 2 | 3 | add_subdirectory(lib/googletest) 4 | 5 | set(ALL_TESTS_RUNNER_FILE "runAllTests") 6 | 7 | include_directories( 8 | ${gtest_SOURCE_DIR}/include 9 | ${gtest_SOURCE_DIR}/ 10 | ${gmock_SOURCE_DIR}/include 11 | ${gmock_SOURCE_DIR}/ 12 | controlblock 13 | ) 14 | 15 | set_source_files_properties(controlblock/app/ControlBlockTest.cpp PROPERTIES COMPILE_DEFINITIONS DEF_TESTCONFIGFILE="${PROJECT_SOURCE_DIR}/supplementary/controlblockconfig.cfg") 16 | 17 | add_executable(${ALL_TESTS_RUNNER_FILE} 18 | ../src/controlblock/app/ControlBlock.cpp 19 | controlblock/app/ControlBlockTest.cpp 20 | 21 | ../src/controlblock/app/PowerSwitch.cpp 22 | controlblock/app/PowerSwitchTest.cpp 23 | 24 | ../src/controlblock/gamepads/ArcadeGamepad.cpp 25 | controlblock/gamepads/ArcadeGamepadTest.cpp 26 | 27 | ../src/controlblock/gamepads/MAMEGamepad.cpp 28 | controlblock/gamepads/MAMEGamepadTest.cpp 29 | 30 | ../src/controlblock/gamepads/SNESGamepad.cpp 31 | controlblock/gamepads/SNESGamepadTest.cpp 32 | 33 | ../src/controlblock/gamepads/GenesisGamepad.cpp 34 | controlblock/gamepads/GenesisGamepadTest.cpp 35 | ) 36 | 37 | target_link_libraries(${ALL_TESTS_RUNNER_FILE} gtest gtest_main gmock controlblock-gamepads controlblock-config) 38 | 39 | add_test(NAME AllControlBlockTests COMMAND "${PROJECT_BINARY_DIR}/test/${ALL_TESTS_RUNNER_FILE}" " --gtest_output=xml:gtestresults.xml") 40 | -------------------------------------------------------------------------------- /test/controlblock/app/PowerSwitchTest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "gtest/gtest.h" 24 | #include "gmock/gmock.h" 25 | #include "hal/DigitalOutMock.h" 26 | #include "hal/DigitalInMock.h" 27 | #include "app/PowerSwitch.h" 28 | 29 | using ::testing::Return; 30 | using ::testing::NiceMock; 31 | 32 | TEST(PowerSwitchTest, Constructor) 33 | { 34 | DigitalOutMock doMock; 35 | DigitalInMock diMock; 36 | 37 | EXPECT_CALL(doMock, configureDevice(IDigitalOut::DO_DEVICE_POWERSWITCH)); 38 | EXPECT_CALL(doMock, setLevel(IDigitalOut::DO_CHANNEL_TOPOWERSWITCH, IDigitalOut::DO_LEVEL_HIGH, IDigitalOut::BOARD_0)); 39 | EXPECT_CALL(diMock, configureDevice(IDigitalIn::DI_DEVICE_POWERSWITCH)); 40 | PowerSwitch powerSwitch(diMock, doMock, PowerSwitch::SHUTDOWN_ACTIVATED); 41 | EXPECT_FALSE(powerSwitch.isShutdownInitiated()); 42 | } 43 | 44 | TEST(PowerSwitchTest, updateWithNoShutdownActivated_ExpectNoShutdown) 45 | { 46 | NiceMock doMock; 47 | NiceMock diMock; 48 | PowerSwitch powerSwitch(diMock, doMock, PowerSwitch::SHUTDOWN_DEACTIVATED); 49 | 50 | powerSwitch.update(); 51 | EXPECT_FALSE(powerSwitch.isShutdownInitiated()); 52 | } 53 | 54 | TEST(PowerSwitchTest, updateAndExpectShutdown) 55 | { 56 | NiceMock doMock; 57 | NiceMock diMock; 58 | PowerSwitch powerSwitch(diMock, doMock, PowerSwitch::SHUTDOWN_ACTIVATED); 59 | 60 | EXPECT_CALL(diMock, getLevel(IDigitalIn::DI_CHANNEL_FROMPOWERSWITCH, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 61 | 62 | powerSwitch.update(); 63 | EXPECT_TRUE(powerSwitch.isShutdownInitiated()); 64 | } 65 | 66 | TEST(PowerSwitchTest, updateAndExpectNoShutdown) 67 | { 68 | NiceMock doMock; 69 | NiceMock diMock; 70 | PowerSwitch powerSwitch(diMock, doMock, PowerSwitch::SHUTDOWN_ACTIVATED); 71 | 72 | EXPECT_CALL(diMock, getLevel(IDigitalIn::DI_CHANNEL_FROMPOWERSWITCH, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_LOW)); 73 | 74 | powerSwitch.update(); 75 | EXPECT_FALSE(powerSwitch.isShutdownInitiated()); 76 | } 77 | -------------------------------------------------------------------------------- /test/controlblock/config/ControlBlockConfigurationMock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_CONTROLBLOCKCONFIGURATIONMOCK_H 24 | #define CONTROLBLOCKSERVICE2_CONTROLBLOCKCONFIGURATIONMOCK_H 25 | 26 | #include "gmock/gmock.h" // Brings in Google Mock. 27 | #include "config/IControlBlockConfiguration.h" 28 | 29 | class ControlBlockConfigurationMock : public IControlBlockConfiguration 30 | { 31 | public: 32 | MOCK_METHOD0(loadConfiguration, void()); 33 | MOCK_METHOD1(getConfiguration, SingleConfiguration&(int controlBlockID)); 34 | 35 | }; 36 | 37 | #endif //CONTROLBLOCKSERVICE2_CONTROLBLOCKCONFIGURATIONMOCK_H 38 | -------------------------------------------------------------------------------- /test/controlblock/config/SingleConfigurationMock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_SINGLECONFIGURATIONMOCK_H 24 | #define CONTROLBLOCKSERVICE2_SINGLECONFIGURATIONMOCK_H 25 | 26 | #include "gmock/gmock.h" // Brings in Google Mock. 27 | #include "config/ISingleConfiguration.h" 28 | 29 | class SingleConfigurationMock: public ISingleConfiguration 30 | { 31 | public: 32 | MOCK_METHOD0(isEnabled, bool()); 33 | MOCK_METHOD0(getDeviceAddress, uint8_t()); 34 | MOCK_METHOD0(getGamepadType, GamepadType_e()); 35 | MOCK_METHOD0(isPowerSwitchEnabled, bool()); 36 | MOCK_METHOD0(isOnlyOneGamepadEnabled, bool()); 37 | }; 38 | 39 | #endif //CONTROLBLOCKSERVICE2_SINGLECONFIGURATIONMOCK_H 40 | -------------------------------------------------------------------------------- /test/controlblock/gamepads/GamepadFactoryMock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_GAMEPADFACTORYMOCK_H 24 | #define CONTROLBLOCKSERVICE2_GAMEPADFACTORYMOCK_H 25 | 26 | #include "gmock/gmock.h" // Brings in Google Mock. 27 | #include "gamepads/IGamepadFactory.h" 28 | 29 | class GamepadFactoryMock: public IGamepadFactory 30 | { 31 | public: 32 | GamepadFactoryMock() { } 33 | virtual ~GamepadFactoryMock() { } 34 | 35 | virtual InputDevice* createGamepad(InputDevice::GamepadType_e gamepadType) 36 | { 37 | return createGamepadProxy(gamepadType); 38 | } 39 | 40 | MOCK_METHOD1(createGamepadProxy, InputDevice*(InputDevice::GamepadType_e gamepadType)); 41 | }; 42 | 43 | #endif //CONTROLBLOCKSERVICE2_GAMEPADFACTORYMOCK_H 44 | -------------------------------------------------------------------------------- /test/controlblock/gamepads/GenesisGamepadTest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "gtest/gtest.h" 24 | #include "gmock/gmock.h" 25 | 26 | #include "uinput/UInputDeviceMock.h" 27 | #include "uinput/UInputFactoryMock.h" 28 | #include "hal/DigitalInMock.h" 29 | #include "hal/DigitalOutMock.h" 30 | #include "gamepads/GenesisGamepad.h" 31 | 32 | using ::testing::Return; 33 | using ::testing::NiceMock; 34 | 35 | TEST(GenesisGamepadTest, Constructor) 36 | { 37 | UInputFactoryMock uiFactory; 38 | DigitalInMock di; 39 | DigitalOutMock digitalOut; 40 | 41 | EXPECT_CALL(uiFactory, getUInputDeviceProxy(IUInputDevice::TYPE_GAMEPAD_GENESIS)); 42 | GenesisGamepad gamepad(uiFactory, di, digitalOut); 43 | } 44 | 45 | -------------------------------------------------------------------------------- /test/controlblock/gamepads/InputDeviceMock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_INPUTDEVICEMOCK_H 24 | #define CONTROLBLOCKSERVICE2_INPUTDEVICEMOCK_H 25 | 26 | #include "gmock/gmock.h" // Brings in Google Mock. 27 | #include "gamepads/InputDevice.h" 28 | 29 | class InputDeviceMock: public InputDevice 30 | { 31 | public: 32 | MOCK_METHOD1(initialize, void(Channel_e channel)); 33 | MOCK_METHOD0(update, void()); 34 | }; 35 | 36 | #endif //CONTROLBLOCKSERVICE2_INPUTDEVICEMOCK_H 37 | -------------------------------------------------------------------------------- /test/controlblock/gamepads/MAMEGamepadTest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "gtest/gtest.h" 24 | #include "gmock/gmock.h" 25 | 26 | #include "uinput/UInputDeviceMock.h" 27 | #include "uinput/UInputFactoryMock.h" 28 | #include "hal/DigitalInMock.h" 29 | #include "gamepads/MAMEGamepad.h" 30 | 31 | using ::testing::Return; 32 | using ::testing::NiceMock; 33 | 34 | TEST(MAMEGamepadTest, Constructor) 35 | { 36 | UInputFactoryMock uiFactory; 37 | DigitalInMock di; 38 | 39 | EXPECT_CALL(uiFactory, getUInputDeviceProxy(IUInputDevice::TYPE_KEYVBOARD)); 40 | MAMEGamepad gamepad(uiFactory, di); 41 | } 42 | 43 | TEST(MAMEGamepadTest, initialize) 44 | { 45 | NiceMock uiFactory; 46 | DigitalInMock di; 47 | MAMEGamepad gamepad(uiFactory, di); 48 | 49 | EXPECT_CALL(di, configureDevice(IDigitalIn::DI_DEVICE_ALLIN)); 50 | gamepad.initialize(InputDevice::CHANNEL_1); 51 | } 52 | 53 | TEST(MAMEGamepadTest, updateLeftUpAndAllButtonsPressed) 54 | { 55 | NiceMock uiFactory; 56 | DigitalInMock di; 57 | UInputDeviceMock* gamepadMock = new UInputDeviceMock(); 58 | 59 | EXPECT_CALL(uiFactory, getUInputDeviceProxy(IUInputDevice::TYPE_KEYVBOARD)).WillOnce(Return(gamepadMock)); 60 | EXPECT_CALL(di, configureDevice(IDigitalIn::DI_DEVICE_ALLIN)); 61 | MAMEGamepad gamepad(uiFactory, di); 62 | gamepad.initialize(InputDevice::CHANNEL_1); 63 | 64 | // axes 65 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_LEFT, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 66 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_LEFT, 1, EV_KEY)); 67 | 68 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_RIGHT, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 69 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_RIGHT, 1, EV_KEY)); 70 | 71 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_UP, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 72 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_UP, 1, EV_KEY)); 73 | 74 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_DOWN, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 75 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_DOWN, 1, EV_KEY)); 76 | 77 | 78 | // buttons 79 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_SW1, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 80 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_LEFTCTRL, 1, EV_KEY)); 81 | 82 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_SW2, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 83 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_LEFTALT, 1, EV_KEY)); 84 | 85 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_SW3, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 86 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_SPACE, 1, EV_KEY)); 87 | 88 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_SW4, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 89 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_LEFTSHIFT, 1, EV_KEY)); 90 | 91 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_SW5, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 92 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_Z, 1, EV_KEY)); 93 | 94 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_SW6, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 95 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_X, 1, EV_KEY)); 96 | 97 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_SW7, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 98 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_C, 1, EV_KEY)); 99 | 100 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_SW8, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 101 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_V, 1, EV_KEY)); 102 | 103 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_START, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 104 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_1, 1, EV_KEY)); 105 | 106 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_COIN, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 107 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_5, 1, EV_KEY)); 108 | 109 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_A, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 110 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_P, 1, EV_KEY)); 111 | 112 | EXPECT_CALL(di, getLevel(IDigitalIn::DI_CHANNEL_P1_B, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 113 | EXPECT_CALL(*gamepadMock, setKeyState(KEY_ENTER, 1, EV_KEY)); 114 | 115 | EXPECT_CALL(*gamepadMock, sync()); 116 | gamepad.update(); 117 | } 118 | -------------------------------------------------------------------------------- /test/controlblock/gamepads/SNESGamepadTest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #include "gtest/gtest.h" 24 | #include "gmock/gmock.h" 25 | 26 | #include "uinput/UInputDeviceMock.h" 27 | #include "uinput/UInputFactoryMock.h" 28 | #include "hal/DigitalInMock.h" 29 | #include "hal/DigitalOutMock.h" 30 | #include "gamepads/SNESGamepad.h" 31 | 32 | using ::testing::Return; 33 | using ::testing::NiceMock; 34 | 35 | TEST(SNESGamepadTest, Constructor) 36 | { 37 | UInputFactoryMock uiFactory; 38 | DigitalInMock diMock; 39 | DigitalOutMock doMock; 40 | 41 | EXPECT_CALL(uiFactory, getUInputDeviceProxy(IUInputDevice::TYPE_GAMEPAD_SNES)); 42 | EXPECT_CALL(uiFactory, getUInputDeviceProxy(IUInputDevice::TYPE_KEYVBOARD)); 43 | 44 | SNESGamepad gamepad(uiFactory, diMock, doMock); 45 | } 46 | 47 | TEST(SNESGamepadTest, initialize_channel1) 48 | { 49 | NiceMock uiFactory; 50 | DigitalInMock diMock; 51 | DigitalOutMock doMock; 52 | 53 | EXPECT_CALL(uiFactory, getUInputDeviceProxy(IUInputDevice::TYPE_GAMEPAD_SNES)); 54 | EXPECT_CALL(uiFactory, getUInputDeviceProxy(IUInputDevice::TYPE_KEYVBOARD)); 55 | 56 | SNESGamepad gamepad(uiFactory, diMock, doMock); 57 | 58 | EXPECT_CALL(diMock, configureDevice(IDigitalIn::DI_DEVICE_SNES)); 59 | EXPECT_CALL(doMock, configureDevice(IDigitalOut::DO_DEVICE_SNES)); 60 | 61 | EXPECT_CALL(doMock, setLevel(IDigitalOut::DO_CHANNEL_P1P2_STROBE, IDigitalOut::DO_LEVEL_LOW, IDigitalOut::BOARD_0)); 62 | EXPECT_CALL(doMock, setLevel(IDigitalOut::DO_CHANNEL_P1P2_CLOCK, IDigitalOut::DO_LEVEL_LOW, IDigitalOut::BOARD_0)); 63 | EXPECT_CALL(doMock, setLevel(IDigitalOut::DO_CHANNEL_P1_VCC, IDigitalOut::DO_LEVEL_HIGH, IDigitalOut::BOARD_0)); 64 | 65 | gamepad.initialize(InputDevice::CHANNEL_1); 66 | } 67 | 68 | TEST(SNESGamepadTest, update) 69 | { 70 | NiceMock uiFactory; 71 | DigitalInMock diMock; 72 | DigitalOutMock doMock; 73 | UInputDeviceMock* gamepadMock = new UInputDeviceMock(); 74 | UInputDeviceMock* keyboardMock = new UInputDeviceMock(); 75 | 76 | EXPECT_CALL(uiFactory, getUInputDeviceProxy(IUInputDevice::TYPE_GAMEPAD_SNES)).WillOnce(Return(gamepadMock)); 77 | EXPECT_CALL(uiFactory, getUInputDeviceProxy(IUInputDevice::TYPE_KEYVBOARD)).WillOnce(Return(keyboardMock)); 78 | 79 | SNESGamepad gamepad(uiFactory, diMock, doMock); 80 | 81 | EXPECT_CALL(diMock, configureDevice(IDigitalIn::DI_DEVICE_SNES)); 82 | EXPECT_CALL(doMock, configureDevice(IDigitalOut::DO_DEVICE_SNES)); 83 | 84 | EXPECT_CALL(doMock, setLevel(IDigitalOut::DO_CHANNEL_P1P2_STROBE, IDigitalOut::DO_LEVEL_LOW, IDigitalOut::BOARD_0)); 85 | EXPECT_CALL(doMock, setLevel(IDigitalOut::DO_CHANNEL_P1P2_CLOCK, IDigitalOut::DO_LEVEL_LOW, IDigitalOut::BOARD_0)); 86 | EXPECT_CALL(doMock, setLevel(IDigitalOut::DO_CHANNEL_P1_VCC, IDigitalOut::DO_LEVEL_HIGH, IDigitalOut::BOARD_0)); 87 | 88 | gamepad.initialize(InputDevice::CHANNEL_1); 89 | 90 | EXPECT_CALL(doMock, 91 | setLevel(IDigitalOut::DO_CHANNEL_P1P2_STROBE, IDigitalOut::DO_LEVEL_HIGH, IDigitalOut::BOARD_0)); 92 | EXPECT_CALL(doMock, setLevel(IDigitalOut::DO_CHANNEL_P1P2_STROBE, IDigitalOut::DO_LEVEL_LOW, IDigitalOut::BOARD_0)); 93 | 94 | EXPECT_CALL(diMock, getLevel(IDigitalIn::DI_CHANNEL_P1_DATA, IDigitalIn::BOARD_0)).Times(12); 95 | 96 | EXPECT_CALL(doMock, 97 | setLevel(IDigitalOut::DO_CHANNEL_P1P2_CLOCK, IDigitalOut::DO_LEVEL_HIGH, IDigitalOut::BOARD_0)).Times(12); 98 | EXPECT_CALL(doMock, 99 | setLevel(IDigitalOut::DO_CHANNEL_P1P2_CLOCK, IDigitalOut::DO_LEVEL_LOW, IDigitalOut::BOARD_0)).Times(12); 100 | 101 | // reset button 102 | EXPECT_CALL(diMock, getLevel(IDigitalIn::DI_CHANNEL_P1_B, IDigitalIn::BOARD_0)).WillOnce(Return(IDigitalIn::DI_LEVEL_HIGH)); 103 | EXPECT_CALL(*keyboardMock, setKeyState(KEY_ESC, 0, EV_KEY)); 104 | EXPECT_CALL(*keyboardMock, sync()); 105 | 106 | EXPECT_CALL(*gamepadMock, setKeyState(ABS_X, 2, EV_ABS)); 107 | EXPECT_CALL(*gamepadMock, setKeyState(ABS_Y, 2, EV_ABS)); 108 | 109 | EXPECT_CALL(*gamepadMock, setKeyState(BTN_A, 0, EV_KEY)); 110 | EXPECT_CALL(*gamepadMock, setKeyState(BTN_B, 0, EV_KEY)); 111 | EXPECT_CALL(*gamepadMock, setKeyState(BTN_X, 0, EV_KEY)); 112 | EXPECT_CALL(*gamepadMock, setKeyState(BTN_Y, 0, EV_KEY)); 113 | EXPECT_CALL(*gamepadMock, setKeyState(BTN_TL, 0, EV_KEY)); 114 | EXPECT_CALL(*gamepadMock, setKeyState(BTN_TR, 0, EV_KEY)); 115 | EXPECT_CALL(*gamepadMock, setKeyState(BTN_START, 1, EV_KEY)); 116 | EXPECT_CALL(*gamepadMock, setKeyState(BTN_SELECT, 1, EV_KEY)); 117 | 118 | EXPECT_CALL(*gamepadMock, sync()); 119 | 120 | gamepad.update(); 121 | } -------------------------------------------------------------------------------- /test/controlblock/hal/DigitalInMock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_DIGITALINMOCK_H 24 | #define CONTROLBLOCKSERVICE2_DIGITALINMOCK_H 25 | 26 | #include "gmock/gmock.h" // Brings in Google Mock. 27 | #include "hal/DigitalIn.h" 28 | 29 | class DigitalInMock : public IDigitalIn { 30 | public: 31 | MOCK_METHOD1(configureDevice, void(DI_Device mode)); 32 | MOCK_METHOD2(getLevel, DI_Level_e(DI_Channel_e channel, BoardNumber_e board)); 33 | }; 34 | 35 | #endif //CONTROLBLOCKSERVICE2_DIGITALINMOCK_H 36 | -------------------------------------------------------------------------------- /test/controlblock/hal/DigitalOutMock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_DIGITALOUTMOCK_H 24 | #define CONTROLBLOCKSERVICE2_DIGITALOUTMOCK_H 25 | 26 | #include "gmock/gmock.h" // Brings in Google Mock. 27 | #include "hal/IDigitalOut.h" 28 | 29 | class DigitalOutMock : public IDigitalOut { 30 | public: 31 | MOCK_METHOD1(configureDevice, void(DO_Device device)); 32 | MOCK_METHOD3(setLevel, void(DO_Channel_e channel, DO_Level_e level, BoardNumber_e board)); 33 | }; 34 | 35 | #endif //CONTROLBLOCKSERVICE2_DIGITALOUTMOCK_H 36 | -------------------------------------------------------------------------------- /test/controlblock/uinput/UInputDeviceMock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_UINPUTDEVICEMOCK_H 24 | #define CONTROLBLOCKSERVICE2_UINPUTDEVICEMOCK_H 25 | 26 | #include "gmock/gmock.h" // Brings in Google Mock. 27 | #include "uinput/IUInputDevice.h" 28 | 29 | class UInputDeviceMock : public IUInputDevice { 30 | public: 31 | MOCK_METHOD3(setKeyState, void(uint16_t keycode, int16_t keyvalue, uint16_t evtype)); 32 | MOCK_METHOD0(sync, void()); 33 | }; 34 | 35 | #endif //CONTROLBLOCKSERVICE2_UINPUTDEVICEMOCK_H 36 | -------------------------------------------------------------------------------- /test/controlblock/uinput/UInputFactoryMock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * (c) Copyright 2017 Florian Müller (contact@petrockblock.com) 3 | * https://github.com/petrockblog/ControlBlock2 4 | * 5 | * Permission to use, copy, modify and distribute the program in both binary and 6 | * source form, for non-commercial purposes, is hereby granted without fee, 7 | * providing that this license information and copyright notice appear with 8 | * all copies and any derived work. 9 | * 10 | * This software is provided 'as-is', without any express or implied 11 | * warranty. In no event shall the authors be held liable for any damages 12 | * arising from the use of this software. 13 | * 14 | * This program is freeware for PERSONAL USE only. Commercial users must 15 | * seek permission of the copyright holders first. Commercial use includes 16 | * charging money for the program or software derived from the program. 17 | * 18 | * The copyright holders request that bug fixes and improvements to the code 19 | * should be forwarded to them so everyone can benefit from the modifications 20 | * in future versions. 21 | */ 22 | 23 | #ifndef CONTROLBLOCKSERVICE2_UINPUTFACTORYMOCK_H 24 | #define CONTROLBLOCKSERVICE2_UINPUTFACTORYMOCK_H 25 | 26 | #include "gmock/gmock.h" // Brings in Google Mock. 27 | #include "uinput/IUInputFactory.h" 28 | 29 | class UInputFactoryMock : public IUInputFactory 30 | { 31 | public: 32 | UInputFactoryMock() { } 33 | virtual ~UInputFactoryMock() { } 34 | 35 | virtual std::unique_ptr getUInputDevice(IUInputDevice::DeviceType type) 36 | { 37 | return std::unique_ptr(getUInputDeviceProxy(type)); 38 | } 39 | 40 | MOCK_METHOD1(getUInputDeviceProxy, IUInputDevice*(IUInputDevice::DeviceType type)); 41 | }; 42 | 43 | #endif //CONTROLBLOCKSERVICE2_UINPUTFACTORYMOCK_H 44 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # check, if sudo is used 4 | if [[ "$(id -u)" -ne 0 ]]; then 5 | echo "Script must be run under sudo from the user you want to install for. Try 'sudo $0'" 6 | exit 1 7 | fi 8 | 9 | # ensure that all needed OS packages are installed 10 | apt-get install -y git cmake g++-4.9 doxygen || (c=$?; echo "Error during installation of APT packages"; (exit $c)) 11 | 12 | # make sure that the submodule data is available 13 | git submodule update --init --recursive 14 | 15 | if [[ -d build ]]; then 16 | rm -rf build 17 | fi 18 | 19 | # create a folder that will contain all build artefacts and change into that folder 20 | mkdir build || (c=$?; echo "Error while creating build folder"; (exit $c)) 21 | pushd build || (c=$?; echo "Error while changing into the folder build"; (exit $c)) 22 | 23 | # create Makefiles 24 | cmake .. || (c=$?; echo "Error while generating Makefiles"; (exit $c)) 25 | 26 | # ensure that no old instance of the driver is running and installed 27 | make uninstallservice 28 | popd 29 | 30 | # check that the service is not running anymore 31 | ps -ef | grep controlblockservice | grep -v grep 32 | [ $? -eq "0" ] && echo "[ERROR] Could not remove the ControlBlock service" || echo "[SUCCESS] The ControlBlock service was successfully removed." 33 | 34 | -------------------------------------------------------------------------------- /workspace.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.associations": { 9 | "format": "cpp", 10 | "cctype": "cpp", 11 | "clocale": "cpp", 12 | "cmath": "cpp", 13 | "cstdarg": "cpp", 14 | "cstddef": "cpp", 15 | "cstdio": "cpp", 16 | "cstdlib": "cpp", 17 | "cstring": "cpp", 18 | "ctime": "cpp", 19 | "cwchar": "cpp", 20 | "cwctype": "cpp", 21 | "array": "cpp", 22 | "atomic": "cpp", 23 | "*.tcc": "cpp", 24 | "chrono": "cpp", 25 | "condition_variable": "cpp", 26 | "cstdint": "cpp", 27 | "deque": "cpp", 28 | "list": "cpp", 29 | "unordered_map": "cpp", 30 | "vector": "cpp", 31 | "exception": "cpp", 32 | "algorithm": "cpp", 33 | "functional": "cpp", 34 | "iterator": "cpp", 35 | "map": "cpp", 36 | "memory": "cpp", 37 | "memory_resource": "cpp", 38 | "numeric": "cpp", 39 | "optional": "cpp", 40 | "random": "cpp", 41 | "ratio": "cpp", 42 | "set": "cpp", 43 | "string": "cpp", 44 | "string_view": "cpp", 45 | "system_error": "cpp", 46 | "tuple": "cpp", 47 | "type_traits": "cpp", 48 | "utility": "cpp", 49 | "fstream": "cpp", 50 | "initializer_list": "cpp", 51 | "iomanip": "cpp", 52 | "iosfwd": "cpp", 53 | "iostream": "cpp", 54 | "istream": "cpp", 55 | "limits": "cpp", 56 | "mutex": "cpp", 57 | "new": "cpp", 58 | "ostream": "cpp", 59 | "sstream": "cpp", 60 | "stdexcept": "cpp", 61 | "streambuf": "cpp", 62 | "thread": "cpp", 63 | "cinttypes": "cpp", 64 | "typeinfo": "cpp", 65 | "variant": "cpp" 66 | } 67 | } 68 | } --------------------------------------------------------------------------------