Skip to content
Merged
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
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ jobs:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failure
# In the format of release/<version>.SHA256SUMS.txt, and what the library needs from the system.
cd build
if [ "$RUNNER_OS" = macOS ]; then
lib=libmodavd_extras.dylib
otool -l "$lib" | grep -A3 LC_BUILD_VERSION | grep minos
else
lib=libmodavd_extras.so
objdump -T "$lib" | grep -o 'GLIBC_[0-9.]*' | sort -uV | tail -n 1
fi
cmake -E sha256sum "$lib"

privacy:
runs-on: ubuntu-24.04
Expand Down
100 changes: 79 additions & 21 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ concurrency:
cancel-in-progress: false

jobs:
build:
check:
if: github.repository == 'ModAVD/ModAVDExtras' && !github.event.deleted
runs-on: windows-2025
timeout-minutes: 20
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
- name: verify tag, ancestry and version
- name: verify tag, ancestry, version and the hash list
shell: pwsh
env:
TAG: ${{ github.ref_name }}
Expand All @@ -37,6 +37,24 @@ jobs:
if ($found.Count -ne 1) { throw "expected one project(ModAVDExtras VERSION ...) line in CMakeLists.txt, found $($found.Count)" }
$want = $found[0].Matches[0].Groups[1].Value
if ($want -cne $version) { throw "CMakeLists.txt version=$want, tag=$version" }
# Exactly one line per release file, sorted by name, as the CI jobs print them.
$names = @('external_renderer_ipc.dll', 'libmodavd_extras.dylib', 'libmodavd_extras.so', 'modavd_extras.dll')
$sums = Get-Content -Raw "release/$version.SHA256SUMS.txt"
$pattern = '\A' + (($names | ForEach-Object { '[0-9a-f]{64} ' + [regex]::Escape($_) + '\n' }) -join '') + '\z'
if ($sums -cnotmatch $pattern) { throw "release/$version.SHA256SUMS.txt must be one '<sha256> <name>' line for each of: $($names -join ', ')" }
# The notes are written before CI prints the hashes, with @NAME@ where they go.
if ((Get-Content -Raw "release/$version.md") -cmatch '@[A-Z_]+@') { throw "release/$version.md still has a placeholder: $($Matches[0])" }

windows:
needs: check
runs-on: windows-2025
timeout-minutes: 20
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: build, test and verify
shell: pwsh
env:
Expand All @@ -50,22 +68,60 @@ jobs:
ctest --test-dir build -C Release --output-on-failure
if ($LASTEXITCODE -ne 0) { throw 'tests failed' }
$version = $env:TAG.Substring(1)
# The list must be exactly these lines, in this order: what ci.yml prints for the same build.
$names = @('external_renderer_ipc.dll', 'modavd_extras.dll')
$lines = foreach ($name in $names) { '{0} {1}' -f (Get-FileHash "build/Release/$name" -Algorithm SHA256).Hash.ToLowerInvariant(), $name }
$lines
if ((Get-Content -Raw "release/$version.SHA256SUMS.txt") -cne (($lines -join "`n") + "`n")) { throw "release/$version.SHA256SUMS.txt must be exactly the lines above" }
$sums = (Get-Content -Raw "release/$version.SHA256SUMS.txt") -split "`n"
New-Item -ItemType Directory -Force artifacts | Out-Null
foreach ($name in $names) { Copy-Item "build/Release/$name" artifacts }
Copy-Item "release/$version.SHA256SUMS.txt" artifacts/SHA256SUMS.txt
foreach ($name in 'external_renderer_ipc.dll', 'modavd_extras.dll') {
$line = '{0} {1}' -f (Get-FileHash "build/Release/$name" -Algorithm SHA256).Hash.ToLowerInvariant(), $name
$line
if ($sums -cnotcontains $line) { throw "release/$version.SHA256SUMS.txt does not have this line" }
Copy-Item "build/Release/$name" artifacts
}
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-windows
path: artifacts
if-no-files-found: error

posix:
needs: check
strategy:
matrix:
os: [ubuntu-24.04, macos-15]
runs-on: ${{ matrix.os }}
timeout-minutes: 20
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: build, test and verify
shell: bash
env:
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
# The same build as ci.yml's posix job.
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failure
if [ "$RUNNER_OS" = macOS ]; then lib=libmodavd_extras.dylib; else lib=libmodavd_extras.so; fi
line="$(cd build && cmake -E sha256sum "$lib")"
echo "$line"
if ! grep -Fxq -- "$line" "release/${TAG#v}.SHA256SUMS.txt"; then
echo "release/${TAG#v}.SHA256SUMS.txt does not have this line" >&2
exit 1
fi
mkdir artifacts
cp "build/$lib" artifacts/
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-assets
name: release-${{ matrix.os }}
path: artifacts
if-no-files-found: error

