mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-07 17:43:53 +02:00
List families asynchronously
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from typing import Dict, List, Sequence
|
||||
from typing import Any, BinaryIO, Dict, List, Optional, Sequence
|
||||
|
||||
from kitty.constants import is_macos
|
||||
|
||||
@@ -14,30 +14,58 @@ else:
|
||||
from .fontconfig import list_fonts, prune_family_group
|
||||
|
||||
|
||||
def create_family_groups(monospaced: bool = True) -> Dict[str, List[ListedFont]]:
|
||||
def create_family_groups(monospaced: bool = True, add_variable_data: bool = False) -> Dict[str, List[ListedFont]]:
|
||||
g: Dict[str, List[ListedFont]] = {}
|
||||
for f in list_fonts():
|
||||
if not monospaced or f['is_monospace']:
|
||||
g.setdefault(f['family'], []).append(f)
|
||||
if add_variable_data and f['is_variable']:
|
||||
f['variable_data'] = get_variable_data_for_descriptor(f['descriptor']) # type: ignore
|
||||
return {k: prune_family_group(v) for k, v in g.items()}
|
||||
|
||||
|
||||
def as_json() -> str:
|
||||
def as_json(indent: Optional[int] = None) -> str:
|
||||
import json
|
||||
groups = create_family_groups()
|
||||
for g in groups.values():
|
||||
for f in g:
|
||||
if f['is_variable']:
|
||||
f['variable_data'] = get_variable_data_for_descriptor(f['descriptor'])
|
||||
return json.dumps(groups, indent=2)
|
||||
groups = create_family_groups(add_variable_data=True)
|
||||
return json.dumps(groups, indent=indent)
|
||||
|
||||
|
||||
exception_in_io_handler: Optional[Exception] = None
|
||||
|
||||
|
||||
def handle_io(from_kitten: BinaryIO, to_kitten: BinaryIO) -> None:
|
||||
import json
|
||||
global exception_in_io_handler
|
||||
|
||||
def send_to_kitten(x: Any) -> None:
|
||||
to_kitten.write(json.dumps(x).encode())
|
||||
to_kitten.write(b'\n')
|
||||
|
||||
try:
|
||||
send_to_kitten(create_family_groups(add_variable_data=True))
|
||||
to_kitten.write(as_json().encode())
|
||||
for line in from_kitten:
|
||||
cmd = json.loads(line)
|
||||
action = cmd['action']
|
||||
if action == 'ping':
|
||||
send_to_kitten({'action': 'pong'})
|
||||
except Exception as e:
|
||||
exception_in_io_handler = e
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
import subprocess
|
||||
import sys
|
||||
from threading import Thread
|
||||
|
||||
from kitty.constants import kitten_exe
|
||||
argv = list(argv)
|
||||
if '--psnames' in argv:
|
||||
argv.remove('--psnames')
|
||||
cp = subprocess.run([kitten_exe(), '__list_fonts__'], input=as_json().encode())
|
||||
raise SystemExit(cp.returncode)
|
||||
p = subprocess.Popen([kitten_exe(), '__list_fonts__'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
Thread(target=handle_io, args=(p.stdout, p.stdin), daemon=True).start()
|
||||
try:
|
||||
raise SystemExit(p.wait())
|
||||
finally:
|
||||
if exception_in_io_handler is not None:
|
||||
print(exception_in_io_handler, file=sys.stderr)
|
||||
|
||||
@@ -12,18 +12,15 @@ import (
|
||||
|
||||
var _ = fmt.Print
|
||||
var debugprintln = tty.DebugPrintln
|
||||
var json_decoder *json.Decoder
|
||||
|
||||
func main() (rc int, err error) {
|
||||
d := json.NewDecoder(os.Stdin)
|
||||
var fonts map[string][]ListedFont
|
||||
if err = d.Decode(&fonts); err != nil {
|
||||
return 1, err
|
||||
}
|
||||
json_decoder = json.NewDecoder(os.Stdin)
|
||||
lp, err := loop.New()
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
h := &handler{lp: lp, fonts: fonts}
|
||||
h := &handler{lp: lp}
|
||||
lp.OnInitialize = func() (string, error) {
|
||||
lp.AllowLineWrapping(false)
|
||||
lp.SetWindowTitle(`Choose a font for kitty`)
|
||||
|
||||
@@ -3,6 +3,7 @@ package list_fonts
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"kitty/tools/tui/loop"
|
||||
"kitty/tools/tui/readline"
|
||||
@@ -16,20 +17,35 @@ var _ = fmt.Print
|
||||
type State int
|
||||
|
||||
const (
|
||||
LISTING_FAMILIES State = iota
|
||||
SCANNING_FAMILIES State = iota
|
||||
LISTING_FAMILIES
|
||||
CHOOSING_FACES
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
lp *loop.Loop
|
||||
fonts map[string][]ListedFont
|
||||
state State
|
||||
lp *loop.Loop
|
||||
fonts map[string][]ListedFont
|
||||
state State
|
||||
err_mutex sync.Mutex
|
||||
err_in_worker_thread error
|
||||
|
||||
// Listing
|
||||
rl *readline.Readline
|
||||
family_list FamilyList
|
||||
}
|
||||
|
||||
func (h *handler) set_worker_error(err error) {
|
||||
h.err_mutex.Lock()
|
||||
defer h.err_mutex.Unlock()
|
||||
h.err_in_worker_thread = err
|
||||
}
|
||||
|
||||
func (h *handler) get_worker_error() error {
|
||||
h.err_mutex.Lock()
|
||||
defer h.err_mutex.Unlock()
|
||||
return h.err_in_worker_thread
|
||||
}
|
||||
|
||||
// Listing families {{{
|
||||
func (h *handler) draw_search_bar() {
|
||||
h.lp.SetCursorVisible(true)
|
||||
@@ -103,9 +119,15 @@ func (h *handler) next(delta int, allow_wrapping bool) {
|
||||
}
|
||||
|
||||
func (h *handler) handle_listing_key_event(event *loop.KeyEvent) (err error) {
|
||||
if event.MatchesPressOrRepeat("ctrl+c") || event.MatchesPressOrRepeat("esc") {
|
||||
h.lp.Quit(1)
|
||||
if event.MatchesPressOrRepeat("esc") {
|
||||
event.Handled = true
|
||||
if h.rl.AllText() != "" {
|
||||
h.rl.ResetText()
|
||||
h.update_family_search()
|
||||
h.draw_screen()
|
||||
} else {
|
||||
h.lp.Quit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
ev := event
|
||||
@@ -162,9 +184,12 @@ func (h *handler) handle_listing_text(text string, from_key_event bool, in_brack
|
||||
// Events {{{
|
||||
func (h *handler) initialize() {
|
||||
h.lp.SetCursorVisible(false)
|
||||
h.family_list.UpdateFamilies(utils.StableSortWithKey(maps.Keys(h.fonts), strings.ToLower))
|
||||
h.rl = readline.New(h.lp, readline.RlInit{DontMarkPrompts: true, Prompt: "Family: "})
|
||||
h.draw_screen()
|
||||
go func() {
|
||||
h.set_worker_error(json_decoder.Decode(&h.fonts))
|
||||
h.lp.WakeupMainThread()
|
||||
}()
|
||||
}
|
||||
|
||||
func (h *handler) finalize() {
|
||||
@@ -178,6 +203,9 @@ func (h *handler) draw_screen() (err error) {
|
||||
h.lp.ClearScreen()
|
||||
h.lp.AllowLineWrapping(false)
|
||||
switch h.state {
|
||||
case SCANNING_FAMILIES:
|
||||
h.lp.Println("Scanning system for fonts, please wait...")
|
||||
return nil
|
||||
case LISTING_FAMILIES:
|
||||
return h.draw_listing_screen()
|
||||
}
|
||||
@@ -185,10 +213,24 @@ func (h *handler) draw_screen() (err error) {
|
||||
}
|
||||
|
||||
func (h *handler) on_wakeup() (err error) {
|
||||
if err = h.get_worker_error(); err != nil {
|
||||
return
|
||||
}
|
||||
switch h.state {
|
||||
case SCANNING_FAMILIES:
|
||||
h.state = LISTING_FAMILIES
|
||||
h.family_list.UpdateFamilies(utils.StableSortWithKey(maps.Keys(h.fonts), strings.ToLower))
|
||||
return h.draw_screen()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *handler) on_key_event(event *loop.KeyEvent) (err error) {
|
||||
if event.MatchesPressOrRepeat("ctrl+c") {
|
||||
event.Handled = true
|
||||
h.lp.Quit(1)
|
||||
return nil
|
||||
}
|
||||
switch h.state {
|
||||
case LISTING_FAMILIES:
|
||||
return h.handle_listing_key_event(event)
|
||||
|
||||
Reference in New Issue
Block a user