generated from SteamDeckHomebrew/decky-plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 8
/
main.py
128 lines (116 loc) · 4.1 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import sys
import subprocess
def get_all_children(pid: int) -> list[str]:
pids = []
tmpPids = [str(pid)]
try:
while tmpPids:
ppid = tmpPids.pop(0)
lines = []
with subprocess.Popen(["ps", "--ppid", ppid, "-o", "pid="], stdout=subprocess.PIPE) as p:
lines = p.stdout.readlines()
for chldPid in lines:
chldPid = chldPid.strip()
if not chldPid:
continue
pids.append(chldPid)
tmpPids.append(chldPid)
return pids
except:
return pids
class Plugin:
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
async def _main(self):
pass
# Function called first during the unload process, utilize this to handle your plugin being removed
async def _unload(self):
pass
# Migrations that should be performed before entering `_main()`.
async def _migration(self):
pass
async def is_paused(self, pid: int) -> bool:
try:
with subprocess.Popen(["ps", "--ppid", str(pid), "-o", "stat="], stdout=subprocess.PIPE) as p:
return p.stdout.readline().lstrip().startswith(b'T')
except:
return False
async def pause(self, pid: int) -> bool:
pids = get_all_children(pid)
if pids:
command = ["kill", "-SIGSTOP"]
command.extend(pids)
try:
return subprocess.run(command, stderr=sys.stderr, stdout=sys.stdout).returncode == 0
except:
return False
else:
return False
async def resume(self, pid: int) -> bool:
pids = get_all_children(pid)
if pids:
command = ["kill", "-SIGCONT"]
command.extend(pids)
try:
return subprocess.run(command, stderr=sys.stderr, stdout=sys.stdout).returncode == 0
except:
return False
else:
return False
async def terminate(self, pid: int) -> bool:
pids = get_all_children(pid)
if pids:
command = ["kill", "-SIGTERM"]
command.extend(pids)
try:
return subprocess.run(command, stderr=sys.stderr, stdout=sys.stdout).returncode == 0
except:
return False
else:
return False
async def kill(self, pid: int) -> bool:
pids = get_all_children(pid)
if pids:
command = ["kill", "-SIGKILL"]
command.extend(pids)
try:
return subprocess.run(command, stderr=sys.stderr, stdout=sys.stdout).returncode == 0
except:
return False
else:
return False
async def pid_from_appid(self, appid: int) -> int:
pid = ""
try:
with subprocess.Popen(["pgrep", "--full", "--oldest", f"/reaper\\s.*\\bAppId={appid}\\b"], stdout=subprocess.PIPE) as p:
pid = p.stdout.read().strip()
except:
return 0
if not pid:
return 0
return int(pid)
async def appid_from_pid(self, pid: int) -> int:
# search upwards for the process that has the AppId= command line argument
while pid and pid != 1:
try:
args = []
with open(f"/proc/{pid}/cmdline", "r") as f:
args = f.read().split('\0')
for arg in args:
arg = arg.strip()
if arg.startswith("AppId="):
arg = arg.lstrip("AppId=")
if arg:
return int(arg)
except:
pass
try:
strppid = ""
with subprocess.Popen(["ps", "--pid", str(pid), "-o", "ppid="], stdout=subprocess.PIPE) as p:
strppid = p.stdout.read().strip()
if strppid:
pid = int(strppid)
else:
break
except:
break
return 0