Skip to content
Open
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
39 changes: 38 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ jobs:
- name: Install dep
run: |
sudo apt-get update -y
sudo apt-get install -y ninja-build cmake ccache
sudo apt-get install -y ninja-build cmake ccache libpipewire-0.3-dev libdbus-1-dev

- uses: pnpm/action-setup@v4
with:
Expand Down Expand Up @@ -206,6 +206,43 @@ jobs:
run: |
python3 tools/maadeps-download.py ${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-linux

- name: Inject ARM64 Dependencies
if: matrix.arch == 'aarch64'
run: |
echo "Installing ARM64 cross-compile dependencies..."
sudo dpkg --add-architecture arm64
sudo sed -i 's/^deb http/deb [arch=amd64] http/g' /etc/apt/sources.list 2>/dev/null || true
sudo sed -i 's/^deb mirror/deb [arch=amd64] mirror/g' /etc/apt/sources.list 2>/dev/null || true
if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then
sudo sed -i 's/^Types: deb$/Types: deb\nArchitectures: amd64/g' /etc/apt/sources.list.d/ubuntu.sources
fi

sudo sh -c 'echo "Types: deb" > /etc/apt/sources.list.d/ubuntu-arm64.sources'
sudo sh -c 'echo "URIs: http://ports.ubuntu.com/ubuntu-ports" >> /etc/apt/sources.list.d/ubuntu-arm64.sources'
sudo sh -c 'echo "Suites: noble noble-updates noble-security" >> /etc/apt/sources.list.d/ubuntu-arm64.sources'
sudo sh -c 'echo "Components: main universe restricted multiverse" >> /etc/apt/sources.list.d/ubuntu-arm64.sources'
sudo sh -c 'echo "Architectures: arm64" >> /etc/apt/sources.list.d/ubuntu-arm64.sources'

sudo apt-get update -y

mkdir -p arm64_deps && cd arm64_deps
sudo apt-get download libdbus-1-dev:arm64 libdbus-1-3:arm64 libpipewire-0.3-dev:arm64 libpipewire-0.3-0t64:arm64 libspa-0.2-dev:arm64 libspa-0.2-modules:arm64

mkdir -p ../extracted
for deb in *.deb; do
dpkg -x "$deb" ../extracted
done
cd ..

SYSROOT_DIR="${GITHUB_WORKSPACE}/source/MaaUtils/MaaDeps/x-tools/aarch64-linux-gnu/aarch64-linux-gnu/sysroot"

# 确保目录结构存在后再拷贝
sudo mkdir -p "$SYSROOT_DIR/usr/include"
sudo mkdir -p "$SYSROOT_DIR/usr/lib"

sudo cp -r ./extracted/usr/include/* "$SYSROOT_DIR/usr/include/"
sudo cp -r ./extracted/usr/lib/aarch64-linux-gnu/* "$SYSROOT_DIR/usr/lib/"

- name: Prepare node_modules
run: |
cd source/binding/NodeJS
Expand Down
39 changes: 38 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ jobs:
- name: Install dep
run: |
sudo apt-get update -y
sudo apt-get install -y ninja-build cmake ccache
sudo apt-get install -y ninja-build cmake ccache libpipewire-0.3-dev libdbus-1-dev

# https://github.com/MaaXYZ/MaaFramework/actions/runs/5643408179/job/15285186255
- uses: actions/checkout@v4
Expand All @@ -202,6 +202,43 @@ jobs:
run: |
python3 tools/maadeps-download.py ${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-linux

- name: Inject ARM64 Dependencies
if: matrix.arch == 'aarch64'
run: |
echo "Installing ARM64 cross-compile dependencies..."
sudo dpkg --add-architecture arm64
sudo sed -i 's/^deb http/deb [arch=amd64] http/g' /etc/apt/sources.list 2>/dev/null || true
sudo sed -i 's/^deb mirror/deb [arch=amd64] mirror/g' /etc/apt/sources.list 2>/dev/null || true
if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then
sudo sed -i 's/^Types: deb$/Types: deb\nArchitectures: amd64/g' /etc/apt/sources.list.d/ubuntu.sources
fi

sudo sh -c 'echo "Types: deb" > /etc/apt/sources.list.d/ubuntu-arm64.sources'
sudo sh -c 'echo "URIs: http://ports.ubuntu.com/ubuntu-ports" >> /etc/apt/sources.list.d/ubuntu-arm64.sources'
sudo sh -c 'echo "Suites: noble noble-updates noble-security" >> /etc/apt/sources.list.d/ubuntu-arm64.sources'
sudo sh -c 'echo "Components: main universe restricted multiverse" >> /etc/apt/sources.list.d/ubuntu-arm64.sources'
sudo sh -c 'echo "Architectures: arm64" >> /etc/apt/sources.list.d/ubuntu-arm64.sources'

sudo apt-get update -y

mkdir -p arm64_deps && cd arm64_deps
sudo apt-get download libdbus-1-dev:arm64 libdbus-1-3:arm64 libpipewire-0.3-dev:arm64 libpipewire-0.3-0t64:arm64 libspa-0.2-dev:arm64 libspa-0.2-modules:arm64

mkdir -p ../extracted
for deb in *.deb; do
dpkg -x "$deb" ../extracted
done
cd ..

SYSROOT_DIR="${GITHUB_WORKSPACE}/source/MaaUtils/MaaDeps/x-tools/aarch64-linux-gnu/aarch64-linux-gnu/sysroot"

# 确保目录结构存在后再拷贝
sudo mkdir -p "$SYSROOT_DIR/usr/include"
sudo mkdir -p "$SYSROOT_DIR/usr/lib"

sudo cp -r ./extracted/usr/include/* "$SYSROOT_DIR/usr/include/"
sudo cp -r ./extracted/usr/lib/aarch64-linux-gnu/* "$SYSROOT_DIR/usr/lib/"

- uses: pnpm/action-setup@v4
with:
version: latest
Expand Down
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_KWIN_CONTROLLER "build with KWin controller for Linux (uinput + PipeWire)" 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_KWIN_CONTROLLER AND (NOT LINUX OR ANDROID))
message(STATUS "Not on Linux, disable WITH_KWIN_CONTROLLER")
set(WITH_KWIN_CONTROLLER OFF)
endif()

if(WITH_MAA_AGENT)
find_package(cppzmq REQUIRED)
endif()
Expand Down
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 KWinControlUnitAPI
: public ControlUnitAPI
, public ScrollableUnit
, public RelativeMovableUnit
{
public:
virtual ~KWinControlUnitAPI() = 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 MaaKWinControlUnitHandle = MAA_CTRL_UNIT_NS::KWinControlUnitAPI*;
using MaaGamepadControlUnitHandle = MAA_CTRL_UNIT_NS::GamepadControlUnitAPI*;
using MaaCustomControlUnitHandle = MAA_CTRL_UNIT_NS::CustomControlUnitAPI*;
using MaaReplayControlUnitHandle = MAA_CTRL_UNIT_NS::FullControlUnitAPI*;
Expand Down
23 changes: 23 additions & 0 deletions include/MaaControlUnit/KWinControlUnitAPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

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

#ifdef __cplusplus
extern "C"
{
#endif

MAA_CONTROL_UNIT_API const char* MaaKWinControlUnitGetVersion();

MAA_CONTROL_UNIT_API MaaKWinControlUnitHandle MaaKWinControlUnitCreate(const char* device_node, int screen_width, int screen_height);

MAA_CONTROL_UNIT_API MaaBool MaaKWinControlUnitConnect(MaaKWinControlUnitHandle handle);

MAA_CONTROL_UNIT_API MaaBool MaaKWinControlUnitTestScreencap(MaaKWinControlUnitHandle handle);

MAA_CONTROL_UNIT_API void MaaKWinControlUnitDestroy(MaaKWinControlUnitHandle handle);

#ifdef __cplusplus
}
#endif
1 change: 1 addition & 0 deletions include/MaaControlUnit/MaaControlUnitAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
#include "ReplayControlUnitAPI.h"
#include "Win32ControlUnitAPI.h"
#include "WlRootsControlUnitAPI.h"
#include "KWinControlUnitAPI.h"
16 changes: 16 additions & 0 deletions include/MaaFramework/Instance/MaaController.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,22 @@ extern "C"
*/
MAA_FRAMEWORK_API MaaController* MaaWlRootsControllerCreate(const char* wlr_socket_path, MaaBool use_win32_vk_code);

/**
* @brief Create a KWin (pure Wayland) controller for Linux.
*
* @param device_node The uinput device node path (e.g., "/dev/uinput").
* @param screen_width The screen width in pixels.
* @param screen_height The screen height in pixels.
* @return The controller handle, or nullptr on failure.
*
* @note This controller is designed for KWin (pure Wayland) on Linux.
* @note Input is simulated via /dev/uinput (kernel-level virtual touchscreen).
* @note Screencap is not yet implemented (requires PipeWire / xdg-desktop-portal).
* @note Requires write permission to /dev/uinput (typically via the "input" group).
* @note Only single touch is supported (contact must be 0).
*/
MAA_FRAMEWORK_API MaaController* MaaKWinControllerCreate(const char* device_node, int screen_width, int screen_height);

/**
* @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_KWIN_CONTROLLER)
add_subdirectory(MaaKWinControlUnit)
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_KWIN_CONTROLLER)
add_dependencies(LibraryHolder MaaKWinControlUnit)
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 @@ -13,6 +13,7 @@
#include "MaaControlUnit/ReplayControlUnitAPI.h"
#include "MaaControlUnit/Win32ControlUnitAPI.h"
#include "MaaControlUnit/WlRootsControlUnitAPI.h"
#include "MaaControlUnit/KWinControlUnitAPI.h"
#include "MaaUtils/Logger.h"
#include "MaaUtils/Runtime.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::KWinControlUnitAPI>
KWinControlUnitLibraryHolder::create_control_unit(const char* device_node, int screen_width, int screen_height)
{
if (!load_library(library_dir() / libname_)) {
LogError << "Failed to load library" << VAR(library_dir()) << VAR(libname_);
return nullptr;
}

check_version<KWinControlUnitLibraryHolder, decltype(MaaKWinControlUnitGetVersion)>(version_func_name_);

auto create_control_unit_func = get_function<decltype(MaaKWinControlUnitCreate)>(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(MaaKWinControlUnitDestroy)>(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(device_node, screen_width, screen_height);

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

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

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

MaaController* MaaKWinControllerCreate(const char* device_node, int screen_width, int screen_height)
{
LogFunc << VAR(device_node) << VAR(screen_width) << VAR(screen_height);

#ifndef __linux__

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

#else

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

if (screen_width <= 0 || screen_height <= 0) {
LogError << "Invalid screen dimensions" << VAR(screen_width) << VAR(screen_height);
return nullptr;
}

auto control_unit = MAA_NS::KWinControlUnitLibraryHolder::create_control_unit(device_node, screen_width, screen_height);

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
Loading
Loading