diff options
Diffstat (limited to '.config/eww/listapps.py')
| -rwxr-xr-x | .config/eww/listapps.py | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/.config/eww/listapps.py b/.config/eww/listapps.py new file mode 100755 index 0000000..2dae1dc --- /dev/null +++ b/.config/eww/listapps.py @@ -0,0 +1,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 |
