1717import sys
1818import numpy
1919import io
20+ import threading
2021
2122# Fix encoding issues on Windows
2223if 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+
684720def 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