More work on notify kitten

This commit is contained in:
Kovid Goyal
2024-07-29 08:21:45 +05:30
parent f6a24af229
commit 9bd155ae50
2 changed files with 77 additions and 17 deletions

View File

@@ -6,8 +6,10 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time"
"kitty/tools/cli" "kitty/tools/cli"
"kitty/tools/tty"
"kitty/tools/tui/loop" "kitty/tools/tui/loop"
"kitty/tools/utils" "kitty/tools/utils"
) )
@@ -22,7 +24,7 @@ func b64encode(x string) string {
return base64.RawStdEncoding.EncodeToString(utils.UnsafeStringToBytes(x)) return base64.RawStdEncoding.EncodeToString(utils.UnsafeStringToBytes(x))
} }
func create_metadata(opts *Options) string { func create_metadata(opts *Options, wait_till_closed bool, expire_time time.Duration) string {
ans := []string{} ans := []string{}
if opts.AppName != "" { if opts.AppName != "" {
ans = append(ans, "f="+b64encode(opts.AppName)) ans = append(ans, "f="+b64encode(opts.AppName))
@@ -33,13 +35,13 @@ func create_metadata(opts *Options) string {
case "critical": case "critical":
ans = append(ans, "u=2") ans = append(ans, "u=2")
} }
if opts.ExpireTime >= 0 { if expire_time >= 0 {
ans = append(ans, "w="+strconv.Itoa(opts.ExpireTime)) ans = append(ans, "w="+strconv.FormatInt(expire_time.Milliseconds(), 10))
} }
if opts.Type != "" { if opts.Type != "" {
ans = append(ans, "t="+b64encode(opts.Type)) ans = append(ans, "t="+b64encode(opts.Type))
} }
if opts.WaitTillClosed { if wait_till_closed {
ans = append(ans, "c=1") ans = append(ans, "c=1")
} }
m := strings.Join(ans, ":") m := strings.Join(ans, ":")
@@ -49,7 +51,9 @@ func create_metadata(opts *Options) string {
return m return m
} }
func generate_chunks(title, body, identifier string, opts *Options, wait_till_closed bool, callback func(string)) { var debugprintln = tty.DebugPrintln
func generate_chunks(title, body, identifier string, opts *Options, wait_till_closed bool, expire_time time.Duration, callback func(string)) {
prefix := ESC_CODE_PREFIX + "i=" + identifier prefix := ESC_CODE_PREFIX + "i=" + identifier
write_chunk := func(middle string) { write_chunk := func(middle string) {
callback(prefix + middle + ESC_CODE_SUFFIX) callback(prefix + middle + ESC_CODE_SUFFIX)
@@ -61,10 +65,10 @@ func generate_chunks(title, body, identifier string, opts *Options, wait_till_cl
chunk := payload[:min(CHUNK_SIZE, len(payload))] chunk := payload[:min(CHUNK_SIZE, len(payload))]
payload = utils.IfElse(len(payload) > len(chunk), payload[len(chunk):], "") payload = utils.IfElse(len(payload) > len(chunk), payload[len(chunk):], "")
enc := b64encode(chunk) enc := b64encode(chunk)
write_chunk(":d=0" + p + ";" + enc) write_chunk(":d=0:e=1" + p + ";" + enc)
} }
} }
metadata := create_metadata(opts) metadata := create_metadata(opts, wait_till_closed, expire_time)
write_chunk(":d=0" + metadata + ";") write_chunk(":d=0" + metadata + ";")
add_payload("title", title) add_payload("title", title)
if body != "" { if body != "" {
@@ -73,22 +77,60 @@ func generate_chunks(title, body, identifier string, opts *Options, wait_till_cl
write_chunk(";") write_chunk(";")
} }
func run_loop(title, body, identifier string, opts *Options, wait_till_closed bool) (err error) { func run_loop(title, body, identifier string, opts *Options, wait_till_closed bool, expire_time time.Duration) (err error) {
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking) lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
if err != nil { if err != nil {
return err return err
} }
lp.OnInitialize = func() (string, error) { lp.OnInitialize = func() (string, error) {
generate_chunks(title, body, identifier, opts, wait_till_closed, func(x string) { lp.QueueWriteString(x) }) generate_chunks(title, body, identifier, opts, wait_till_closed, expire_time, func(x string) { lp.QueueWriteString(x) })
return "", nil return "", nil
} }
err = lp.Run()
ds := lp.DeathSignalName()
if ds != "" {
fmt.Println("Killed by signal: ", ds)
lp.KillIfSignalled()
return
}
return
} }
func random_ident() (string, error) { func random_ident() (string, error) {
return utils.HumanUUID4() return utils.HumanUUID4()
} }
func parse_duration(x string) (ans time.Duration, err error) {
switch x {
case "never":
return 0, nil
case "":
return -1, nil
}
trailer := x[len(x)-1]
multipler := time.Second
switch trailer {
case 's':
x = x[:len(x)-1]
case 'm':
x = x[:len(x)-1]
multipler = time.Minute
case 'h':
x = x[:len(x)-1]
multipler = time.Hour
case 'd':
x = x[:len(x)-1]
multipler = time.Hour * 24
}
val, err := strconv.ParseFloat(x, 64)
if err != nil {
return ans, err
}
ans = time.Duration(float64(multipler) * val)
return
}
func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) { func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
if len(args) == 0 { if len(args) == 0 {
return 1, fmt.Errorf("Must specify a TITLE for the notification") return 1, fmt.Errorf("Must specify a TITLE for the notification")
@@ -107,9 +149,13 @@ func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
return 1, fmt.Errorf("Failed to generate a random identifier with error: %w", err) return 1, fmt.Errorf("Failed to generate a random identifier with error: %w", err)
} }
} }
var expire_time time.Duration
if expire_time, err = parse_duration(opts.ExpireTime); err != nil {
return 1, fmt.Errorf("Invalid expire time: %s with error: %w", opts.ExpireTime, err)
}
wait_till_closed := opts.WaitTillClosed wait_till_closed := opts.WaitTillClosed
if opts.OnlyPrintEscapeCode { if opts.OnlyPrintEscapeCode {
generate_chunks(title, body, ident, opts, wait_till_closed, func(x string) { generate_chunks(title, body, ident, opts, wait_till_closed, expire_time, func(x string) {
if err == nil { if err == nil {
_, err = os.Stdout.WriteString(x) _, err = os.Stdout.WriteString(x)
} }
@@ -118,7 +164,21 @@ func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
if opts.PrintIdentifier { if opts.PrintIdentifier {
fmt.Println(ident) fmt.Println(ident)
} }
err = run_loop(title, body, ident, opts, wait_till_closed) if wait_till_closed {
err = run_loop(title, body, ident, opts, wait_till_closed, expire_time)
} else {
var term *tty.Term
if term, err = tty.OpenControllingTerm(); err != nil {
return 1, fmt.Errorf("Failed to open controlling terminal with error: %w", err)
}
generate_chunks(title, body, ident, opts, wait_till_closed, expire_time, func(x string) {
if err == nil {
_, err = term.WriteString(x)
}
})
term.RestoreAndClose()
}
} }
if err != nil { if err != nil {
rc = 1 rc = 1

View File

@@ -16,13 +16,13 @@ The urgency of the notification.
--expire-time -t --expire-time -t
default=-1 The duration, for the notification to appear on screen. The default is to
type=int use the policy of the OS notification service. A value of :code:`never` means the notification should
The duration, in milliseconds, for the notification to appear on screen. The default is to
use the policy of the OS notification service. A value of 0 means the notification should
never expire, however, this may or may not work depending on the policies of the OS notification never expire, however, this may or may not work depending on the policies of the OS notification
service. Positive values guarantee the notification will be closed automatically service. Time is specified in the form NUMBER[SUFFIX] where SUFFIX can be :code:`s` for seconds, :code:`m` for minutes,
after that many milliseconds have elapsed. The notification could be closed before by user :code:`h` for hours or :code:`d` for days. Non-integer numbers are allowed.
If not specified, seconds is assumed. The notification is guaranteed to be closed automatically
after the specified time has elapsed. The notification could be closed before by user
action or OS policy. action or OS policy.