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
9 changes: 9 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Summary

What does this PR do?

## Screenshots

## Manual verification

What did you test on hardware? (Steam Controller chord open/close, touchpad typing, split layout, touchscreen taps, tray toggles.)
7 changes: 2 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
cache: pip
- name: Set up mise
uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5

- name: Install dependencies
run: python -m pip install --upgrade pip && python -m pip install --prefer-binary -r requirements.txt
Expand Down
112 changes: 112 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: Tests

on:
pull_request:
branches: [main]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write

jobs:
tests:
name: tests
runs-on: windows-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up mise
id: setup-mise
uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5
- name: Install dependencies
id: install-deps
working-directory: windows
run: |
python -m pip install --upgrade pip
python -m pip install --prefer-binary -r ../requirements.txt -r requirements-dev.txt
- name: Run tests
id: run-tests
working-directory: windows
run: |
python -m pytest tests -q 2>&1 | Out-File -FilePath "$env:RUNNER_TEMP\pytest-report.txt" -Encoding utf8
$code = $LASTEXITCODE
"TEST_EXIT=$code" | Out-File -FilePath $env:GITHUB_ENV -Append
exit 0
- name: Report result to PR
if: always()
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$ErrorActionPreference = 'Stop'
$event = Get-Content $env:GITHUB_EVENT_PATH | ConvertFrom-Json
$pr = $event.pull_request.number
if (-not $pr) {
Write-Host "Not a pull_request run - skipping comment"
exit 0
}
$report = "$env:RUNNER_TEMP\pytest-report.txt"
$out = ""
if (Test-Path $report) {
$out = Get-Content -Raw $report
}
$exit = -1
if ($env:TEST_EXIT -ne $null) { $exit = [int]$env:TEST_EXIT }

$summary = "no pytest summary captured"
$matches = [regex]::Matches($out, '(?m)^\s*\d+ passed.*$')
if ($matches.Count -gt 0) {
$summary = $matches[$matches.Count - 1].Value.Trim()
}

$failedTests = @()
foreach ($line in ($out -split "`r?`n")) {
if ($line -match '^FAILED\s+([^\s]+)') { $failedTests += $Matches[1] }
}

$verdict = if ($exit -eq 0) { "**Result:** PASSED" } else { "**Result:** FAILED (exit $exit)" }
$conclusions = "mise=${{ steps.setup-mise.conclusion }}, deps=${{ steps.install-deps.conclusion }}, tests=${{ steps.run-tests.conclusion }}"
if ($conclusions -match 'failure|skipped') {
$verdict = $verdict + "`n`n_Step conclusions — $conclusions — check the workflow log._"
}
$failBlock = ""
if ($failedTests.Count -gt 0) {
$failBlock = "**Failed tests:**`n" + ($failedTests -join "`n")
}

$fence = '```'
$body = '<!-- dualtouch-tests -->' + "`n" +
"## Tests on $env:GITHUB_SHA" + "`n`n" +
$verdict + "`n`n" +
"**Summary:** $summary" + "`n" +
$failBlock + "`n`n" +
'<details><summary>Full output</summary>' + "`n`n" +
$fence + 'text' + "`n" +
$out + "`n" +
$fence + "`n" +
'</details>'

$headers = @{
Authorization = "Bearer $env:GH_TOKEN"
"X-GitHub-Api-Version" = "2022-11-28"
}
$api = "https://api.github.com/repos/$env:GITHUB_REPOSITORY"
$existing = Invoke-RestMethod -Uri "$api/issues/$pr/comments?per_page=100" -Headers $headers -Method Get
$mine = $existing | Where-Object { $_.body -like '<!-- dualtouch-tests -->*' } | Select-Object -First 1
$payload = @{ body = $body } | ConvertTo-Json
if ($mine) {
Invoke-RestMethod -Uri "$api/issues/comments/$($mine.id)" -Headers $headers -Method Patch -Body $payload -ContentType "application/json" | Out-Null
} else {
Invoke-RestMethod -Uri "$api/issues/$pr/comments" -Headers $headers -Method Post -Body $payload -ContentType "application/json" | Out-Null
}
Write-Host "Comment posted for PR #$pr"

if ($exit -ne 0) {
Write-Host "::error::Tests failed (exit $exit)"
exit 1
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ menus), or by hand-editing `settings.json` for finer control.
| `skin` | `"Gruvbox"` | Keyboard skin: Steam OSK themes load from the Steam install; Gruvbox is the bundled original |
| `osk_size` | `"medium"` | `"small"`/`"medium"`/`"full"` |
| `osk_transparency` | `"off"` | `"off"`/`"low"`/`"medium"`/`"high"` |
| `osk_split_layout` | `false` | Split the keyboard into left/right halves with a middle gap; each touchpad covers its own half |
| `key_sound_enabled_sc` | `true` | Steam keyboard click sound |
| `rumble_enabled_sc` | `true` | Haptics on key clicks and trigger pulls |
| `steam_kbd_layer` | `true` | Dispatch the `forceinputappid` keyboard layer while open |
Expand Down
170 changes: 137 additions & 33 deletions mise.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion mise.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[tools]
prek = "1.39.10"
python = "3.12"
prek = "0.4.13"
ruff = "0.16.3"
"github:DetachHead/basedpyright" = "1.39.10"
6 changes: 6 additions & 0 deletions windows/appsettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@
# watcher (works elevated, unlike Steam Input's injected key chords).
# Tray "Startup → Open Keyboard Chord". Default "X" (Steam+X).
"sc_osk_open_chord": "X",
# Split keyboard layout (tray "Steam Controller -> Split Keyboard"):
# split the keyboard into left/right halves with a middle gap, each
# touchpad covering its own half (better ergonomics — no cross-body
# reach). Applied on the next OSK open (or live while open, like the
# other SC toggles).
"osk_split_layout": False,
# Per-foreground-app remembered OSK "Move" position: {exe name (lowercase):
# position index 0-5}. Remembered per app so the keyboard reopens where the
# user left it in each app; apps without an entry keep the current spot
Expand Down
7 changes: 7 additions & 0 deletions windows/sdl3w/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class SDL_Surface(ctypes.Structure):

# Hints
SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN = b"SDL_WINDOW_ACTIVATE_WHEN_SHOWN"
SDL_HINT_TOUCH_MOUSE_EVENTS = b"SDL_TOUCH_MOUSE_EVENTS"


# ===========================================================================
Expand Down Expand Up @@ -253,6 +254,12 @@ def get_error():
ctypes.POINTER(ctypes.c_int),
],
)
SDL_SetWindowSize = _bind(
SDL,
"SDL_SetWindowSize",
ctypes.c_bool,
[ctypes.c_void_p, ctypes.c_int, ctypes.c_int],
)
SDL_ShowWindow = _bind(SDL, "SDL_ShowWindow", ctypes.c_bool, [ctypes.c_void_p])
SDL_HideWindow = _bind(SDL, "SDL_HideWindow", ctypes.c_bool, [ctypes.c_void_p])
SDL_SetWindowAlwaysOnTop = _bind(
Expand Down
Loading