draft:
needs: build
needs: [windows, posix]
runs-on: windows-2025
timeout-minutes: 10
permissions:
Expand All @@ -77,26 +133,27 @@ jobs:
fetch-depth: 1
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-assets
pattern: release-*
path: artifacts
- name: verify artifact
merge-multiple: true
- name: verify artifacts
shell: pwsh
env:
TAG: ${{ github.ref_name }}
run: |
# Checked against the tag's own list, not only the copy that travelled with the artifact.
# Checked against the tag's own list, not only what the build jobs reported.
$version = $env:TAG.Substring(1)
$sums = Get-Content -Raw "release/$version.SHA256SUMS.txt"
if ((Get-Content -Raw artifacts/SHA256SUMS.txt) -cne $sums) { throw 'artifact SHA256SUMS.txt differs from the tag' }
$names = @('external_renderer_ipc.dll', 'modavd_extras.dll')
$names = @('external_renderer_ipc.dll', 'libmodavd_extras.dylib', 'libmodavd_extras.so', 'modavd_extras.dll')
$have = (Get-ChildItem artifacts -File | ForEach-Object Name | Sort-Object) -join ','
$expect = (@($names + 'SHA256SUMS.txt') | Sort-Object) -join ','
$expect = ($names | Sort-Object) -join ','
if ($have -cne $expect) { throw "unexpected artifact files: $have" }
foreach ($name in $names) {
$want = [regex]::Match($sums, "(?m)^([0-9a-f]{64}) $([regex]::Escape($name))\r?$").Groups[1].Value
$got = (Get-FileHash "artifacts/$name" -Algorithm SHA256).Hash.ToLowerInvariant()
if (-not $want -or $got -ne $want) { throw "downloaded $name hash mismatch" }
}
Copy-Item "release/$version.SHA256SUMS.txt" artifacts/SHA256SUMS.txt
- name: create draft
shell: pwsh
env:
Expand All @@ -109,11 +166,12 @@ jobs:
$tags = gh api "repos/$env:GH_REPO/releases?per_page=100" --paginate --jq '.[].tag_name'
if ($LASTEXITCODE -ne 0) { throw 'cannot list releases' }
if ($tags -contains $env:TAG) { throw "a release for $env:TAG already exists (draft or published)" }
# --verify-tag only checks that the tag exists: make sure it still points at the commit the build job built.
# --verify-tag only checks that the tag exists: make sure it still points at the commit the build jobs built.
$head = git rev-parse HEAD
if ($LASTEXITCODE -ne 0) { throw 'git rev-parse failed' }
$tagCommit = gh api "repos/$env:GH_REPO/commits/tags/$env:TAG" --jq .sha
if ($LASTEXITCODE -ne 0) { throw "cannot resolve $env:TAG" }
if ($tagCommit -ne $head) { throw "$env:TAG now points at $tagCommit; this run built $head" }
gh release create $env:TAG --draft --verify-tag --notes-file "release/$version.md" artifacts/modavd_extras.dll artifacts/external_renderer_ipc.dll artifacts/SHA256SUMS.txt
$files = @('modavd_extras.dll', 'external_renderer_ipc.dll', 'libmodavd_extras.so', 'libmodavd_extras.dylib', 'SHA256SUMS.txt') | ForEach-Object { "artifacts/$_" }
gh release create $env:TAG --draft --verify-tag --notes-file "release/$version.md" @files
if ($LASTEXITCODE -ne 0) { throw 'gh release create failed' }
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.20)
project(ModAVDExtras VERSION 0.1.1 LANGUAGES C)
# macOS: the oldest release the library loads on. Without it the build machine's SDK version is used.
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum macOS version")
project(ModAVDExtras VERSION 0.1.2 LANGUAGES C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@

- v0.1:只有截图。点击、按键、文字的函数已经声明,现在返回 `MODAVD_E_UNSUPPORTED`。
- Windows:截图这套逻辑在 ModAVD 上已经用了一个多月,形式是下面的兼容外壳。
- Linux、macOS:能编,单元测试在 CI 上跑;还没有在模拟器上实测。
- macOS(Apple Silicon):11-09 有贡献者用官方 Emulator 37.2.13、API 35 ARM64 实测过截图。
- Linux:能编,单元测试在 CI 上跑;还没有在模拟器上实测。
- [Release](https://github.com/ModAVD/ModAVDExtras/releases) 从 v0.1.2 起带三个平台的库:Windows x64 的 `modavd_extras.dll`、macOS arm64 的 `libmodavd_extras.dylib`、Linux x64 的 `libmodavd_extras.so`,外壳 `external_renderer_ipc.dll` 只有 Windows 版。

## 接口

Expand Down Expand Up @@ -109,5 +111,6 @@ ModAVDExtras is a small C library that reads the screen of a [ModAVD](https://gi
- The library finds the console port from an adb address, starts frame sharing through the console when it is off, copies a frame in the format the caller asks for (copying again if a new frame arrived meanwhile), and reports whether capture is usable. It never falls back to adb by itself.
- Version 0.1 does screen capture only; the input functions are declared and return `MODAVD_E_UNSUPPORTED`. See [include/modavd_extras.h](include/modavd_extras.h).
- `external_renderer_ipc.dll` is a Windows-only compatibility shell. It exports the four capture functions that MaaCore looks up in MuMu's DLL, on top of the same code, so MaaCore versions that do not know ModAVD can still capture from it. AUTO-MAS uses it today. It will be retired once MaaCore can connect to ModAVD directly.
- Status: Windows has used this capture code for over a month through the shell below; on Apple Silicon macOS a contributor tested capture with the official Emulator 37.2.13 and API 35 ARM64; Linux builds and passes the unit tests in CI but has not been tested with an emulator. From v0.1.2 the [releases](https://github.com/ModAVD/ModAVDExtras/releases) carry `modavd_extras.dll` (Windows x64), `libmodavd_extras.dylib` (macOS arm64) and `libmodavd_extras.so` (Linux x64), plus the Windows-only shell.
- Build: `build.ps1` on Windows (Visual Studio 2022 Build Tools, CMake, Ninja); CMake on Linux and macOS. The unit tests need no emulator.
- License: LGPL-3.0-or-later ([LICENSE](LICENSE), [COPYING](COPYING)). Only code written for this project goes in; code from GPL or AGPL projects such as MaaCore does not.
4 changes: 4 additions & 0 deletions release/0.1.2.SHA256SUMS.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
8a0a82a59745da36e93eba6278dbacfab67616a415fd0780b24a9b03f7bb99ed external_renderer_ipc.dll
84fbbdd76fc3262bae9e058777d4bc20c800426883ce3f47ac29ec7d2ed5cb62 libmodavd_extras.dylib
c18c2b2f1f00346b944c2bd85213b8654b557fbe7dc7bd04670e84e782cbef56 libmodavd_extras.so
a5ef972919d76c7821225ada2d67d322b2f0128e195ba34d69bbf9aa9759daba modavd_extras.dll
48 changes: 48 additions & 0 deletions release/0.1.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# ModAVDExtras v0.1.2

- `modavd_extras.dll`:`a5ef972919d76c7821225ada2d67d322b2f0128e195ba34d69bbf9aa9759daba`
- `external_renderer_ipc.dll`:`8a0a82a59745da36e93eba6278dbacfab67616a415fd0780b24a9b03f7bb99ed`
- `libmodavd_extras.dylib`:`84fbbdd76fc3262bae9e058777d4bc20c800426883ce3f47ac29ec7d2ed5cb62`
- `libmodavd_extras.so`:`c18c2b2f1f00346b944c2bd85213b8654b557fbe7dc7bd04670e84e782cbef56`

这一版起 Release 也带 macOS、Linux 的库;另外修了 Linux、macOS 上截图会拿到旧画面的问题。接口版本还是 1,头文件只改了注释,调用方不用重编。

## 新增

- macOS arm64 的 `libmodavd_extras.dylib`(macOS 12 起能载入)和 Linux x64 的 `libmodavd_extras.so`(要 glibc 2.34 或更新)。
- 接口和 Windows 的 `modavd_extras.dll` 一样,见 `include/modavd_extras.h`。
- macOS 上有贡献者在 Apple Silicon 上用官方 Emulator 37.2.13、API 35 ARM64 实测过截图。Linux 还没有在模拟器上实测。

## 修复

- Linux、macOS:模拟器退出后共享内存还在,下次冷启动时库直接认了上次的末帧,截到的一直是旧画面(#8)。现在 `modavd_open` 默认先经控制台开流,成功了才打开映射;控制台鉴权或命令失败时返回 `MODAVD_E_IO`,不再被旧映射掩盖。确定映射属于当前实例、而且已经在开流的,可以带 `MODAVD_OPEN_NO_START` 跳过控制台。
- 所有平台:开了流、第一帧还没到时,`modavd_capture` 返回 `MODAVD_E_NOT_READY`,以前返回成功和一张全黑的图(#8)。稍后再调就行。
- `external_renderer_ipc.dll`:行为不变。第一帧到之前照旧交出尺寸和黑图、返回成功,和它替换的 DLL 一样,因为 MaaCore 连上时马上要问尺寸,失败就放弃这条路(#9)。
- `modavd_version_string()` 返回 `"0.1.2"`。

## 许可证与源码

- LGPL-3.0-or-later。正文是 tag 下的 [LICENSE](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.2/LICENSE)(LGPL-3.0)和 [COPYING](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.2/COPYING)(GPL-3.0);转发这些库时请一起带上。
- 源码:<https://github.com/ModAVD/ModAVDExtras/tree/v0.1.2>。

## 下载与核对

四个文件都由本仓库的 release workflow 在 GitHub 上编出,和 tag 下的 `release/0.1.2.SHA256SUMS.txt` 核对过才上传:

- Windows:`windows-2025`,`cmake -A x64`,`--config Release`。两个 DLL 都静态链接 C 运行库,只依赖 `KERNEL32`、`WS2_32`。编译带 `/Brepro`:同样的命令、同一版本的 MSVC 和 Windows SDK 能编出相同的字节。MSVC 版本在 workflow 日志里 CMake 配置那一步,Windows SDK 跟着 runner 镜像,镜像版本在「Set up job」那一步。
- macOS:`macos-15`(Apple Silicon),`cmake -DCMAKE_BUILD_TYPE=Release`。
- Linux:`ubuntu-24.04`(x64),`cmake -DCMAKE_BUILD_TYPE=Release`。

请用附件 `SHA256SUMS.txt` 核对:Windows 上 `Get-FileHash .\modavd_extras.dll -Algorithm SHA256`,macOS 上 `shasum -a 256 libmodavd_extras.dylib`,Linux 上 `sha256sum libmodavd_extras.so`。

## English

From this release the releases also carry the macOS and Linux libraries, and a Linux / macOS issue where capture returned a stale frame is fixed. The API version is still 1 and only comments changed in the header, so callers need not rebuild.

- New: `libmodavd_extras.dylib` for macOS arm64 (loads on macOS 12 and later) and `libmodavd_extras.so` for Linux x64 (needs glibc 2.34 or later), with the same API as `modavd_extras.dll` (`include/modavd_extras.h`). A contributor tested capture on Apple Silicon with the official Emulator 37.2.13 and API 35 ARM64; Linux has not been tested with an emulator yet.
- Fixed on Linux and macOS: the shared memory outlives the emulator, so on the next cold boot the library took the last frame of the previous run and kept returning it (#8). By default `modavd_open` now starts frame sharing through the console before it opens the mapping; console auth or command failures return `MODAVD_E_IO` instead of being hidden by an old mapping. Callers that know the mapping belongs to the running instance and sharing is on can pass `MODAVD_OPEN_NO_START` to skip the console.
- Fixed on all platforms: after sharing starts and before the first frame arrives, `modavd_capture` returns `MODAVD_E_NOT_READY` instead of success and an all-black frame (#8). Call again a little later.
- `external_renderer_ipc.dll`: unchanged behaviour. Before the first frame it still reports the size and a black frame with success, as the DLL it replaces did, because MaaCore asks for the size right after connecting and gives up on that path if it fails (#9).
- `modavd_version_string()` returns `"0.1.2"`.
- License: LGPL-3.0-or-later; the texts are [LICENSE](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.2/LICENSE) (LGPL-3.0) and [COPYING](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.2/COPYING) (GPL-3.0) at the tag. Ship them with the libraries. Source: <https://github.com/ModAVD/ModAVDExtras/tree/v0.1.2>.
- All four files are built by this repository's release workflow on GitHub (Windows: `windows-2025`, `cmake -A x64`, `--config Release`, with `/Brepro`, static C runtime, depending only on `KERNEL32` and `WS2_32`; macOS: `macos-15` on Apple Silicon; Linux: `ubuntu-24.04` x64; both `cmake -DCMAKE_BUILD_TYPE=Release`) and checked against `release/0.1.2.SHA256SUMS.txt` at the tag before upload. Check them with the attached `SHA256SUMS.txt`.
Loading