Skip to content

Commit

Permalink
add system tray icon
Browse files Browse the repository at this point in the history
  • Loading branch information
buck54321 committed Nov 21, 2022
1 parent 7852e38 commit bb03538
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 24 deletions.
15 changes: 15 additions & 0 deletions client/cmd/dexc-desktop/fileurl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build !darwin && !windows

package main

import (
"path/filepath"
)

func filePathToURL(name string) (string, error) {
path, err := filepath.Abs(name)
if err != nil { // can't pwd if name was relative, probably impossible
return "", err
}
return "file://" + path, nil
}
7 changes: 7 additions & 0 deletions client/cmd/dexc-desktop/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ replace decred.org/dcrdex => ../../..

require (
decred.org/dcrdex v0.5.5
fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93
github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6
github.com/webview/webview v0.0.0-20210330151455-f540d88dde4e
)

Expand Down Expand Up @@ -84,6 +86,8 @@ require (
github.com/go-chi/chi/v5 v5.0.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
Expand Down Expand Up @@ -127,6 +131,7 @@ require (
github.com/mattn/go-runewidth v0.0.12 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/tsdb v0.7.1 // indirect
Expand All @@ -138,6 +143,8 @@ require (
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
github.com/tevino/abool v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
Expand Down
13 changes: 13 additions & 0 deletions client/cmd/dexc-desktop/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ decred.org/cspp/v2 v2.0.0/go.mod h1:0shJWKTWY3LxZEWGxtbER1Y45+HVjC0WZtj4bctSzCI=
decred.org/dcrwallet/v2 v2.0.8 h1:ps0hU8SO2qnCjl4FxQxri8mO8MBWH4NRNW42hSN3E6o=
decred.org/dcrwallet/v2 v2.0.8/go.mod h1:DEt4isEGSqMiMvo4scTX58oepPIwhTnaMCyTVPxCbzY=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 h1:V2IC9t0Zj9Ur6qDbfhUuzVmIvXKFyxZXRJyigUvovs4=
fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
Expand Down Expand Up @@ -422,6 +423,8 @@ github.com/gcash/neutrino v0.0.0-20210524083541-2900eef48821/go.mod h1:ud/wsgR45
github.com/gcash/neutrino v0.0.0-20210524105223-4cec86bbd8a4/go.mod h1:YBR6T+ZT02eR1S7JGqJ2gVPxZlfjWswTCXB4HZafp/U=
github.com/gcash/neutrino v0.0.0-20210524114821-3b1878290cf9 h1:V5UNzi/5pZxE5s6kfCe59VjJRmfkyI+npZizMcAvEdI=
github.com/gcash/neutrino v0.0.0-20210524114821-3b1878290cf9/go.mod h1:MshBO/Xf8SCndZFetZ8yg79db/JghnOiMmPiY1Eatlw=
github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6 h1:jFEK/SA/7E8lg9T33+y8D4Z0I782+bbiEjmyyklRzRQ=
github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6/go.mod h1:/WeFVhhxMOGypVKS0w8DUJxUBbHypnWkUVnW7p5c9Pw=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
Expand Down Expand Up @@ -465,6 +468,8 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE=
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
Expand All @@ -485,6 +490,8 @@ github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
Expand Down Expand Up @@ -940,6 +947,8 @@ github.com/nishanths/exhaustive v0.1.0/go.mod h1:S1j9110vxV1ECdCudXRkeMnFQ/DQk9a
github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ=
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
Expand Down Expand Up @@ -1165,11 +1174,14 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
github.com/tetafro/godot v1.4.6/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8=
github.com/tetafro/godot v1.4.7/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8=
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
Expand Down Expand Up @@ -1518,6 +1530,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
149 changes: 134 additions & 15 deletions client/cmd/dexc-desktop/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,17 @@ differences that make this version more suitable for less tech-savvy users.
| there are active orders. | active orders. Run a little server that |
| | synchronizes at start-up, enabling the |
| | window to be reopened when the user |
| | tries to start another instance. |
| | tries to start another instance. A |
| | desktop notification is sent and the |
| | system tray icon remains in the tray. |
|-----------------------------------|------------------------------------------|
Both versions use the same default client configuration file locations at
AppDataDir("dexc").
Since the program continues running in the background if there are active
orders, there becomes a question of how and when to shutdown, or what
happens when the user simply shuts off their computer, or it automatically restarts
after updating.
orders, there becomes a question of how and when to shutdown, or what happens
when the user simply shuts off their computer.
1) If there are no active orders when the user closes the window, dexc will
exit immediately.
2) If we receive a SIGTERM signal, expected for system shutdown, shut down
Expand All @@ -42,17 +43,6 @@ after updating.
We check every minute while the window is closed.
4) The user can kill the background program with a command-line argument,
--kill, which uses the sync server in the background to issue the command.
DRAFT NOTES:
Should we show a system-tray icon when running in the background?
I (Buck) think we should offer a way for the user to run dexc as a system
service (under the user, runs at login). The service would start with no window,
but the UX would be unaffected, other than always being synced. This would
expand our options for solving the problem of securing refunds through reboots.
For UTXO based assets, we can send refund txs without user login. For EVM, we
likely can't, because the nonce is probably no good.
*/

