Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ option(WITH_CUSTOM_CONTROLLER "build with custom controller" ON)
option(WITH_PLAYCOVER_CONTROLLER "build with PlayCover controller for macOS" ON)
option(WITH_GAMEPAD_CONTROLLER "build with virtual gamepad controller for Windows" ON)
option(WITH_WLROOTS_CONTROLLER "build with wlroots controller for Linux" ON)
option(WITH_GAMESCOPE_CONTROLLER "build with gamescope controller for Linux" ON)
option(WITH_RECORD_CONTROLLER "build with record controller for recording" ON)
option(WITH_REPLAY_CONTROLLER "build with replay controller" ON)
option(WITH_DBG_CONTROLLER "build with debug controller" OFF)
Expand Down Expand Up @@ -80,6 +81,11 @@ if(WITH_WLROOTS_CONTROLLER AND (NOT LINUX OR ANDROID))
set(WITH_WLROOTS_CONTROLLER OFF)
endif()

if(WITH_GAMESCOPE_CONTROLLER AND (NOT LINUX OR ANDROID))
message(STATUS "Not on Linux, disable WITH_GAMESCOPE_CONTROLLER")
set(WITH_GAMESCOPE_CONTROLLER OFF)
endif()

if(WITH_MAA_AGENT)
find_package(cppzmq REQUIRED)
endif()
Expand Down
9 changes: 9 additions & 0 deletions docs/en_us/2.2-IntegratedInterfaceOverview.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,14 @@ Create PlayCover controller for controlling iOS applications running via [fork P

Create WlRoots controller for controlling applications running in wlroots compositor on Linux. See [Control Methods](2.4-ControlMethods.md#wlroots-linux).

### MaaGamescopeControllerCreate

- `node_id`: PipeWire node ID identifying the gamescope output stream
- `eis_socket_path`: EIS socket path for libei keyboard/mouse emulation, e.g. `/run/user/1000/gamescope-0-ei`
- `use_win32_vk_code`: Whether to interpret key codes as Win32 Virtual-Key codes (VK_*). When `true`, key codes passed to `click_key` / `key_down` / `key_up` are interpreted as Win32 VK codes and translated to Linux evdev codes internally; when `false`, they are passed through as raw evdev codes. Default is `false`

Create Gamescope controller for controlling applications running in Gamescope microcompositor on Linux. See [Control Methods](2.4-ControlMethods.md#gamescope-linux).

### MaaGamepadControllerCreate

- `hWnd`: Window handle for screencap. Pass nullptr/None/null if screencap is not needed
Expand Down Expand Up @@ -596,6 +604,7 @@ Fields returned by each type:
- `playcover`: `type`, `address`
- `gamepad`: `type`, `hwnd`, `gamepad_type`, `screencap_method`
- `wlroots`: `type`, `wlr_socket_path`
- `gamescope`: `type`, `node_id`, `eis_socket_path`, `use_win32_vk_code`
- `custom`: `type` (if the custom controller implements the `get_info` callback, additional fields from it will also be included)
- `dbg`: `type`, `path`, `image_count`, `image_index`
- `replay`: `type`, `record_count`, `record_index`
Expand Down
10 changes: 10 additions & 0 deletions docs/en_us/2.4-ControlMethods.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,13 @@ The wlroots compositor to be controlled must support the following protocols:
Keycodes for MaaControllerPostKey{Down,Up} in the WlRoots controller are **by default** evdev key codes, defined in [linux/input-event-codes.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/input-event-codes.h).

If you prefer using Win32 Virtual-Key codes (VK_*), pass `true` for the `use_win32_vk_code` parameter when calling `MaaWlRootsControllerCreate`. When enabled, key codes passed to `MaaControllerPostClickKey` / `MaaControllerPostKey{Down,Up}` are interpreted as Win32 VK codes and translated to evdev codes internally. This flag is decided at creation time and cannot be changed at runtime; it makes it easy to reuse key handling logic written for the Win32 controller.

## Gamescope (Linux)

The Gamescope controller is used to control applications running in a [Gamescope](https://github.com/ValveSoftware/gamescope) microcompositor on Linux.

### Keyboard input notice

Keycodes for MaaControllerPostKey{Down,Up} in the Gamescope controller are **by default** evdev key codes, defined in [linux/input-event-codes.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/input-event-codes.h).

If you prefer using Win32 Virtual-Key codes (VK_*), pass `true` for the `use_win32_vk_code` parameter when calling `MaaGamescopeControllerCreate`. When enabled, key codes passed to `MaaControllerPostClickKey` / `MaaControllerPostKey{Down,Up}` are interpreted as Win32 VK codes and translated to evdev codes internally. This flag is decided at creation time and cannot be changed at runtime.
9 changes: 9 additions & 0 deletions docs/zh_cn/2.2-集成接口一览.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,14 @@

创建 WlRoots 控制器,用于在 Linux 上控制在 wlroots 合成器中运行的应用程序。详见 [控制方式说明](2.4-控制方式说明.md#wlroots-linux)。

### MaaGamescopeControllerCreate

- `node_id`: PipeWire 节点 ID,标识 gamescope 的输出流
- `eis_socket_path`: EIS 套接字路径,用于 libei 键盘鼠标模拟,如 `/run/user/1000/gamescope-0-ei`
- `use_win32_vk_code`: 是否将按键视为 Win32 Virtual-Key 键码(VK_*)。为 `true` 时,传入 `click_key` / `key_down` / `key_up` 的键码会被视为 Win32 VK 码并在内部转换为 Linux evdev 码;为 `false` 时按原始 evdev 码处理。默认 `false`

创建 Gamescope 控制器,用于在 Linux 上控制 Gamescope 微合成器中运行的应用程序。详见 [控制方式说明](2.4-控制方式说明.md#gamescope-linux)。

### MaaGamepadControllerCreate

- `hWnd`: 窗口句柄,用于截图。如果不需要截图可以传 nullptr/None/null
Expand Down Expand Up @@ -595,6 +603,7 @@
- `playcover`: `type`, `address`
- `gamepad`: `type`, `hwnd`, `gamepad_type`, `screencap_method`
- `wlroots`: `type`, `wlr_socket_path`
- `gamescope`: `type`, `node_id`, `eis_socket_path`, `use_win32_vk_code`
- `custom`: `type`(如果自定义控制器实现了 `get_info` 回调,还会包含其返回的额外字段)
- `dbg`: `type`, `path`, `image_count`, `image_index`
- `replay`: `type`, `record_count`, `record_index`
Expand Down
10 changes: 10 additions & 0 deletions docs/zh_cn/2.4-控制方式说明.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,13 @@ WlRoots 控制器用于在 Linux 上控制在专用 wlroots 合成器中运行
WlRoots 控制器下,使用 MaaControllerPostKey{Down,Up} 传入的按键**默认**为 evdev 扫描码,定义见 [linux/input-event-codes.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/input-event-codes.h)。

如需使用 Win32 Virtual-Key 键码(VK_*),在创建控制器时将 `MaaWlRootsControllerCreate` 的 `use_win32_vk_code` 参数置为 `true` 即可。开启后,`MaaControllerPostClickKey` / `MaaControllerPostKey{Down,Up}` 所接受的键码会被视为 Win32 VK 键码并在内部转换为 evdev 码。此开关在创建时一次性决定,不可运行时修改,便于从 Win32 控制器的代码中迁移按键逻辑到 WlRoots。

## Gamescope (Linux)

Gamescope 控制器用于在 Linux 上控制 [Gamescope](https://github.com/ValveSoftware/gamescope) 微合成器中运行的应用程序。

### 键盘输入说明

Gamescope 控制器下,使用 MaaControllerPostKey{Down,Up} 传入的按键**默认**为 evdev 扫描码,定义见 [linux/input-event-codes.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/input-event-codes.h)。

如需使用 Win32 Virtual-Key 键码(VK_*),在创建控制器时将 `MaaGamescopeControllerCreate` 的 `use_win32_vk_code` 参数置为 `true` 即可。开启后,`MaaControllerPostClickKey` / `MaaControllerPostKey{Down,Up}` 所接受的键码会被视为 Win32 VK 键码并在内部转换为 evdev 码。此开关在创建时一次性决定,不可运行时修改。
10 changes: 10 additions & 0 deletions include/MaaControlUnit/ControlUnitAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ class WlRootsControlUnitAPI
virtual ~WlRootsControlUnitAPI() = default;
};

class GamescopeControlUnitAPI
: public ControlUnitAPI
, public ScrollableUnit
, public RelativeMovableUnit
{
public:
virtual ~GamescopeControlUnitAPI() = default;
};

class CustomControlUnitAPI
: public ControlUnitAPI
, public ScrollableUnit
Expand Down Expand Up @@ -156,6 +165,7 @@ using MaaAdbControlUnitHandle = MAA_CTRL_UNIT_NS::AdbControlUnitAPI*;
using MaaWin32ControlUnitHandle = MAA_CTRL_UNIT_NS::Win32ControlUnitAPI*;
using MaaMacOSControlUnitHandle = MAA_CTRL_UNIT_NS::MacOSControlUnitAPI*;
using MaaWlRootsControlUnitHandle = MAA_CTRL_UNIT_NS::WlRootsControlUnitAPI*;
using MaaGamescopeControlUnitHandle = MAA_CTRL_UNIT_NS::GamescopeControlUnitAPI*;
using MaaGamepadControlUnitHandle = MAA_CTRL_UNIT_NS::GamepadControlUnitAPI*;
using MaaCustomControlUnitHandle = MAA_CTRL_UNIT_NS::CustomControlUnitAPI*;
using MaaReplayControlUnitHandle = MAA_CTRL_UNIT_NS::FullControlUnitAPI*;
Expand Down
20 changes: 20 additions & 0 deletions include/MaaControlUnit/GamescopeControlUnitAPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include "MaaControlUnit/ControlUnitAPI.h"
#include "MaaFramework/MaaDef.h"

#ifdef __cplusplus
extern "C"
{
#endif

MAA_CONTROL_UNIT_API const char* MaaGamescopeControlUnitGetVersion();

MAA_CONTROL_UNIT_API MaaGamescopeControlUnitHandle
MaaGamescopeControlUnitCreate(uint32_t node_id, const char* eis_socket_path, MaaBool use_win32_vk_code);

MAA_CONTROL_UNIT_API void MaaGamescopeControlUnitDestroy(MaaGamescopeControlUnitHandle handle);

#ifdef __cplusplus
}
#endif
13 changes: 13 additions & 0 deletions include/MaaFramework/Instance/MaaController.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@ extern "C"
*/
MAA_FRAMEWORK_API MaaController* MaaWlRootsControllerCreate(const char* wlr_socket_path, MaaBool use_win32_vk_code);

/**
* @brief Create a Gamescope controller for Linux.
*
* @param node_id PipeWire node ID for the gamescope output stream.
* @param eis_socket_path EIS socket path for libei keyboard/mouse emulation.
* @param use_win32_vk_code If true, key codes are Win32 VK codes translated to evdev internally.
* @return The controller handle, or nullptr on failure.
*
* @note This controller is designed for Gamescope on Linux.
* @note Requires libpipewire >= 0.3.50 and libei >= 1.6.
*/
MAA_FRAMEWORK_API MaaController* MaaGamescopeControllerCreate(uint32_t node_id, const char* eis_socket_path, MaaBool use_win32_vk_code);

/**
* @brief Create a virtual gamepad controller for Windows.
*
Expand Down
4 changes: 4 additions & 0 deletions source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ if(WITH_WLROOTS_CONTROLLER)
add_subdirectory(MaaWlRootsControlUnit)
endif()

if(WITH_GAMESCOPE_CONTROLLER)
add_subdirectory(MaaGamescopeControlUnit)
endif()

add_subdirectory(LibraryHolder)
add_subdirectory(MaaFramework)
add_subdirectory(MaaToolkit)
Expand Down
4 changes: 4 additions & 0 deletions source/LibraryHolder/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ if(WITH_WLROOTS_CONTROLLER)
add_dependencies(LibraryHolder MaaWlRootsControlUnit)
endif()

if(WITH_GAMESCOPE_CONTROLLER)
add_dependencies(LibraryHolder MaaGamescopeControlUnit)
endif()

source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${library_holder_src})
33 changes: 33 additions & 0 deletions source/LibraryHolder/ControlUnit/ControlUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "MaaControlUnit/CustomControlUnitAPI.h"
#include "MaaControlUnit/DbgControlUnitAPI.h"
#include "MaaControlUnit/GamepadControlUnitAPI.h"
#include "MaaControlUnit/GamescopeControlUnitAPI.h"
#include "MaaControlUnit/MacOSControlUnitAPI.h"
#include "MaaControlUnit/PlayCoverControlUnitAPI.h"
#include "MaaControlUnit/RecordControlUnitAPI.h"
Expand Down Expand Up @@ -394,4 +395,36 @@ std::shared_ptr<MAA_CTRL_UNIT_NS::MacOSControlUnitAPI> MacOSControlUnitLibraryHo
return std::shared_ptr<MAA_CTRL_UNIT_NS::MacOSControlUnitAPI>(control_unit_handle, destroy_control_unit_func);
}

std::shared_ptr<MAA_CTRL_UNIT_NS::GamescopeControlUnitAPI>
GamescopeControlUnitLibraryHolder::create_control_unit(uint32_t node_id, const char* eis_socket_path, MaaBool use_win32_vk_code)
{
if (!load_library(library_dir() / libname_)) {
LogError << "Failed to load library" << VAR(library_dir()) << VAR(libname_);
return nullptr;
}

check_version<GamescopeControlUnitLibraryHolder, decltype(MaaGamescopeControlUnitGetVersion)>(version_func_name_);

auto create_control_unit_func = get_function<decltype(MaaGamescopeControlUnitCreate)>(create_func_name_);
if (!create_control_unit_func) {
LogError << "Failed to get function create_control_unit";
return nullptr;
}

auto destroy_control_unit_func = get_function<decltype(MaaGamescopeControlUnitDestroy)>(destroy_func_name_);
if (!destroy_control_unit_func) {
LogError << "Failed to get function destroy_control_unit";
return nullptr;
}

auto control_unit_handle = create_control_unit_func(node_id, eis_socket_path, use_win32_vk_code);

if (!control_unit_handle) {
LogError << "Failed to create control unit";
return nullptr;
}

return std::shared_ptr<MAA_CTRL_UNIT_NS::GamescopeControlUnitAPI>(control_unit_handle, destroy_control_unit_func);
}

MAA_NS_END
27 changes: 27 additions & 0 deletions source/MaaFramework/API/MaaFramework.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,33 @@ MaaController* MaaWlRootsControllerCreate(const char* wlr_socket_path, MaaBool u
#endif
}

MaaController* MaaGamescopeControllerCreate(uint32_t node_id, const char* eis_socket_path, MaaBool use_win32_vk_code)
{
LogFunc << VAR(node_id) << VAR(eis_socket_path) << VAR(use_win32_vk_code);

#ifndef __linux__

LogError << "This API " << __FUNCTION__ << " is only available on Linux";
return nullptr;

#else

if (!eis_socket_path) {
LogError << "eis_socket_path is null";
return nullptr;
}

auto control_unit = MAA_NS::GamescopeControlUnitLibraryHolder::create_control_unit(node_id, eis_socket_path, use_win32_vk_code);

if (!control_unit) {
LogError << "Failed to create control unit";
return nullptr;
}

return new MAA_CTRL_NS::ControllerAgent(std::move(control_unit));
#endif
}

void MaaControllerDestroy(MaaController* ctrl)
{
LogFunc << VAR_VOIDP(ctrl);
Expand Down
35 changes: 35 additions & 0 deletions source/MaaGamescopeControlUnit/API/GamescopeControlUnitAPI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "MaaControlUnit/GamescopeControlUnitAPI.h"

#include "MaaUtils/Logger.h"
#include "Manager/GamescopeControlUnitMgr.h"

const char* MaaGamescopeControlUnitGetVersion()
{
#pragma message("MaaGamescopeControlUnit MAA_VERSION: " MAA_VERSION)

return MAA_VERSION;
}

MaaGamescopeControlUnitHandle MaaGamescopeControlUnitCreate(uint32_t node_id, const char* eis_socket_path, MaaBool use_win32_vk_code)
{
using namespace MAA_CTRL_UNIT_NS;

LogFunc << VAR(node_id) << VAR(eis_socket_path) << VAR(use_win32_vk_code);

if (!eis_socket_path) {
LogError << "eis_socket_path is null or empty";
return nullptr;
}

auto unit_mgr = std::make_unique<GamescopeControlUnitMgr>(node_id, eis_socket_path, use_win32_vk_code);
return unit_mgr.release();
}

void MaaGamescopeControlUnitDestroy(MaaGamescopeControlUnitHandle handle)
{
LogFunc << VAR_VOIDP(handle);

if (handle) {
delete handle;
}
}
28 changes: 28 additions & 0 deletions source/MaaGamescopeControlUnit/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
file(GLOB_RECURSE maa_gamescope_control_unit_src *.h *.hpp *.cpp)
file(GLOB_RECURSE maa_gamescope_control_unit_header ${MAA_PUBLIC_INC}/MaaControlUnit/GamescopeControlUnitAPI.h ${MAA_PUBLIC_INC}/MaaControlUnit/ControlUnitAPI.h)

add_library(MaaGamescopeControlUnit SHARED ${maa_gamescope_control_unit_src} ${maa_gamescope_control_unit_header})

target_include_directories(MaaGamescopeControlUnit
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${MAA_PRIVATE_INC} ${MAA_PUBLIC_INC})

find_path(PipeWire_INCLUDE_DIR pipewire/pipewire.h PATH_SUFFIXES pipewire-0.3)
find_path(Spa_INCLUDE_DIR spa/param/video/format-utils.h PATH_SUFFIXES spa-0.2)
find_library(PipeWire_LIBS pipewire-0.3)
find_path(LibEI_INCLUDE_DIR libei.h PATH_SUFFIXES libei-1.0)
find_library(LibEI_LIBS ei)

target_include_directories(MaaGamescopeControlUnit SYSTEM PRIVATE ${PipeWire_INCLUDE_DIR} ${Spa_INCLUDE_DIR} ${LibEI_INCLUDE_DIR})
target_link_libraries(MaaGamescopeControlUnit PRIVATE MaaUtils HeaderOnlyLibraries ${OpenCV_LIBS} ${PipeWire_LIBS} ${LibEI_LIBS})

target_compile_definitions(MaaGamescopeControlUnit PRIVATE MAA_CONTROL_UNIT_EXPORTS)

add_dependencies(MaaGamescopeControlUnit MaaUtils)

install(
TARGETS MaaGamescopeControlUnit
RUNTIME DESTINATION bin
LIBRARY DESTINATION bin
)

source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${maa_gamescope_control_unit_src})
Loading
Loading