prkit 0.2.0__tar.gz → 0.2.1__tar.gz
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.
- {prkit-0.2.0 → prkit-0.2.1}/PKG-INFO +6 -6
- {prkit-0.2.0 → prkit-0.2.1}/README.md +5 -5
- {prkit-0.2.0 → prkit-0.2.1}/pyproject.toml +1 -1
- {prkit-0.2.0 → prkit-0.2.1}/pyproject.toml.orig +1 -1
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/__init__.py +1 -1
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/cursor_watchdog.py +23 -3
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/windows_cursor.py +39 -8
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/__main__.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/app.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/capslock.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/icon.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/input_source.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/keyboard.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/keys.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/mac_capslock.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/mac_keyboard.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/overlays.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/platform.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/py.typed +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/settings.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/spotlight.py +0 -0
- {prkit-0.2.0 → prkit-0.2.1}/src/prkit/tray.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: prkit
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A cross-platform system-tray presentation toolkit
|
|
5
5
|
Author: meebox
|
|
6
6
|
Author-email: meebox <meebox@gmail.com>
|
|
@@ -36,12 +36,12 @@ Description-Content-Type: text/markdown
|
|
|
36
36
|
|
|
37
37
|
| 平台 | 下載檔案 | 啟動方式 |
|
|
38
38
|
| --- | --- | --- |
|
|
39
|
-
| Windows x64 | `PRKit-0.2.
|
|
40
|
-
| macOS Apple Silicon | `PRKit-0.2.
|
|
39
|
+
| Windows x64 | `PRKit-0.2.1-windows-x64.exe` | 直接開啟執行 |
|
|
40
|
+
| macOS Apple Silicon | `PRKit-0.2.1-macos-arm64.dmg` | 開啟 DMG,將 `PRKit.app` 拖到「Applications」捷徑後啟動 |
|
|
41
41
|
|
|
42
|
-
Windows
|
|
42
|
+
Windows 為單一 EXE,已包含執行環境與游標復原 watchdog,不需額外下載檔案。macOS 目前僅提供 Apple Silicon 版。程式啟動後顯示在系統匣/選單列,不會開啟主視窗。
|
|
43
43
|
|
|
44
|
-
桌面版尚未使用 Windows 發行者憑證或 Apple Developer ID 簽署/公證;macOS 僅使用 ad-hoc 簽章。若系統阻擋你確認來源的下載檔案,請依系統安全提示允許開啟。macOS 按鍵顯示仍需在「輸入監控」允許 `PRKit
|
|
44
|
+
桌面版尚未使用 Windows 發行者憑證或 Apple Developer ID 簽署/公證;macOS 僅使用 ad-hoc 簽章。若系統阻擋你確認來源的下載檔案,請依系統安全提示允許開啟。macOS 按鍵顯示仍需在「輸入監控」允許 `PRKit`;輸入法狀態模式不需此權限。每個下載檔另提供 `.sha256` 校驗檔。
|
|
45
45
|
|
|
46
46
|
版本標籤會自動觸發 GitHub 雲端打包與成品測試;手動建置可在 Actions 的 **Build desktop apps** 執行,成品在該次執行的 Artifacts。詳細操作見[發佈指南](docs/RELEASING.md)。
|
|
47
47
|
|
|
@@ -68,7 +68,7 @@ uv tool install git+https://github.com/codemee/prkit.git@latest
|
|
|
68
68
|
prkit
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
若要固定特定版本,將 `latest` 改成版本標籤,例如 `v0.2.
|
|
71
|
+
若要固定特定版本,將 `latest` 改成版本標籤,例如 `v0.2.1`。
|
|
72
72
|
|
|
73
73
|
若安裝後找不到 `prkit` 指令,執行 `uv tool update-shell`,再重新開啟終端機。移除工具可使用 `uv tool uninstall prkit`。
|
|
74
74
|
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
|
|
20
20
|
| 平台 | 下載檔案 | 啟動方式 |
|
|
21
21
|
| --- | --- | --- |
|
|
22
|
-
| Windows x64 | `PRKit-0.2.
|
|
23
|
-
| macOS Apple Silicon | `PRKit-0.2.
|
|
22
|
+
| Windows x64 | `PRKit-0.2.1-windows-x64.exe` | 直接開啟執行 |
|
|
23
|
+
| macOS Apple Silicon | `PRKit-0.2.1-macos-arm64.dmg` | 開啟 DMG,將 `PRKit.app` 拖到「Applications」捷徑後啟動 |
|
|
24
24
|
|
|
25
|
-
Windows
|
|
25
|
+
Windows 為單一 EXE,已包含執行環境與游標復原 watchdog,不需額外下載檔案。macOS 目前僅提供 Apple Silicon 版。程式啟動後顯示在系統匣/選單列,不會開啟主視窗。
|
|
26
26
|
|
|
27
|
-
桌面版尚未使用 Windows 發行者憑證或 Apple Developer ID 簽署/公證;macOS 僅使用 ad-hoc 簽章。若系統阻擋你確認來源的下載檔案,請依系統安全提示允許開啟。macOS 按鍵顯示仍需在「輸入監控」允許 `PRKit
|
|
27
|
+
桌面版尚未使用 Windows 發行者憑證或 Apple Developer ID 簽署/公證;macOS 僅使用 ad-hoc 簽章。若系統阻擋你確認來源的下載檔案,請依系統安全提示允許開啟。macOS 按鍵顯示仍需在「輸入監控」允許 `PRKit`;輸入法狀態模式不需此權限。每個下載檔另提供 `.sha256` 校驗檔。
|
|
28
28
|
|
|
29
29
|
版本標籤會自動觸發 GitHub 雲端打包與成品測試;手動建置可在 Actions 的 **Build desktop apps** 執行,成品在該次執行的 Artifacts。詳細操作見[發佈指南](docs/RELEASING.md)。
|
|
30
30
|
|
|
@@ -51,7 +51,7 @@ uv tool install git+https://github.com/codemee/prkit.git@latest
|
|
|
51
51
|
prkit
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
若要固定特定版本,將 `latest` 改成版本標籤,例如 `v0.2.
|
|
54
|
+
若要固定特定版本,將 `latest` 改成版本標籤,例如 `v0.2.1`。
|
|
55
55
|
|
|
56
56
|
若安裝後找不到 `prkit` 指令,執行 `uv tool update-shell`,再重新開啟終端機。移除工具可使用 `uv tool uninstall prkit`。
|
|
57
57
|
|
|
@@ -6,6 +6,7 @@ from ctypes import wintypes
|
|
|
6
6
|
|
|
7
7
|
SPI_SETCURSORS = 0x0057
|
|
8
8
|
SYNCHRONIZE = 0x00100000
|
|
9
|
+
EVENT_MODIFY_STATE = 0x0002
|
|
9
10
|
WAIT_OBJECT_0 = 0
|
|
10
11
|
INFINITE = 0xFFFFFFFF
|
|
11
12
|
|
|
@@ -14,10 +15,20 @@ def restore_system_cursors() -> None:
|
|
|
14
15
|
ctypes.windll.user32.SystemParametersInfoW(SPI_SETCURSORS, 0, None, 0)
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def watch(parent_pid: int, stop_event_name: str) -> int:
|
|
18
|
+
def watch(parent_pid: int, stop_event_name: str, ready_event_name: str | None = None) -> int:
|
|
18
19
|
kernel32 = ctypes.windll.kernel32
|
|
19
20
|
kernel32.OpenProcess.restype = wintypes.HANDLE
|
|
21
|
+
kernel32.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
|
|
20
22
|
kernel32.OpenEventW.restype = wintypes.HANDLE
|
|
23
|
+
kernel32.OpenEventW.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.LPCWSTR]
|
|
24
|
+
kernel32.SetEvent.argtypes = [wintypes.HANDLE]
|
|
25
|
+
kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
|
|
26
|
+
kernel32.WaitForMultipleObjects.argtypes = [
|
|
27
|
+
wintypes.DWORD,
|
|
28
|
+
ctypes.POINTER(wintypes.HANDLE),
|
|
29
|
+
wintypes.BOOL,
|
|
30
|
+
wintypes.DWORD,
|
|
31
|
+
]
|
|
21
32
|
parent = kernel32.OpenProcess(SYNCHRONIZE, False, parent_pid)
|
|
22
33
|
stop_event = kernel32.OpenEventW(SYNCHRONIZE, False, stop_event_name)
|
|
23
34
|
if not parent or not stop_event:
|
|
@@ -28,6 +39,15 @@ def watch(parent_pid: int, stop_event_name: str) -> int:
|
|
|
28
39
|
kernel32.CloseHandle(stop_event)
|
|
29
40
|
return 1
|
|
30
41
|
try:
|
|
42
|
+
if ready_event_name is not None:
|
|
43
|
+
ready = kernel32.OpenEventW(EVENT_MODIFY_STATE, False, ready_event_name)
|
|
44
|
+
if not ready:
|
|
45
|
+
return 1
|
|
46
|
+
try:
|
|
47
|
+
if not kernel32.SetEvent(ready):
|
|
48
|
+
return 1
|
|
49
|
+
finally:
|
|
50
|
+
kernel32.CloseHandle(ready)
|
|
31
51
|
handles = (wintypes.HANDLE * 2)(parent, stop_event)
|
|
32
52
|
result = kernel32.WaitForMultipleObjects(2, handles, False, INFINITE)
|
|
33
53
|
if result == WAIT_OBJECT_0:
|
|
@@ -39,9 +59,9 @@ def watch(parent_pid: int, stop_event_name: str) -> int:
|
|
|
39
59
|
|
|
40
60
|
|
|
41
61
|
def main() -> int:
|
|
42
|
-
if sys.platform != "win32" or len(sys.argv)
|
|
62
|
+
if sys.platform != "win32" or len(sys.argv) not in {3, 4}:
|
|
43
63
|
return 2
|
|
44
|
-
return watch(int(sys.argv[1]), sys.argv[2])
|
|
64
|
+
return watch(int(sys.argv[1]), sys.argv[2], sys.argv[3] if len(sys.argv) == 4 else None)
|
|
45
65
|
|
|
46
66
|
|
|
47
67
|
if __name__ == "__main__":
|
|
@@ -101,29 +101,61 @@ class CursorWatchdog:
|
|
|
101
101
|
def __init__(self) -> None:
|
|
102
102
|
self._kernel32 = ctypes.windll.kernel32
|
|
103
103
|
self._kernel32.CreateEventW.restype = wintypes.HANDLE
|
|
104
|
+
self._kernel32.CreateEventW.argtypes = [
|
|
105
|
+
ctypes.c_void_p,
|
|
106
|
+
wintypes.BOOL,
|
|
107
|
+
wintypes.BOOL,
|
|
108
|
+
wintypes.LPCWSTR,
|
|
109
|
+
]
|
|
110
|
+
self._kernel32.SetEvent.argtypes = [wintypes.HANDLE]
|
|
111
|
+
self._kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
|
|
112
|
+
self._kernel32.WaitForSingleObject.argtypes = [wintypes.HANDLE, wintypes.DWORD]
|
|
104
113
|
self._event_name = f"Local\\PrtoolsCursorRestore-{uuid.uuid4()}"
|
|
114
|
+
self._ready_event_name = self._event_name + "-ready"
|
|
105
115
|
self._event = self._kernel32.CreateEventW(None, True, False, self._event_name)
|
|
106
116
|
if not self._event:
|
|
107
117
|
raise CursorBackendError("無法建立游標復原事件")
|
|
118
|
+
ready = self._kernel32.CreateEventW(None, True, False, self._ready_event_name)
|
|
119
|
+
if not ready:
|
|
120
|
+
self._kernel32.CloseHandle(self._event)
|
|
121
|
+
self._event = None
|
|
122
|
+
raise CursorBackendError("無法建立游標監控就緒事件")
|
|
108
123
|
try:
|
|
124
|
+
environment = os.environ.copy()
|
|
125
|
+
if getattr(sys, "frozen", False):
|
|
126
|
+
# The watchdog must outlive the GUI and own its unpacked runtime.
|
|
127
|
+
environment["PYINSTALLER_RESET_ENVIRONMENT"] = "1"
|
|
109
128
|
self._process = subprocess.Popen(
|
|
110
129
|
self._command(),
|
|
111
130
|
creationflags=CREATE_NO_WINDOW,
|
|
112
131
|
close_fds=True,
|
|
132
|
+
env=environment,
|
|
113
133
|
)
|
|
114
|
-
time.
|
|
115
|
-
|
|
116
|
-
|
|
134
|
+
deadline = time.monotonic() + 30
|
|
135
|
+
while self._kernel32.WaitForSingleObject(ready, 100) != 0:
|
|
136
|
+
if self._process.poll() is not None:
|
|
137
|
+
raise CursorBackendError("游標復原監控程序啟動失敗")
|
|
138
|
+
if time.monotonic() >= deadline:
|
|
139
|
+
raise CursorBackendError("游標復原監控程序啟動逾時")
|
|
117
140
|
except Exception:
|
|
141
|
+
self._kernel32.SetEvent(self._event)
|
|
142
|
+
if hasattr(self, "_process") and self._process.poll() is None:
|
|
143
|
+
self._process.terminate()
|
|
144
|
+
self._process.wait(timeout=5)
|
|
118
145
|
self._kernel32.CloseHandle(self._event)
|
|
119
146
|
self._event = None
|
|
120
147
|
raise
|
|
148
|
+
finally:
|
|
149
|
+
self._kernel32.CloseHandle(ready)
|
|
121
150
|
|
|
122
151
|
def _command(self) -> list[str]:
|
|
152
|
+
arguments = [str(os.getpid()), self._event_name, self._ready_event_name]
|
|
153
|
+
if getattr(sys, "frozen", False):
|
|
154
|
+
return [sys.executable, "--cursor-watchdog", *arguments]
|
|
123
155
|
sibling = Path(sys.executable).with_name("prkit-cursor-watchdog.exe")
|
|
124
156
|
if sibling.is_file():
|
|
125
|
-
return [str(sibling),
|
|
126
|
-
if
|
|
157
|
+
return [str(sibling), *arguments]
|
|
158
|
+
if "__compiled__" in globals():
|
|
127
159
|
raise CursorBackendError("找不到 prkit-cursor-watchdog.exe")
|
|
128
160
|
pythonw = Path(sys.executable).with_name("pythonw.exe")
|
|
129
161
|
executable = pythonw if pythonw.is_file() else Path(sys.executable)
|
|
@@ -131,8 +163,7 @@ class CursorWatchdog:
|
|
|
131
163
|
str(executable),
|
|
132
164
|
"-m",
|
|
133
165
|
"prkit.cursor_watchdog",
|
|
134
|
-
|
|
135
|
-
self._event_name,
|
|
166
|
+
*arguments,
|
|
136
167
|
]
|
|
137
168
|
|
|
138
169
|
def stop(self) -> None:
|
|
@@ -140,7 +171,7 @@ class CursorWatchdog:
|
|
|
140
171
|
return
|
|
141
172
|
self._kernel32.SetEvent(self._event)
|
|
142
173
|
try:
|
|
143
|
-
self._process.wait(timeout=
|
|
174
|
+
self._process.wait(timeout=10)
|
|
144
175
|
except subprocess.TimeoutExpired:
|
|
145
176
|
self._process.terminate()
|
|
146
177
|
self._kernel32.CloseHandle(self._event)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|