ksaa 2025.6.8.0__py3-none-any.whl → 2025.6.28.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. kotonebot/backend/context/context.py +29 -25
  2. kotonebot/backend/debug/server.py +0 -2
  3. kotonebot/backend/dispatch.py +1 -17
  4. kotonebot/backend/loop.py +277 -0
  5. kotonebot/client/__init__.py +4 -2
  6. kotonebot/client/device.py +178 -25
  7. kotonebot/client/host/__init__.py +2 -1
  8. kotonebot/client/host/adb_common.py +94 -0
  9. kotonebot/client/host/custom.py +33 -4
  10. kotonebot/client/host/leidian_host.py +17 -15
  11. kotonebot/client/host/mumu12_host.py +79 -11
  12. kotonebot/client/host/protocol.py +44 -19
  13. kotonebot/client/host/windows_common.py +55 -0
  14. kotonebot/client/implements/__init__.py +7 -0
  15. kotonebot/client/implements/adb.py +24 -7
  16. kotonebot/client/implements/adb_raw.py +3 -3
  17. kotonebot/client/implements/nemu_ipc/__init__.py +8 -0
  18. kotonebot/client/implements/nemu_ipc/external_renderer_ipc.py +280 -0
  19. kotonebot/client/implements/nemu_ipc/nemu_ipc.py +327 -0
  20. kotonebot/client/implements/remote_windows.py +17 -21
  21. kotonebot/client/implements/uiautomator2.py +5 -8
  22. kotonebot/client/implements/windows.py +28 -36
  23. kotonebot/client/protocol.py +13 -0
  24. kotonebot/client/registration.py +24 -0
  25. kotonebot/config/base_config.py +8 -2
  26. kotonebot/debug_entry.py +11 -4
  27. kotonebot/errors.py +7 -0
  28. kotonebot/kaa/game_ui/common.py +4 -7
  29. kotonebot/kaa/game_ui/dialog.py +24 -4
  30. kotonebot/kaa/game_ui/idols_overview.py +28 -12
  31. kotonebot/kaa/game_ui/primary_button.py +55 -0
  32. kotonebot/kaa/game_ui/scrollable.py +0 -5
  33. kotonebot/kaa/main/cli.py +9 -4
  34. kotonebot/kaa/main/dmm_host.py +15 -8
  35. kotonebot/kaa/main/gr.py +114 -27
  36. kotonebot/kaa/main/kaa.py +140 -41
  37. kotonebot/kaa/metadata.py +47 -0
  38. kotonebot/kaa/resources/__pycache__/__init__.cpython-310.pyc +0 -0
  39. kotonebot/kaa/resources/game.db +0 -0
  40. kotonebot/kaa/resources/game_ver.txt +0 -0
  41. kotonebot/kaa/resources/idol_cards/i_card-skin-kllj-3-012_0.png +0 -0
  42. kotonebot/kaa/resources/idol_cards/i_card-skin-kllj-3-012_1.png +0 -0
  43. kotonebot/kaa/resources/idol_cards/i_card-skin-ssmk-3-012_0.png +0 -0
  44. kotonebot/kaa/resources/idol_cards/i_card-skin-ssmk-3-012_1.png +0 -0
  45. kotonebot/kaa/sprites/24e99232-9434-457f-a9a0-69dd7ecf675f.png +0 -0
  46. kotonebot/kaa/sprites/2ededcf5-1d80-4e2a-9c83-2a31998331ce.png +0 -0
  47. kotonebot/kaa/sprites/3b473fe6-e147-477f-b088-9b8fb042a4f6.png +0 -0
  48. kotonebot/kaa/sprites/55f7db71-0a18-4b3d-b847-57959b8d2e32.png +0 -0
  49. kotonebot/kaa/sprites/6cd80be8-c9b3-4ba5-bf17-3ffc9b000743.png +0 -0
  50. kotonebot/kaa/sprites/74ec3510-583d-4a76-ac69-38480fbf1387.png +0 -0
  51. kotonebot/kaa/sprites/a0bd6a5f-784d-4f0a-9d66-10f4b80c8d3e.png +0 -0
  52. kotonebot/kaa/sprites/d3424d31-0502-4623-996e-f0194e5085ce.png +0 -0
  53. kotonebot/kaa/sprites/e6b45405-cd9f-4c6e-a9f1-6ec953747c65.png +0 -0
  54. kotonebot/kaa/sprites/f5c16d2f-ebc5-4617-9b96-971696af7c52.png +0 -0
  55. kotonebot/kaa/tasks/R.py +157 -135
  56. kotonebot/kaa/{clear_logs.py → tasks/clear_logs.py} +0 -3
  57. kotonebot/kaa/tasks/produce/in_purodyuusu.py +0 -4
  58. kotonebot/kaa/tasks/produce/non_lesson_actions.py +0 -3
  59. kotonebot/kaa/tasks/produce/produce.py +119 -70
  60. kotonebot/kaa/tasks/start_game.py +7 -6
  61. kotonebot/kaa/util/paths.py +6 -1
  62. kotonebot/tools/mirror.py +23 -13
  63. kotonebot/util.py +32 -0
  64. {ksaa-2025.6.8.0.dist-info → ksaa-2025.6.28.0.dist-info}/METADATA +4 -1
  65. {ksaa-2025.6.8.0.dist-info → ksaa-2025.6.28.0.dist-info}/RECORD +201 -181
  66. kotonebot/client/factory.py +0 -92
  67. kotonebot/kaa/sprites/38b8f119-8de1-49bf-8a5f-a44371de4569.png +0 -0
  68. /kotonebot/kaa/sprites/{5ca50448-cc21-499a-a685-c5e1c4240c48.png → 019273c4-22ce-486f-8a7d-dbc78f13dcac.png} +0 -0
  69. /kotonebot/kaa/sprites/{afa0f85f-dcb7-48a1-a1ae-624456b9b65c.png → 03e34ef7-36b5-4ddc-9545-5b70a2eba72e.png} +0 -0
  70. /kotonebot/kaa/sprites/{a00fcf77-2800-4f7d-993d-4636b36045bb.png → 06ea773b-e2b3-40d4-82f6-b119ce9ca00b.png} +0 -0
  71. /kotonebot/kaa/sprites/{d58d9987-b1ca-4669-a9a6-9a0fd1847fa6.png → 0769f8f4-1c47-4bad-a783-bba5e20250bf.png} +0 -0
  72. /kotonebot/kaa/sprites/{82a5e224-a161-43d4-8e15-2946480336dd.png → 08946997-4e9e-4242-95e1-e988fa96b919.png} +0 -0
  73. /kotonebot/kaa/sprites/{fb927b92-7a82-4869-8c61-581d49cd7201.png → 0d60ce10-20ce-4980-8172-b6bac08d6258.png} +0 -0
  74. /kotonebot/kaa/sprites/{b1e88fda-434a-4d06-8891-883a7beafa12.png → 0f40352f-6443-406e-8e2c-c40d5cfc6c59.png} +0 -0
  75. /kotonebot/kaa/sprites/{c4f28132-4aba-40ba-be6c-b1a31d014635.png → 0f6d7f89-bdb2-4235-9b6b-e65cb73a4167.png} +0 -0
  76. /kotonebot/kaa/sprites/{e866d8b1-4f61-45bf-8170-5229f53c047a.png → 14d09c44-3897-4c81-b615-19c35d0f3793.png} +0 -0
  77. /kotonebot/kaa/sprites/{a105d17d-d44c-4798-aeff-70808c91cb47.png → 15191dec-7e33-4721-8068-15c11978e885.png} +0 -0
  78. /kotonebot/kaa/sprites/{869b1771-b2ce-4872-ab75-2157c47c5b13.png → 15f8f31e-65ce-408d-8556-b799bcefc9d4.png} +0 -0
  79. /kotonebot/kaa/sprites/{994aa6a6-0189-4a79-8125-56136973b581.png → 20b01af5-907b-4d49-9f5b-a3700ca9c0c3.png} +0 -0
  80. /kotonebot/kaa/sprites/{92b7d830-3db5-434e-b156-0ac855db480d.png → 242c088b-bc73-4d11-aa31-ee7c71477fb4.png} +0 -0
  81. /kotonebot/kaa/sprites/{9c1fa1d4-2ae5-4093-abaa-cebb51135721.png → 2f2a7951-a30a-4835-b9f6-3205cfdd2f05.png} +0 -0
  82. /kotonebot/kaa/sprites/{a33ae1c3-ac69-4783-977e-9f0f1b1a2f8d.png → 3031397e-3d12-4f23-8377-006eca5d2627.png} +0 -0
  83. /kotonebot/kaa/sprites/{e7022542-4317-4a61-8098-f3f733d8400e.png → 312dbd82-5457-474a-91ce-511f0e4e9c80.png} +0 -0
  84. /kotonebot/kaa/sprites/{cd8d3d55-f102-419e-a6fd-2ad5ecc4fd2e.png → 31851ad1-df39-4a4f-9ae7-19c1d1c6b51a.png} +0 -0
  85. /kotonebot/kaa/sprites/{826bf80a-112e-4356-9111-4b7e692ccc82.png → 338b699a-d556-4b80-a577-123442c3db45.png} +0 -0
  86. /kotonebot/kaa/sprites/{d8136469-6d73-4f82-a79c-fee83c3e93a8.png → 35319890-98e3-4f75-b969-6b8576ef668d.png} +0 -0
  87. /kotonebot/kaa/sprites/{96cf0633-6531-499b-b1cc-6670ab710293.png → 3536f829-70d3-48f9-8fd4-b65eab7502f3.png} +0 -0
  88. /kotonebot/kaa/sprites/{31a52dc8-b7fb-4ff6-a261-eb90a918603c.png → 35516b69-a4f4-4ad4-8af4-35043475ee90.png} +0 -0
  89. /kotonebot/kaa/sprites/{d747239f-7620-4f23-9ac7-cf2d34889266.png → 36d71d33-09c9-4be8-b184-ccf40f700333.png} +0 -0
  90. /kotonebot/kaa/sprites/{b44f5e5b-e999-4427-8a9f-f76c6485f4a5.png → 379d5084-637f-45e8-8bd8-789eaa919821.png} +0 -0
  91. /kotonebot/kaa/sprites/{0a430873-8c12-4ce2-a5de-ab9790c08344.png → 3851303a-e3a0-46eb-aa7e-b47fb41946f7.png} +0 -0
  92. /kotonebot/kaa/sprites/{ff77d221-8444-4d0f-8c93-0223a97e2a98.png → 3d1aa368-e198-478f-a47f-49818efbaec8.png} +0 -0
  93. /kotonebot/kaa/sprites/{333fe6d3-0be7-40fb-a2a3-364f98758f2a.png → 3f5907a9-7fe4-4f46-8e46-a96ac7fa3923.png} +0 -0
  94. /kotonebot/kaa/sprites/{0decaa6a-b9a9-4d9b-829a-0fc1f67538b4.png → 40c0f296-d29e-4e05-aaff-a212339b742e.png} +0 -0
  95. /kotonebot/kaa/sprites/{c1ad996a-925e-41b5-b26c-d48b8ded21d2.png → 43e72796-a644-4d9b-a817-8add011d9a75.png} +0 -0
  96. /kotonebot/kaa/sprites/{e2fae2a4-bb19-4f49-b382-47e504ec26d3.png → 469c2b41-4171-4902-acc5-da0c807886ab.png} +0 -0
  97. /kotonebot/kaa/sprites/{559df9b9-e74d-45af-a300-77d3424e69c6.png → 497e7930-f423-457a-8168-b1a1532bb1e9.png} +0 -0
  98. /kotonebot/kaa/sprites/{e41bb5df-164d-4760-9db6-d5d5d763bb5f.png → 49a43af3-17e3-4434-ba4d-519775ccaea9.png} +0 -0
  99. /kotonebot/kaa/sprites/{f9b2f64d-f829-4b13-9704-c5f00fb956be.png → 49b9a298-e847-4322-99a2-c0588c3542fe.png} +0 -0
  100. /kotonebot/kaa/sprites/{645744c4-3bbf-4ea2-8f46-4725fff874b3.png → 4a1902aa-01db-46bb-bea5-88ac62c82562.png} +0 -0
  101. /kotonebot/kaa/sprites/{c0fc7de5-b842-4c70-9cf2-89d833bfe7a6.png → 4a96a728-6303-4ae3-8e0c-9f1e8e5f86a7.png} +0 -0
  102. /kotonebot/kaa/sprites/{73f4204e-8281-47cc-9dce-6bb8280c4ce5.png → 4b7c4221-a679-47f6-929d-0ef514894b27.png} +0 -0
  103. /kotonebot/kaa/sprites/{c1b1125f-542b-4e63-9c86-5d2e35a89a10.png → 4d355e36-9169-43e4-bd58-2836ee3e56e0.png} +0 -0
  104. /kotonebot/kaa/sprites/{ad8ee599-88a8-429c-8982-99086e552dcc.png → 4eb97d68-cd47-4100-9c3b-35b5c86b4bdf.png} +0 -0
  105. /kotonebot/kaa/sprites/{7a479871-547e-4098-9cdd-6c9bb9f7449a.png → 515a2ff0-36b8-4f2f-9902-e6c1b9796586.png} +0 -0
  106. /kotonebot/kaa/sprites/{02e0aa0d-4094-4bdc-8625-1a063733615b.png → 51a3ed29-92b9-4429-9e50-783d1b8dc6a7.png} +0 -0
  107. /kotonebot/kaa/sprites/{22d5a7d5-59a0-4137-b02e-1a7af86d4250.png → 52d3438b-afae-4c7a-abe5-7ed530c7558c.png} +0 -0
  108. /kotonebot/kaa/sprites/{5bf00cba-9aab-4ffe-b20b-8f5e74279276.png → 540ee3b5-ca8f-4091-80a6-ce774d95e5f7.png} +0 -0
  109. /kotonebot/kaa/sprites/{05ff5efd-0f76-47ce-bc89-237266bc17c0.png → 54a15298-ecaf-4cc5-b8a2-5b0d8f8add41.png} +0 -0
  110. /kotonebot/kaa/sprites/{1f9165e6-77ee-403e-9b76-99c114527dbb.png → 554c0ff6-0a05-419d-85f9-5ced260d0d2e.png} +0 -0
  111. /kotonebot/kaa/sprites/{79bc56c5-eae6-4389-9116-4a9e182d6bf3.png → 55e4ef75-ba8e-4230-819c-450cf14b08cd.png} +0 -0
  112. /kotonebot/kaa/sprites/{cd4cbd7b-df80-45bb-a053-cc333a806934.png → 5824ef5f-6776-402c-b78c-ae79cc4cc878.png} +0 -0
  113. /kotonebot/kaa/sprites/{804a4b05-1ca3-4915-bf23-a66ccdc34404.png → 5a393483-c631-47fd-805c-c460c23b8f9e.png} +0 -0
  114. /kotonebot/kaa/sprites/{e5d15c50-45ee-4de9-8b97-4630cbd8f01a.png → 5b75cae4-a853-402c-acec-edbfef94342a.png} +0 -0
  115. /kotonebot/kaa/sprites/{1a3d18a4-7d91-4182-9e40-e60d73fed365.png → 5c9ae62c-488f-4109-a4d2-d2385c32043d.png} +0 -0
  116. /kotonebot/kaa/sprites/{7344e180-251b-465a-a8cf-974477f7ad44.png → 5cdd71be-c8cd-4310-b6f4-5e97ddbeb46e.png} +0 -0
  117. /kotonebot/kaa/sprites/{67a81c23-4ce4-45e3-bfa8-1136fb14b831.png → 5d5cb13b-c1fd-4808-b352-b21291a9ff0b.png} +0 -0
  118. /kotonebot/kaa/sprites/{4e21f02d-d5e1-49eb-946a-8edc74d0f1f7.png → 5d6c4462-3d7c-42eb-ab3e-195ebf845a09.png} +0 -0
  119. /kotonebot/kaa/sprites/{d7f604a6-c663-44db-b1d3-c9792eb3daa4.png → 5d94e628-82ff-4531-b70a-b13297491bea.png} +0 -0
  120. /kotonebot/kaa/sprites/{74521b6f-de5e-4ba8-a3c9-c6ba01070498.png → 5dc2dca3-48bd-4eee-af89-1a1d30593bd5.png} +0 -0
  121. /kotonebot/kaa/sprites/{b604e25d-680a-4e02-a012-2e593b5bc276.png → 5f0c2a57-efd0-4094-9f71-6f6fe97316f6.png} +0 -0
  122. /kotonebot/kaa/sprites/{fd610de1-b023-4747-8c1b-72f89b1c5647.png → 610348f2-6262-4cd1-8fc3-e7470f85580c.png} +0 -0
  123. /kotonebot/kaa/sprites/{5be4f6c7-2baa-42f3-a5a5-99ca0830cc30.png → 613e9b10-b2eb-44ea-b6d9-9310c0e1bdcf.png} +0 -0
  124. /kotonebot/kaa/sprites/{72c1804f-db8c-40fa-bc79-6533f2861a5d.png → 618b21ad-ab8b-436a-8eb8-bd320a198c9f.png} +0 -0
  125. /kotonebot/kaa/sprites/{572077d4-7ee2-449a-b4f0-66239b0d7aff.png → 61a09df6-7443-436c-9ef0-1d5a1cae1509.png} +0 -0
  126. /kotonebot/kaa/sprites/{321b54c8-c69b-4437-931d-655882dabeed.png → 64be274e-e8ad-4938-84c1-5d8aabcd10d8.png} +0 -0
  127. /kotonebot/kaa/sprites/{22ee3f1b-ddcb-48bf-85dd-8423f85e3199.png → 671b4a55-023a-434d-b29a-e844fe528e6b.png} +0 -0
  128. /kotonebot/kaa/sprites/{668002b6-805b-467b-a703-203fb75444f4.png → 6e5e9cf9-80b0-4241-a222-ff50dbd89a0c.png} +0 -0
  129. /kotonebot/kaa/sprites/{4bd66b38-78d3-4a27-816b-d315039a8bb6.png → 6ef59649-bbb3-42b6-a6b0-eac4f35a3da4.png} +0 -0
  130. /kotonebot/kaa/sprites/{7b3c6860-fbbe-48aa-aadf-8062fa88f1fe.png → 75a2093d-8743-427e-8d24-ae9a69fa14c1.png} +0 -0
  131. /kotonebot/kaa/sprites/{371a3192-101c-45ef-9da4-2ed389770c0f.png → 76bde19a-4b1b-4e52-86d9-f56be7529c71.png} +0 -0
  132. /kotonebot/kaa/sprites/{ef39804e-8d2d-40f6-8d13-bd2877d1353a.png → 77d06f4d-8214-4306-9dc5-035c720f7256.png} +0 -0
  133. /kotonebot/kaa/sprites/{24cd42fd-bb3b-4fc6-b40d-d3e3e36fe9f1.png → 78b642d3-9a2c-4c17-8872-3cc78211f4a0.png} +0 -0
  134. /kotonebot/kaa/sprites/{0bab7080-9a37-4c27-9821-8df36d49b678.png → 7a9d1c39-b306-4cb0-be2f-7a5963099949.png} +0 -0
  135. /kotonebot/kaa/sprites/{b1d1dfab-51f5-4ae9-b718-cdf46774c0ff.png → 7ddda828-c8b0-4331-864f-99b85b7c5557.png} +0 -0
  136. /kotonebot/kaa/sprites/{5edef075-4843-4dc9-840f-ae445cbe0730.png → 81399fd9-f254-4d0e-9775-ad451c237a4e.png} +0 -0
  137. /kotonebot/kaa/sprites/{705d9ecf-faf8-40f4-b701-637cec822c1e.png → 81526e97-1b8d-433f-a5c9-907e4e8c85cd.png} +0 -0
  138. /kotonebot/kaa/sprites/{ecd44a4a-e1ca-4a86-ae0d-2306a672613f.png → 818e2370-8fb9-4e6c-ab78-58e86d6525b5.png} +0 -0
  139. /kotonebot/kaa/sprites/{4f58031c-36b9-4baf-bccf-11c05d83bd19.png → 8431cc52-0041-4425-808c-dcd4d196506c.png} +0 -0
  140. /kotonebot/kaa/sprites/{13535ecd-bdf5-4ff7-aa2c-6ecb20fe9c06.png → 85a49ff3-6075-4797-8b7f-35851a843698.png} +0 -0
  141. /kotonebot/kaa/sprites/{d22ca9e0-001d-43d6-a557-1c76c5a27c81.png → 86062e91-d6c1-44cf-9f8c-26a8b9dd8686.png} +0 -0
  142. /kotonebot/kaa/sprites/{0bbfb5b0-f0f7-4ad9-86f7-a2adb01a6371.png → 860c3d98-e263-482f-95fb-4fc9c472e8b8.png} +0 -0
  143. /kotonebot/kaa/sprites/{ed5d2bf3-405c-4165-88d0-9f8a4be968c6.png → 8669ce44-dec7-47c2-aafd-3f2fbfd1d083.png} +0 -0
  144. /kotonebot/kaa/sprites/{f0786a26-81b5-4e76-b577-db70ab3fab50.png → 86b15801-c932-4034-bed8-fd76b0f65916.png} +0 -0
  145. /kotonebot/kaa/sprites/{80fb515c-4323-422d-a24c-80a3bf702f1d.png → 893b015d-6894-4e74-880e-d22859a25662.png} +0 -0
  146. /kotonebot/kaa/sprites/{fdfecda8-2fe0-487e-b5fe-6358274bb9ff.png → 8c0fa5fa-cebb-4ae7-bd1c-b542c9cbf694.png} +0 -0
  147. /kotonebot/kaa/sprites/{73af305f-840f-4325-abae-b3519fba672a.png → 913a1d17-54fe-4c8f-ac50-ef7f39600b86.png} +0 -0
  148. /kotonebot/kaa/sprites/{c2667677-cbff-4dd4-a401-e0963e7224f4.png → 964c6b90-f526-4199-9523-d8124373a56b.png} +0 -0
  149. /kotonebot/kaa/sprites/{db94ffb7-a09d-4a51-b420-b930851c9db4.png → 97c281a9-ebb0-4f54-b644-b7ed7dd65f81.png} +0 -0
  150. /kotonebot/kaa/sprites/{48c257f8-3583-49f7-b01a-ba7d251086ac.png → 998fc84a-af71-4fa5-8aa4-e0bfeb9c53ea.png} +0 -0
  151. /kotonebot/kaa/sprites/{469006bc-a011-46e4-bcde-992be888456e.png → 9cbf7f6e-fe77-463b-9c60-495dc823510a.png} +0 -0
  152. /kotonebot/kaa/sprites/{4d6c198a-0e34-4c80-86d1-7789b2082d64.png → 9ce7d48e-295d-47b2-96eb-f3ce697c1b37.png} +0 -0
  153. /kotonebot/kaa/sprites/{a3c12040-2b11-4038-b830-215fb7b6f359.png → 9dcca89b-c92b-4708-8f19-facd40d09a65.png} +0 -0
  154. /kotonebot/kaa/sprites/{fbd816df-7ae6-40a4-b4d4-5708f5ba4a7c.png → 9e369992-ea8c-4c15-88d3-1344319978b0.png} +0 -0
  155. /kotonebot/kaa/sprites/{54bb5fcf-be61-4af8-bb19-a3c5113cf9e1.png → 9f9003ba-468f-406f-a82f-1b7a23ef347c.png} +0 -0
  156. /kotonebot/kaa/sprites/{309105d4-073a-47c9-8c5c-d593aa497723.png → a2bf4e11-fbc3-4172-bc0b-b16f8f59e68c.png} +0 -0
  157. /kotonebot/kaa/sprites/{111a03e6-e401-4ad5-b5dc-d5124e8f1ab0.png → a546e254-0490-43df-a291-a9a5c2b4c34a.png} +0 -0
  158. /kotonebot/kaa/sprites/{7736a171-9221-4cda-8288-908997abeff2.png → aacd229c-d307-4a20-a388-84d8f4afb4a5.png} +0 -0
  159. /kotonebot/kaa/sprites/{1bfce48a-b151-406e-a6f5-863cb68cbbf2.png → b003bef5-53db-4032-b1bd-d5075de365a8.png} +0 -0
  160. /kotonebot/kaa/sprites/{b2f7a430-5777-4861-8435-5419394e5ee8.png → b641a122-4734-43e1-b22f-468464a0cff1.png} +0 -0
  161. /kotonebot/kaa/sprites/{a82e06fb-a02e-4be5-a7b4-4d2758914fcf.png → bc7303f3-43e9-4ece-9206-5f9c779b813b.png} +0 -0
  162. /kotonebot/kaa/sprites/{f74fc514-61f1-4068-bdf4-754fcaf8ac89.png → bd7a6754-fffc-4315-987f-5ba32d95b697.png} +0 -0
  163. /kotonebot/kaa/sprites/{c7aa3f89-8a5e-4685-9297-4fc892bfdd81.png → c39f2006-442b-4ab7-99bb-3dfa0673f05d.png} +0 -0
  164. /kotonebot/kaa/sprites/{6bf5556a-8f4e-41d7-8ab5-d683ad1b47a8.png → c70f4634-6821-4480-a615-b5f9cdc578c1.png} +0 -0
  165. /kotonebot/kaa/sprites/{89c0e675-3fd4-456a-8ae7-3bfb248b7281.png → c774098e-d782-455c-80a9-5bad3713129d.png} +0 -0
  166. /kotonebot/kaa/sprites/{09433024-3dfe-4fcb-9d67-cf442b58f31b.png → c7e3736a-0e0f-474a-9386-f58292a45b92.png} +0 -0
  167. /kotonebot/kaa/sprites/{48d2f007-8c1b-43fc-bd3f-f0b10d2a0c88.png → c90b582c-8570-48b4-98c0-62d5b067ca2c.png} +0 -0
  168. /kotonebot/kaa/sprites/{9bb1a400-935a-4c5e-838b-fca502c49aba.png → c9d3699b-c8b1-4294-9b48-103df4ab6c65.png} +0 -0
  169. /kotonebot/kaa/sprites/{157dd58d-e319-4e0c-a37a-638e1a31c60b.png → cc9483c3-c895-4dcc-b3f2-0934ba1ad42e.png} +0 -0
  170. /kotonebot/kaa/sprites/{43d65b3f-bbae-4e29-a7bf-539379c1b072.png → cec49286-3d48-4fe0-8911-6a1b17d1ac20.png} +0 -0
  171. /kotonebot/kaa/sprites/{56a8c0ca-4659-4de2-9bce-55ce86a40d75.png → cef78712-d9ae-4d6e-8d7d-d047d48fbb98.png} +0 -0
  172. /kotonebot/kaa/sprites/{12e8d3f1-494a-46f5-908f-f621a0c57ec6.png → d06fd859-4678-4593-b096-c86b38810cb7.png} +0 -0
  173. /kotonebot/kaa/sprites/{ba955365-1328-4018-83a2-bfe818f8f172.png → d19c37ff-d5c1-4316-8424-e69ccb5c2523.png} +0 -0
  174. /kotonebot/kaa/sprites/{695f8d57-b2c9-4a6d-9ae9-1b49ee3eb22a.png → d26b60f7-d15c-4563-bedc-dd6e7e4078b2.png} +0 -0
  175. /kotonebot/kaa/sprites/{910d307b-6461-4950-badb-509975d0b797.png → d26bf52c-cb1b-4ebf-9c03-ff3f2d245959.png} +0 -0
  176. /kotonebot/kaa/sprites/{7e8164f6-a9e4-4600-b776-77b35a490933.png → d5fd6c73-4ad8-47b5-89be-c7b137a9950f.png} +0 -0
  177. /kotonebot/kaa/sprites/{05bfd235-2077-479a-8c43-11fb49316000.png → d696062b-9b58-4af1-835e-2cf5a0a0fbeb.png} +0 -0
  178. /kotonebot/kaa/sprites/{03458cf9-8ab0-4908-9a56-9c5c7e06bc9b.png → da7a329e-339e-4ee7-8775-bca505a1980d.png} +0 -0
  179. /kotonebot/kaa/sprites/{2f81e70a-f2af-420f-9b6b-6338c391911f.png → dedb8499-5881-4add-914f-ff0049285dae.png} +0 -0
  180. /kotonebot/kaa/sprites/{b3d1f8d4-1393-4206-b78e-a5495d8fb930.png → dedea21b-f21d-4621-a213-70ad03faec58.png} +0 -0
  181. /kotonebot/kaa/sprites/{f65a4495-f044-44de-bf0d-c869e637d5d3.png → dff74cce-ba2d-44aa-b924-183259080a32.png} +0 -0
  182. /kotonebot/kaa/sprites/{b3e72de2-e103-49a3-b0f2-8593cff87c4e.png → e0246b0d-f0cf-49d4-8423-7da46e75a575.png} +0 -0
  183. /kotonebot/kaa/sprites/{624555b9-31e3-46a3-8078-55619eaa40b9.png → e1b6b32b-c797-4f1d-aa2b-e7320083a9a6.png} +0 -0
  184. /kotonebot/kaa/sprites/{90a9c819-ee6b-4b3e-a0e4-ce484c6e413d.png → e23e3f19-03f6-4507-9128-115191272b90.png} +0 -0
  185. /kotonebot/kaa/sprites/{cc829886-23e8-4d47-b4c4-37ca20381180.png → e2eb3bae-6451-40b9-bbff-90f11dca1904.png} +0 -0
  186. /kotonebot/kaa/sprites/{6a7d061e-682b-42c1-97d5-121c52921e8b.png → e46e1b6d-6702-46bf-a07e-aec8d183453f.png} +0 -0
  187. /kotonebot/kaa/sprites/{de709296-e411-465f-86fc-5daf5ca83cef.png → e5297036-9934-44a4-a761-a6a49ae06e3e.png} +0 -0
  188. /kotonebot/kaa/sprites/{52620946-b92c-42c7-9fcc-0ff13388fd0d.png → e7da5988-0e83-44ad-888b-246365473f9e.png} +0 -0
  189. /kotonebot/kaa/sprites/{05f8f996-a38a-45d9-8ff9-720b6c4fd599.png → eb8d208a-dbcc-43ca-a8ac-098e1005ebfa.png} +0 -0
  190. /kotonebot/kaa/sprites/{8e8a0b57-0231-4ed6-958e-3196f7c9ca7b.png → ebe4ce64-98a9-40f2-9fde-3d5cdae673c7.png} +0 -0
  191. /kotonebot/kaa/sprites/{4a235eaa-46f1-4f71-b54d-ada0ade44572.png → ef2ad3df-3f89-4a48-8fc3-c70a8adc1044.png} +0 -0
  192. /kotonebot/kaa/sprites/{f8cbb089-a63e-4e40-9238-26d14da2c459.png → f05e410d-15b8-4831-bcc1-4cd8af07c882.png} +0 -0
  193. /kotonebot/kaa/sprites/{550fc45d-22c0-4a7c-a324-2c4ff107ab39.png → f1fac13a-84f3-42bd-9b52-e0ba90bc5255.png} +0 -0
  194. /kotonebot/kaa/sprites/{ad784a29-f0e2-4037-976b-45143db8441c.png → f7c34ab6-c165-48b7-ad5a-0b60613ce1bc.png} +0 -0
  195. /kotonebot/kaa/sprites/{0439399f-5fca-43f9-a105-99342ff04d2e.png → fa80b14e-953f-44bd-a4ad-99aefd276fd7.png} +0 -0
  196. /kotonebot/kaa/sprites/{7cea265a-fca5-43d0-b2da-067c8fff4d72.png → fe35655e-701b-4081-8df8-c17a7252acef.png} +0 -0
  197. /kotonebot/kaa/sprites/{da3e7cee-d77b-4e62-b703-ed257706dd39.png → fe853ac7-934e-4b4a-bcc7-b986e057dfcf.png} +0 -0
  198. /kotonebot/kaa/sprites/{1f8f8c46-43ec-4449-be9f-2e96da11da1e.png → ffda716c-2903-4ec7-8c59-aab5b8ad10bb.png} +0 -0
  199. /kotonebot/kaa/sprites/{4c72f39e-1182-4479-867f-8207744ee4fd.png → fff33200-c947-49f2-ab86-3ac98937fa36.png} +0 -0
  200. {ksaa-2025.6.8.0.dist-info → ksaa-2025.6.28.0.dist-info}/WHEEL +0 -0
  201. {ksaa-2025.6.8.0.dist-info → ksaa-2025.6.28.0.dist-info}/entry_points.txt +0 -0
  202. {ksaa-2025.6.8.0.dist-info → ksaa-2025.6.28.0.dist-info}/licenses/LICENSE +0 -0
  203. {ksaa-2025.6.8.0.dist-info → ksaa-2025.6.28.0.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,15 @@
1
1
  import time
2
2
  import socket
3
3
  from abc import ABC, abstractmethod
4
- from typing_extensions import ParamSpec, Concatenate
5
- from typing import Callable, TypeVar, Generic, Protocol, runtime_checkable, Type, Any
4
+ from typing import Callable, TypeVar, Protocol, Any, Generic
5
+ from dataclasses import dataclass
6
6
 
7
7
  from adbutils import adb, AdbTimeout, AdbError
8
8
  from adbutils._device import AdbDevice
9
9
 
10
10
  from kotonebot import logging
11
- from kotonebot.client import Device, create_device, DeviceImpl
11
+ from kotonebot.client import Device, DeviceImpl
12
+
12
13
  from kotonebot.util import Countdown, Interval
13
14
 
14
15
  logger = logging.getLogger(__name__)
@@ -17,6 +18,28 @@ _T = TypeVar("_T")
17
18
  def copy_type(_: _T) -> Callable[[Any], _T]:
18
19
  return lambda x: x
19
20
 
21
+ # --- 定义专用的 HostConfig 数据类 ---
22
+ @dataclass
23
+ class AdbHostConfig:
24
+ """由外部为基于 ADB 的主机提供的配置。"""
25
+ timeout: float = 180
26
+
27
+ @dataclass
28
+ class WindowsHostConfig:
29
+ """由外部为 Windows 实现提供配置。"""
30
+ window_title: str
31
+ ahk_exe_path: str
32
+
33
+ @dataclass
34
+ class RemoteWindowsHostConfig:
35
+ """由外部为远程 Windows 实现提供配置。"""
36
+ windows_host_config: WindowsHostConfig
37
+ host: str
38
+ port: int
39
+
40
+ # --- 使用泛型改造 Instance 协议 ---
41
+ T_HostConfig = TypeVar("T_HostConfig")
42
+
20
43
  def tcp_ping(host: str, port: int, timeout: float = 1.0) -> bool:
21
44
  """
22
45
  通过 TCP ping 检查主机和端口是否可达。
@@ -36,7 +59,11 @@ def tcp_ping(host: str, port: int, timeout: float = 1.0) -> bool:
36
59
  return False
37
60
 
38
61
 
39
- class Instance(ABC):
62
+ class Instance(Generic[T_HostConfig], ABC):
63
+ """
64
+ 代表一个可运行环境的实例(如一个模拟器)。
65
+ 使用泛型来约束 create_device 方法的配置参数类型。
66
+ """
40
67
  def __init__(self,
41
68
  id: str,
42
69
  name: str,
@@ -68,7 +95,7 @@ class Instance(ABC):
68
95
  启动模拟器实例。
69
96
  """
70
97
  raise NotImplementedError()
71
-
98
+
72
99
  @abstractmethod
73
100
  def stop(self):
74
101
  """
@@ -80,21 +107,16 @@ class Instance(ABC):
80
107
  def running(self) -> bool:
81
108
  raise NotImplementedError()
82
109
 
83
- def create_device(self, impl: DeviceImpl, *, timeout: float = 180) -> Device:
110
+ @abstractmethod
111
+ def create_device(self, impl: DeviceImpl, host_config: T_HostConfig) -> Device:
84
112
  """
85
- 创建 Device 实例,可用于控制模拟器系统。
86
-
87
- :return: Device 实例
113
+ 根据实现名称和类型化的主机配置创建设备。
114
+
115
+ :param impl: 设备实现的名称。
116
+ :param host_config: 一个类型化的数据对象,包含创建所需的所有外部配置。
117
+ :return: 配置好的 Device 实例。
88
118
  """
89
- if self.adb_port is None:
90
- raise ValueError("ADB port is not set and is required.")
91
- return create_device(
92
- addr=f'{self.adb_ip}:{self.adb_port}',
93
- impl=impl,
94
- device_serial=self.adb_name,
95
- connect=True,
96
- timeout=timeout
97
- )
119
+ raise NotImplementedError()
98
120
 
99
121
  def wait_available(self, timeout: float = 180):
100
122
  logger.info('Starting to wait for emulator %s(127.0.0.1:%d) to be available...', self.name, self.adb_port)
@@ -173,7 +195,8 @@ class Instance(ABC):
173
195
  def __repr__(self) -> str:
174
196
  return f'{self.__class__.__name__}(name="{self.name}", id="{self.id}", adb="{self.adb_ip}:{self.adb_port}"({self.adb_name}))'
175
197
 
176
- class HostProtocol(Protocol):
198
+ Recipe = TypeVar('Recipe', bound=str)
199
+ class HostProtocol(Generic[Recipe], Protocol):
177
200
  @staticmethod
178
201
  def installed() -> bool: ...
179
202
 
@@ -183,6 +206,8 @@ class HostProtocol(Protocol):
183
206
  @staticmethod
184
207
  def query(*, id: str) -> Instance | None: ...
185
208
 
209
+ @staticmethod
210
+ def recipes() -> 'list[Recipe]': ...
186
211
 
187
212
  if __name__ == '__main__':
188
213
  pass
@@ -0,0 +1,55 @@
1
+ from abc import ABC
2
+ from typing import Literal
3
+ from typing_extensions import assert_never
4
+
5
+ from kotonebot import logging
6
+ from kotonebot.client.device import WindowsDevice
7
+ from .protocol import Device, WindowsHostConfig, RemoteWindowsHostConfig
8
+
9
+ logger = logging.getLogger(__name__)
10
+ WindowsRecipes = Literal['windows', 'remote_windows']
11
+
12
+ # Windows 相关的配置类型联合
13
+ WindowsHostConfigs = WindowsHostConfig | RemoteWindowsHostConfig
14
+
15
+ class CommonWindowsCreateDeviceMixin(ABC):
16
+ """
17
+ 通用 Windows 创建设备的 Mixin。
18
+ 该 Mixin 定义了创建 Windows 设备的通用接口。
19
+ """
20
+ def __init__(self, *args, **kwargs) -> None:
21
+ super().__init__(*args, **kwargs)
22
+
23
+ def create_device(self, recipe: WindowsRecipes, config: WindowsHostConfigs) -> Device:
24
+ """
25
+ 创建 Windows 设备。
26
+ """
27
+ match recipe:
28
+ case 'windows':
29
+ if not isinstance(config, WindowsHostConfig):
30
+ raise ValueError(f"Expected WindowsHostConfig for 'windows' recipe, got {type(config)}")
31
+ from kotonebot.client.implements.windows import WindowsImpl
32
+ d = WindowsDevice()
33
+ impl = WindowsImpl(
34
+ device=d,
35
+ window_title=config.window_title,
36
+ ahk_exe_path=config.ahk_exe_path
37
+ )
38
+ d._screenshot = impl
39
+ d._touch = impl
40
+ return d
41
+ case 'remote_windows':
42
+ if not isinstance(config, RemoteWindowsHostConfig):
43
+ raise ValueError(f"Expected RemoteWindowsHostConfig for 'remote_windows' recipe, got {type(config)}")
44
+ from kotonebot.client.implements.remote_windows import RemoteWindowsImpl
45
+ d = WindowsDevice()
46
+ impl = RemoteWindowsImpl(
47
+ device=d,
48
+ host=config.host,
49
+ port=config.port
50
+ )
51
+ d._screenshot = impl
52
+ d._touch = impl
53
+ return d
54
+ case _:
55
+ assert_never(f'Unsupported Windows recipe: {recipe}')
@@ -0,0 +1,7 @@
1
+ # 导入所有内置实现,以触发它们的 @register_impl 装饰器
2
+ from . import adb # noqa: F401
3
+ from . import adb_raw # noqa: F401
4
+ from . import remote_windows # noqa: F401
5
+ from . import uiautomator2 # noqa: F401
6
+ from . import windows # noqa: F401
7
+ from . import nemu_ipc # noqa: F401
@@ -5,16 +5,27 @@ from typing_extensions import override
5
5
  import cv2
6
6
  import numpy as np
7
7
  from cv2.typing import MatLike
8
+ from adbutils._device import AdbDevice as AdbUtilsDevice
8
9
 
9
- from ..device import Device
10
- from ..protocol import Commandable, Touchable, Screenshotable
10
+ from ..device import AndroidDevice
11
+ from ..protocol import AndroidCommandable, Touchable, Screenshotable
12
+ from ..registration import ImplConfig
13
+ from dataclasses import dataclass
11
14
 
12
15
  logger = logging.getLogger(__name__)
13
16
 
14
- class AdbImpl(Commandable, Touchable, Screenshotable):
15
- def __init__(self, device: Device):
16
- self.device = device
17
- self.adb = device.adb
17
+ # 定义配置模型
18
+ @dataclass
19
+ class AdbImplConfig(ImplConfig):
20
+ addr: str
21
+ connect: bool = True
22
+ disconnect: bool = True
23
+ device_serial: str | None = None
24
+ timeout: float = 180
25
+
26
+ class AdbImpl(AndroidCommandable, Touchable, Screenshotable):
27
+ def __init__(self, adb_connection: AdbUtilsDevice):
28
+ self.adb = adb_connection
18
29
 
19
30
  @override
20
31
  def launch_app(self, package_name: str) -> None:
@@ -36,6 +47,10 @@ class AdbImpl(Commandable, Touchable, Screenshotable):
36
47
  package = activity.split('/')[0]
37
48
  return package
38
49
 
50
+ def adb_shell(self, cmd: str) -> str:
51
+ """执行 ADB shell 命令"""
52
+ return cast(str, self.adb.shell(cmd))
53
+
39
54
  @override
40
55
  def detect_orientation(self):
41
56
  # 判断方向:https://stackoverflow.com/questions/10040624/check-if-device-is-landscape-via-adb
@@ -50,7 +65,9 @@ class AdbImpl(Commandable, Touchable, Screenshotable):
50
65
  def screen_size(self) -> tuple[int, int]:
51
66
  ret = cast(str, self.adb.shell("wm size")).strip('Physical size: ')
52
67
  spiltted = tuple(map(int, ret.split("x")))
53
- landscape = self.device.orientation == 'landscape'
68
+ # 检测当前方向
69
+ orientation = self.detect_orientation()
70
+ landscape = orientation == 'landscape'
54
71
  spiltted = tuple(sorted(spiltted, reverse=landscape))
55
72
  if len(spiltted) != 2:
56
73
  raise ValueError(f"Invalid screen size: {ret}")
@@ -12,7 +12,7 @@ from cv2.typing import MatLike
12
12
  from adbutils._utils import adb_path
13
13
 
14
14
  from .adb import AdbImpl
15
- from ..device import Device
15
+ from adbutils._device import AdbDevice as AdbUtilsDevice
16
16
  from kotonebot import logging
17
17
 
18
18
  logger = logging.getLogger(__name__)
@@ -27,8 +27,8 @@ done
27
27
  """
28
28
 
29
29
  class AdbRawImpl(AdbImpl):
30
- def __init__(self, device: Device):
31
- super().__init__(device)
30
+ def __init__(self, adb_connection: AdbUtilsDevice):
31
+ super().__init__(adb_connection)
32
32
  self.__worker: Thread | None = None
33
33
  self.__process: subprocess.Popen | None = None
34
34
  self.__data: MatLike | None = None
@@ -0,0 +1,8 @@
1
+ from .external_renderer_ipc import ExternalRendererIpc
2
+ from .nemu_ipc import NemuIpcImpl, NemuIpcImplConfig
3
+
4
+ __all__ = [
5
+ "ExternalRendererIpc",
6
+ "NemuIpcImpl",
7
+ "NemuIpcImplConfig",
8
+ ]
@@ -0,0 +1,280 @@
1
+ import ctypes
2
+ import logging
3
+ import os
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ class NemuIpcIncompatible(RuntimeError):
9
+ """MuMu12 IPC 环境不兼容或 DLL 加载失败"""
10
+
11
+
12
+ class ExternalRendererIpc:
13
+ r"""对 `external_renderer_ipc.dll` 的轻量封装。
14
+
15
+ 该类仅处理 DLL 加载与原型声明,并提供带有类型提示的薄包装方法,
16
+ 方便在其他模块中调用且保持类型安全。
17
+ 传入参数为 MuMu 根目录(如 F:\Apps\Netease\MuMuPlayer-12.0)。
18
+ """
19
+
20
+ def __init__(self, mumu_root_folder: str):
21
+ if os.name != "nt":
22
+ raise NemuIpcIncompatible("ExternalRendererIpc only supports Windows.")
23
+
24
+ self.lib = self.__load_dll(mumu_root_folder)
25
+ self.raise_on_error: bool = True
26
+ """是否在调用 DLL 函数失败时抛出异常。"""
27
+ self.__declare_prototypes()
28
+
29
+ def connect(self, nemu_folder: str, instance_id: int) -> int:
30
+ """
31
+ 建立连接。
32
+
33
+ API 原型:
34
+ `int nemu_connect(const wchar_t* path, int index)`
35
+
36
+ :param nemu_folder: 模拟器安装路径。
37
+ :param instance_id: 模拟器实例 ID。
38
+ :return: 成功返回连接 ID,失败返回 0。
39
+ """
40
+ return self.lib.nemu_connect(nemu_folder, instance_id)
41
+
42
+ def disconnect(self, connect_id: int) -> None:
43
+ """
44
+ 断开连接。
45
+
46
+ API 原型:
47
+ `void nemu_disconnect(int handle)`
48
+
49
+ :param connect_id: 连接 ID。
50
+ :return: 无返回值。
51
+ """
52
+ return self.lib.nemu_disconnect(connect_id)
53
+
54
+ def get_display_id(self, connect_id: int, pkg: str, app_index: int) -> int:
55
+ """
56
+ 获取指定包的 display id。
57
+
58
+ API 原型:
59
+ `int nemu_get_display_id(int handle, const char* pkg, int appIndex)`
60
+
61
+ :param connect_id: 连接 ID。
62
+ :param pkg: 包名。
63
+ :param app_index: 多开应用索引。
64
+ :return: <0 表示失败,>=0 表示有效 display id。
65
+ """
66
+ return self.lib.nemu_get_display_id(connect_id, pkg.encode('utf-8'), app_index)
67
+
68
+ def capture_display(
69
+ self,
70
+ connect_id: int,
71
+ display_id: int,
72
+ buf_len: int,
73
+ width_ptr: ctypes.c_void_p,
74
+ height_ptr: ctypes.c_void_p,
75
+ buffer_ptr: ctypes.c_void_p,
76
+ ) -> int:
77
+ """
78
+ 截取指定显示屏内容。
79
+
80
+ API 原型:
81
+ `int nemu_capture_display(int handle, unsigned int displayid, int buffer_size, int *width, int *height, unsigned char* pixels)`
82
+
83
+ :param connect_id: 连接 ID。
84
+ :param display_id: 显示屏 ID。
85
+ :param buf_len: 缓冲区长度(字节)。
86
+ :param width_ptr: 用于接收宽度的指针(ctypes.c_void_p/int 指针)。
87
+ :param height_ptr: 用于接收高度的指针(ctypes.c_void_p/int 指针)。
88
+ :param buffer_ptr: 用于接收像素数据的指针(ctypes.c_void_p/unsigned char* 指针)。
89
+ :return: 0 表示成功,>0 表示失败。
90
+ """
91
+ return self.lib.nemu_capture_display(
92
+ connect_id,
93
+ display_id,
94
+ buf_len,
95
+ width_ptr,
96
+ height_ptr,
97
+ buffer_ptr,
98
+ )
99
+
100
+ def input_text(self, connect_id: int, text: str) -> int:
101
+ """
102
+ 输入文本。
103
+
104
+ API 原型:
105
+ `int nemu_input_text(int handle, int size, const char* buf)`
106
+
107
+ :param connect_id: 连接 ID。
108
+ :param text: 输入文本(utf-8)。
109
+ :return: 0 表示成功,>0 表示失败。
110
+ """
111
+ buf = text.encode('utf-8')
112
+ return self.lib.nemu_input_text(connect_id, len(buf), buf)
113
+
114
+ def input_touch_down(self, connect_id: int, display_id: int, x: int, y: int) -> int:
115
+ """
116
+ 发送触摸按下事件。
117
+
118
+ API 原型:
119
+ `int nemu_input_event_touch_down(int handle, int displayid, int x_point, int y_point)`
120
+
121
+ :param connect_id: 连接 ID。
122
+ :param display_id: 显示屏 ID。
123
+ :param x: 触摸点 X 坐标。
124
+ :param y: 触摸点 Y 坐标。
125
+ :return: 0 表示成功,>0 表示失败。
126
+ """
127
+ return self.lib.nemu_input_event_touch_down(connect_id, display_id, x, y)
128
+
129
+ def input_touch_up(self, connect_id: int, display_id: int) -> int:
130
+ """
131
+ 发送触摸抬起事件。
132
+
133
+ API 原型:
134
+ `int nemu_input_event_touch_up(int handle, int displayid)`
135
+
136
+ :param connect_id: 连接 ID。
137
+ :param display_id: 显示屏 ID。
138
+ :return: 0 表示成功,>0 表示失败。
139
+ """
140
+ return self.lib.nemu_input_event_touch_up(connect_id, display_id)
141
+
142
+ def input_key_down(self, connect_id: int, display_id: int, key_code: int) -> int:
143
+ """
144
+ 发送按键按下事件。
145
+
146
+ API 原型:
147
+ `int nemu_input_event_key_down(int handle, int displayid, int key_code)`
148
+
149
+ :param connect_id: 连接 ID。
150
+ :param display_id: 显示屏 ID。
151
+ :param key_code: 按键码。
152
+ :return: 0 表示成功,>0 表示失败。
153
+ """
154
+ return self.lib.nemu_input_event_key_down(connect_id, display_id, key_code)
155
+
156
+ def input_key_up(self, connect_id: int, display_id: int, key_code: int) -> int:
157
+ """
158
+ 发送按键抬起事件。
159
+
160
+ API 原型:
161
+ `int nemu_input_event_key_up(int handle, int displayid, int key_code)`
162
+
163
+ :param connect_id: 连接 ID。
164
+ :param display_id: 显示屏 ID。
165
+ :param key_code: 按键码。
166
+ :return: 0 表示成功,>0 表示失败。
167
+ """
168
+ return self.lib.nemu_input_event_key_up(connect_id, display_id, key_code)
169
+
170
+ def input_finger_touch_down(self, connect_id: int, display_id: int, finger_id: int, x: int, y: int) -> int:
171
+ """
172
+ 多指触摸按下。
173
+
174
+ API 原型:
175
+ `int nemu_input_event_finger_touch_down(int handle, int displayid, int finger_id, int x_point, int y_point)`
176
+
177
+ :param connect_id: 连接 ID。
178
+ :param display_id: 显示屏 ID。
179
+ :param finger_id: 手指编号(1-10)。
180
+ :param x: 触摸点 X 坐标。
181
+ :param y: 触摸点 Y 坐标。
182
+ :return: 0 表示成功,>0 表示失败。
183
+ """
184
+ return self.lib.nemu_input_event_finger_touch_down(connect_id, display_id, finger_id, x, y)
185
+
186
+ def input_finger_touch_up(self, connect_id: int, display_id: int, finger_id: int) -> int:
187
+ """
188
+ 多指触摸抬起。
189
+
190
+ API 原型:
191
+ `int nemu_input_event_finger_touch_up(int handle, int displayid, int slot_id)`
192
+
193
+ :param connect_id: 连接 ID。
194
+ :param display_id: 显示屏 ID。
195
+ :param finger_id: 手指编号(1-10)。
196
+ :return: 0 表示成功,>0 表示失败。
197
+ """
198
+ return self.lib.nemu_input_event_finger_touch_up(connect_id, display_id, finger_id)
199
+
200
+ # ------------------------------------------------------------------
201
+ # 内部工具
202
+ # ------------------------------------------------------------------
203
+
204
+ def __load_dll(self, mumu_root_folder: str) -> ctypes.CDLL:
205
+ """尝试多条路径加载 DLL。传入为 MuMu 根目录。"""
206
+ candidate_paths = [
207
+ os.path.join(mumu_root_folder, "shell", "sdk", "external_renderer_ipc.dll"),
208
+ os.path.join(
209
+ mumu_root_folder,
210
+ "shell",
211
+ "nx_device",
212
+ "12.0",
213
+ "sdk",
214
+ "external_renderer_ipc.dll",
215
+ ),
216
+ ]
217
+ for p in candidate_paths:
218
+ if not os.path.exists(p):
219
+ continue
220
+ try:
221
+ return ctypes.CDLL(p)
222
+ except OSError as e: # pragma: no cover
223
+ logger.warning("Failed to load DLL (%s): %s", p, e)
224
+ raise NemuIpcIncompatible("external_renderer_ipc.dll not found or failed to load.")
225
+
226
+ def __declare_prototypes(self) -> None:
227
+ """声明 DLL 函数原型,确保 ctypes 类型安全。"""
228
+ # 连接 / 断开
229
+ self.lib.nemu_connect.argtypes = [ctypes.c_wchar_p, ctypes.c_int]
230
+ self.lib.nemu_connect.restype = ctypes.c_int
231
+
232
+ self.lib.nemu_disconnect.argtypes = [ctypes.c_int]
233
+ self.lib.nemu_disconnect.restype = None
234
+
235
+ # 获取 display id
236
+ self.lib.nemu_get_display_id.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int]
237
+ self.lib.nemu_get_display_id.restype = ctypes.c_int
238
+
239
+ # 截图
240
+ self.lib.nemu_capture_display.argtypes = [
241
+ ctypes.c_int,
242
+ ctypes.c_uint,
243
+ ctypes.c_int,
244
+ ctypes.c_void_p,
245
+ ctypes.c_void_p,
246
+ ctypes.c_void_p,
247
+ ]
248
+ self.lib.nemu_capture_display.restype = ctypes.c_int
249
+
250
+ # 输入文本
251
+ self.lib.nemu_input_text.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p]
252
+ self.lib.nemu_input_text.restype = ctypes.c_int
253
+
254
+ # 触摸
255
+ self.lib.nemu_input_event_touch_down.argtypes = [
256
+ ctypes.c_int,
257
+ ctypes.c_int,
258
+ ctypes.c_int,
259
+ ctypes.c_int,
260
+ ]
261
+ self.lib.nemu_input_event_touch_down.restype = ctypes.c_int
262
+
263
+ self.lib.nemu_input_event_touch_up.argtypes = [ctypes.c_int, ctypes.c_int]
264
+ self.lib.nemu_input_event_touch_up.restype = ctypes.c_int
265
+
266
+ # 按键
267
+ self.lib.nemu_input_event_key_down.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int]
268
+ self.lib.nemu_input_event_key_down.restype = ctypes.c_int
269
+
270
+ self.lib.nemu_input_event_key_up.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int]
271
+ self.lib.nemu_input_event_key_up.restype = ctypes.c_int
272
+
273
+ # 多指触摸
274
+ self.lib.nemu_input_event_finger_touch_down.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int]
275
+ self.lib.nemu_input_event_finger_touch_down.restype = ctypes.c_int
276
+
277
+ self.lib.nemu_input_event_finger_touch_up.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int]
278
+ self.lib.nemu_input_event_finger_touch_up.restype = ctypes.c_int
279
+
280
+ logger.debug("DLL function prototypes declared")