Skip to content

Commit ba24fd3

Browse files
Z_06ZeroAd-06
authored andcommitted
fix(controller): release held input state on task stop
Queue an internal inactive action when ControllerAgent::post_stop() clears pending controller work so held background-managed keys are released even if the matching key-up never runs. Skip Tasker\'s normal end-of-run inactive cleanup while stopping to avoid issuing the release twice. Add a Python binding regression test that blocks inside a custom action after key-down and verifies tasker.post_stop() triggers inactive() before the task exits.
1 parent 7b39a6f commit ba24fd3

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

source/MaaFramework/Controller/ControllerAgent.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,15 @@ void ControllerAgent::post_stop()
277277

278278
need_to_stop_ = true;
279279

280-
if (action_runner_ && action_runner_->running()) {
280+
if (!action_runner_) {
281+
return;
282+
}
283+
284+
if (action_runner_->running()) {
281285
action_runner_->clear();
282286
}
287+
288+
action_runner_->post({ .type = Action::Type::inactive });
283289
}
284290

285291
bool ControllerAgent::running() const

source/MaaFramework/Tasker/Tasker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ bool Tasker::run_task(RunnerId runner_id, TaskPtr task_ptr)
361361
}
362362
notifier_.notify(this, ret ? MaaMsg_Tasker_Task_Succeeded : MaaMsg_Tasker_Task_Failed, cb_detail);
363363

364-
if (controller_ && !task_runner_->pending()) {
364+
if (controller_ && !need_to_stop_ && !task_runner_->pending()) {
365365
controller_->wait(controller_->post_inactive());
366366
}
367367

test/python/binding_test.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import sys
1818
import numpy
1919
import io
20+
import threading
2021

2122
# Fix encoding issues on Windows
2223
if sys.stdout.encoding != "utf-8":
@@ -595,6 +596,9 @@ class MyController(CustomController):
595596
def __init__(self):
596597
super().__init__()
597598
self.count = 0
599+
self.inactive_count = 0
600+
self.key_down_event = threading.Event()
601+
self.inactive_event = threading.Event()
598602

599603
def connect(self) -> bool:
600604
print(" on MyController.connect")
@@ -662,6 +666,7 @@ def input_text(self, text: str) -> bool:
662666
def key_down(self, keycode: int) -> bool:
663667
print(f" on MyController.key_down: {keycode}")
664668
self.count += 1
669+
self.key_down_event.set()
665670
return True
666671

667672
def key_up(self, keycode: int) -> bool:
@@ -674,13 +679,44 @@ def scroll(self, dx: int, dy: int) -> bool:
674679
self.count += 1
675680
return True
676681

682+
def inactive(self) -> bool:
683+
print(" on MyController.inactive")
684+
self.count += 1
685+
self.inactive_count += 1
686+
self.inactive_event.set()
687+
return True
688+
677689
def get_custom_info(self) -> dict:
678690
return {
679691
"custom_key": "custom_value",
680692
"answer": 42,
681693
}
682694

683695

696+
class HoldKeyDownAction(CustomAction):
697+
698+
def __init__(self, release_event: threading.Event):
699+
super().__init__()
700+
self.release_event = release_event
701+
702+
def run(
703+
self,
704+
context: Context,
705+
argv: CustomAction.RunArg,
706+
) -> CustomAction.RunResult:
707+
del argv
708+
709+
controller = context.tasker.controller
710+
key_down_job = controller.post_key_down(87).wait()
711+
if not key_down_job.succeeded:
712+
raise RuntimeError("key_down should succeed in HoldKeyDownAction")
713+
714+
if not self.release_event.wait(5):
715+
raise RuntimeError("timeout waiting to release HoldKeyDownAction")
716+
717+
return CustomAction.RunResult(success=True)
718+
719+
684720
def test_custom_controller():
685721
print("\n=== test_custom_controller ===")
686722

@@ -722,6 +758,48 @@ def test_custom_controller():
722758
print(" PASS: custom controller")
723759

724760

761+
def test_tasker_post_stop_releases_custom_controller():
762+
print("\n=== test_tasker_post_stop_releases_custom_controller ===")
763+
764+
resource = Resource()
765+
release_event = threading.Event()
766+
resource.register_custom_action("HoldKeyDownAction", HoldKeyDownAction(release_event))
767+
resource.override_pipeline(
768+
{
769+
"StopEntry": {
770+
"action": "Custom",
771+
"custom_action": "HoldKeyDownAction",
772+
}
773+
}
774+
)
775+
776+
controller = MyController()
777+
assert controller.post_connection().wait().succeeded, "custom controller should connect"
778+
779+
tasker = Tasker()
780+
tasker.bind(resource, controller)
781+
assert tasker.inited, "tasker should init with custom controller"
782+
783+
task_job = tasker.post_task("StopEntry")
784+
stop_job = None
785+
786+
try:
787+
assert controller.key_down_event.wait(5), "key_down should happen before stop"
788+
789+
stop_job = tasker.post_stop()
790+
assert controller.inactive_event.wait(5), (
791+
"post_stop should trigger controller inactive before the task exits"
792+
)
793+
finally:
794+
release_event.set()
795+
if stop_job is not None:
796+
stop_job.wait()
797+
task_job.wait()
798+
799+
assert controller.inactive_count >= 1, "inactive should be called during stop"
800+
print(" PASS: tasker post_stop releases custom controller state")
801+
802+
725803
# ============================================================================
726804
# Toolkit 测试
727805
# ============================================================================
@@ -845,6 +923,7 @@ def test_background_managed_keys_api(dbg_controller: DbgController):
845923

846924
# 测试 CustomController
847925
test_custom_controller()
926+
test_tasker_post_stop_releases_custom_controller()
848927

849928
# 测试 Toolkit
850929
test_toolkit()

0 commit comments

Comments
 (0)