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
|
#!/usr/bin/env python3
import glob
import json
import os
import os.path
import shlex
import sqlite3
import sys
from dataclasses import dataclass
@dataclass
class App:
name: str
icon: str | None
command: str
def parse_desktop_file(path: str) -> App:
section = ''
app = App('', None, '')
with open(path, 'r') as file:
for line in file:
line = line.split('#')[0]
if line == '':
continue
if line.startswith('['):
section = line.rstrip().removeprefix('[').removesuffix(']')
continue
entry = line.split('=')
if len(entry) < 2:
continue
k, v = entry[0], entry[1].rstrip()
if k == 'Name':
app.name = v
elif k == 'Icon':
app.icon = v
elif k == 'Exec':
app.command = v.replace('%u', '').replace('%U', '').replace('%F', '').rstrip()
return app
def get_apps() -> dict[str, App]:
apps = {}
for dir in os.environ['PATH'].split(':'):
if os.path.exists(dir):
for ent in os.listdir(dir):
if os.access(os.path.join(dir, ent), os.X_OK):
apps[ent] = App(ent, None, ent)
for path in glob.glob('/usr/share/applications/*.desktop'):
app = parse_desktop_file(path)
apps[app.name] = app
for path in glob.glob(os.path.expanduser('~/.local/share/applications/*.desktop')):
app = parse_desktop_file(path)
apps[app.name] = app
return apps
def printusage(argv0):
print(f'{argv0} query [QUERY] | run NAME', file=sys.stderr)
def main(argv: list[str]) -> int:
conn = sqlite3.connect(os.path.expanduser('~/.local/share/eww/apps.sqlite'))
conn.execute('''
create table if not exists app(
id integer primary key,
name text unique not null,
count integer default 0 not null
);
''')
apps = get_apps()
with conn:
for app in apps.values():
conn.execute('insert or ignore into app (name) values (?)', (app.name, ))
if len(argv) < 2:
printusage(argv[0])
return 1
if argv[1] == 'query':
out = []
query = '' if len(argv) < 3 else argv[2]
for name, in conn.execute('''
select name
from app
where name like '%'||?||'%'
order by count desc, name
''', (query, )).fetchall():
app = apps[name]
d = {'name': app.name}
if app.icon is not None:
d['icon'] = app.icon
out.append(d)
data = json.dumps(out, separators=(',', ':'))
argv = ['eww', 'update', 'applist=' + data]
os.execvp(argv[0], argv)
elif argv[1] == 'run':
if app := apps.get(argv[2]):
with conn:
conn.execute('insert or ignore into app (name) values (?)', (app.name, ))
conn.execute('update app set count = count + 1 where name = ?', (app.name, ))
argv = shlex.split(app.command)
os.execvp(argv[0], argv)
else:
print(f'app {argv[2]} not found', file=sys.stderr)
else:
printusage(argv[0])
return 1
return 0
if __name__ == '__main__':
try:
sys.exit(main(sys.argv))
except BrokenPipeError:
pass
|