summaryrefslogtreecommitdiff
path: root/.config/eww/listapps.py
blob: 2dae1dcd6c575ad040d740caf3dfe976bb8d7062 (plain)
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