package main
Expand Down Expand Up @@ -103,6 +93,7 @@ int display_height() {
import "C"
import (
"context"
_ "embed"
"errors"
"fmt"
"os"
Expand All @@ -129,13 +120,17 @@ import (
"decred.org/dcrdex/client/webserver"
"decred.org/dcrdex/dex"
"github.com/webview/webview"

"fyne.io/systray"
"github.com/gen2brain/beeep"
)

const appName = "dexc"

var (
log dex.Logger
exePath = findExePath()
srcDir = filepath.Join(filepath.Dir(exePath), "src")
)

func main() {
Expand Down Expand Up @@ -301,6 +296,16 @@ func mainCore() error {
}()
}

wg.Add(2)
go func() {
defer wg.Done()
systray.Run(func() {
systrayOnReady(appCtx, filepath.Dir(cfg.LogPath), openC, killChan)
}, func() {
wg.Done()
})
}()

openWindow := func() {
wg.Add(1)
go func() {
Expand All @@ -313,6 +318,7 @@ func mainCore() error {

windowloop:
for {
var backgroundNoteSent bool
select {
case <-windowManager.zeroLeft:
logout:
Expand All @@ -327,6 +333,10 @@ windowloop:
log.Errorf("Core logout error: %v", err)
break windowloop
}
if !backgroundNoteSent {
sendDesktopNotification("DEX client still running", "DEX client is still resolving active DEX orders")
backgroundNoteSent = true
}
// Can't log out. Keep checking until either
// 1. We can log out. Exit the program.
// 2. The user reopens the window (via syncserver).
Expand Down Expand Up @@ -433,3 +443,112 @@ func findExePath() string {
}
return s
}

//go:embed src/dexc.png
var FavIcon []byte

//go:embed src/symbol-bw-round.png
var SymbolBWIcon []byte

func systrayOnReady(ctx context.Context, logDirectory string, openC chan<- struct{}, killC chan<- os.Signal) {
go func() {
<-ctx.Done()
systray.SetTooltip("Shutting down. Please wait...")
systray.Quit()
}()

systray.SetIcon(FavIcon)
systray.SetTitle("DEX client")
systray.SetTooltip("Self-custodial multi-wallet")

// TODO: Consider reworking main so we can show the icon earlier?
// mStarting := systray.AddMenuItem("Starting...", "Starting up. Please wait...")
// var addr string
// var ok bool
// select {
// case addr, ok = <-webserverReady:
// if !ok { // no webserver started
// fmt.Fprintln(os.Stderr, "Web server required!")
// cancel()
// return
// }
// case <-mainDone:
// return
// }

// mStarting.Hide()

mOpen := systray.AddMenuItem("Open", "Open DEX client window")
mOpen.SetIcon(SymbolBWIcon)
go func() {
for range mOpen.ClickedCh {
select {
case openC <- struct{}{}:
log.Info("Window opened")
default:
log.Infof("Ignored a window open request from the system tray")
}
}
}()

systray.AddSeparator()

if logDirURL, err := filePathToURL(logDirectory); err != nil {
log.Errorf("error constructing log directory URL: %v", err)
} else {
mLogs := systray.AddMenuItem("Open logs folder", "Open the folder with your DEX logs.")
go func() {
for range mLogs.ClickedCh {
runWebviewSubprocess(ctx, logDirURL)
}
}()
}

// TODO: Allow editing of configuration? What happens when they screw it up
// and make startup impossible?
// if cfgPathURL, err := filePathToURL(cfgPath); err != nil {
// fmt.Fprintln(os.Stderr, err)
// } else {
// mConfigFile := systray.AddMenuItem("Edit config file", "Open the config file in a text editor.")
// go func() {
// for range mConfigFile.ClickedCh {
// if _, err := os.Stat(cfgPath); err != nil {
// if os.IsNotExist(err) {
// fid, err := os.Create(cfgPath)
// if err != nil {
// fmt.Fprintf(os.Stderr, "failed to create new config file: %v", err)
// continue
// }
// fid.Close()
// }
// }
// err := browser.OpenURL(cfgPathURL)
// if err != nil {
// fmt.Fprintln(os.Stderr, err)
// }
// }
// }()
// }

// TODO: Enable toggling automatic startup on boot. This would be part of a
// larger effort aimed at securing refunds through system restarts.
// https://github.com/decred/dcrdex/pull/1957#discussion_r1020780014

systray.AddSeparator()

mQuit := systray.AddMenuItem("Force Quit", "Force DEX client to close.")
go func() {
<-mQuit.ClickedCh
mOpen.Disable()
mQuit.Disable()
killC <- os.Interrupt
}()
}

func sendDesktopNotification(title, msg string) {
err := beeep.Notify(title, msg, filepath.Join(srcDir, "dexc.png"))
if err != nil {
log.Errorf("error sending desktop notification: %v", err)
return
}
}
Loading

0 comments on commit bb03538

Please sign in to comment.