#!/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