diff --git a/CounterHandler.go b/CounterHandler.go new file mode 100644 index 0000000..da7ab23 --- /dev/null +++ b/CounterHandler.go @@ -0,0 +1,44 @@ +package main + +import ( + "github.com/fogleman/gg" + "github.com/unix-streamdeck/api" + "github.com/unix-streamdeck/driver" + "golang.org/x/image/font/inconsolata" + "strconv" +) + +type CounterIconHandler struct { + count int + running bool +} + +func (c *CounterIconHandler) Icon(page int, index int, key *api.Key, dev streamdeck.Device) { + if c.running { + img := gg.NewContext(72, 72) + img.SetRGB(0, 0, 0) + img.Clear() + img.SetRGB(1, 1, 1) + img.SetFontFace(inconsolata.Regular8x16) + count := strconv.Itoa(c.count) + img.DrawStringAnchored(count, 72/2, 72/2, 0.5, 0.5) + img.Clip() + SetImage(img.Image(), index, page, dev) + key.Buff = img.Image() + } +} + +func (c CounterIconHandler) Stop() { + c.running = false +} + +type CounterKeyHandler struct{} + +func (CounterKeyHandler) Key(page int, index int, key *api.Key, dev streamdeck.Device) { + if key.IconHandler != "Counter" { + return + } + handler := key.IconHandlerStruct.(*CounterIconHandler) + handler.count += 1 + handler.Icon(page, index, key, dev) +} diff --git a/GifHandler.go b/GifHandler.go new file mode 100644 index 0000000..820241c --- /dev/null +++ b/GifHandler.go @@ -0,0 +1,44 @@ +package main + +import ( + "github.com/unix-streamdeck/api" + "github.com/unix-streamdeck/driver" + "image/gif" + "log" + "os" + "time" +) + +type GifIconHandler struct { + running bool +} + +func (s *GifIconHandler) Icon(page int, index int, key *api.Key, dev streamdeck.Device) { + s.running = true + f, err := os.Open(key.Icon) + if err != nil { + log.Println(err) + return + } + gifs, err := gif.DecodeAll(f) + timeDelay := gifs.Delay[0] + gifIndex := 0 + go loop(gifs, gifIndex, timeDelay, page, index, dev, key, s) +} + +func (s *GifIconHandler) Stop() { + s.running = false +} + +func loop(gifs *gif.GIF, gifIndex int, timeDelay int, page int, index int, dev streamdeck.Device, key *api.Key, s *GifIconHandler) { + for s.running { + img := ResizeImage(gifs.Image[gifIndex]) + SetImage(img, index, page, dev) + key.Buff = img + gifIndex++ + if gifIndex >= len(gifs.Image) { + gifIndex = 0 + } + time.Sleep(time.Duration(timeDelay * 10000000)) + } +} \ No newline at end of file diff --git a/README.md b/README.md index e10cafb..b365e90 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,29 @@ The actions you can have on a button are: - `url`: opens a url in your default browser via xdg - `brightness`: set the brightness of the streamdeck as a percentage - `switch_page`: change the active page on the streamdeck + + +### D-Bus + +There is a D-Bus interface built into the daemon, the service name and interface for D-Bus are `com.thejonsey.streamdeck` and `com/thejonsey/streamdeck` respectively, and is made up of the following methods/signals + +#### Methods + +- GetConfig - returns the current running config +- SetConfig - sets the config, without saving to disk, takes in Stringified json, returns an error if anything breaks +- ReloadConfig - reloads the config from disk +- GetDeckInfo - Returns information about the active streamdeck in the format of +```json +{ + "icon_size": 72, + "rows": 3, + "cols": 5, + "page": 0 +} +``` +- SetPage - Set the page on the streamdeck to the number passed to it, returns an error if anything breaks +- CommitConfig - Commits the currently active config to disk, returns an error if anything breaks + +#### Signals + +- Page - sends the number of the page switched to on the StreamDeck diff --git a/TimeHandler.go b/TimeHandler.go new file mode 100644 index 0000000..84d91bd --- /dev/null +++ b/TimeHandler.go @@ -0,0 +1,48 @@ +package main + +import ( + "github.com/fogleman/gg" + "github.com/unix-streamdeck/api" + "github.com/unix-streamdeck/driver" + "golang.org/x/image/font/inconsolata" + "strconv" + "time" +) + +type TimeIconHandler struct{ + running bool +} + +func (t *TimeIconHandler) Icon(page int, index int, key *api.Key, dev streamdeck.Device) { + t.running = true + go timeLoop(page, index, dev, key, t) +} + +func (t *TimeIconHandler) Stop() { + t.running = false +} + +func timeLoop(page int, index int, dev streamdeck.Device, key *api.Key, handler *TimeIconHandler) { + for handler.running { + img := gg.NewContext(72, 72) + img.SetRGB(0, 0, 0) + img.Clear() + img.SetRGB(1, 1, 1) + img.SetFontFace(inconsolata.Regular8x16) + t := time.Now() + tString := t.Format("15:04:05") + img.DrawStringAnchored(tString, 72/2, 72/2, 0.5, 0.5) + img.Clip() + SetImage(img.Image(), index, page, dev) + key.Buff = img.Image() + time.Sleep(time.Second) + } +} + +func zeroes(i int) (string) { + if i < 10 { + return "0" + strconv.Itoa(i) + } else { + return strconv.Itoa(i) + } +} \ No newline at end of file diff --git a/config.go b/config.go deleted file mode 100644 index d2b9b7c..0000000 --- a/config.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import "image" - -type Page []Key - -type Config struct { - Pages []Page `json:"pages"` -} - -type Key struct { - Icon string `json:"icon,omitempty"` - SwitchPage *int `json:"switch_page,omitempty"` - Text string `json:"text,omitempty"` - Keybind string `json:"keybind,omitempty"` - Command string `json:"command,omitempty"` - Brightness *int `json:"brightness,omitempty"` - Url string `json:"url,omitempty"` - buff image.Image -} diff --git a/dbus.go b/dbus.go new file mode 100644 index 0000000..4efccf6 --- /dev/null +++ b/dbus.go @@ -0,0 +1,102 @@ +package main + +import ( + "encoding/json" + "errors" + "github.com/godbus/dbus/v5" + "log" +) + + +var conn *dbus.Conn + +var s *StreamDeckDBus + +type StreamDeckDBus struct { + Cols int `json:"cols,omitempty"` + Rows int `json:"rows,omitempty"` + IconSize int `json:"icon_size,omitempty"` + Page int `json:"page"` +} + +func (s StreamDeckDBus) GetDeckInfo() (string, *dbus.Error) { + infoString, err := json.Marshal(s) + if err != nil { + return "", dbus.MakeFailedError(err) + } + return string(infoString), nil +} + +func (StreamDeckDBus) GetConfig() (string, *dbus.Error) { + configString, err := json.Marshal(config) + if err != nil { + return "", dbus.MakeFailedError(err) + } + return string(configString), nil +} + +func (StreamDeckDBus) ReloadConfig() *dbus.Error { + err := ReloadConfig() + if err != nil { + return dbus.MakeFailedError(err) + } + return nil +} + +func (StreamDeckDBus) SetPage(page int) *dbus.Error { + SetPage(config, page, dev) + return nil +} + +func (StreamDeckDBus) SetConfig(configString string) *dbus.Error { + err := SetConfig(configString) + if err != nil { + return dbus.MakeFailedError(err) + } + return nil +} + +func (StreamDeckDBus) CommitConfig() *dbus.Error { + err := SaveConfig() + if err != nil { + return dbus.MakeFailedError(err) + } + return nil +} + +func InitDBUS() error { + var err error + conn, err = dbus.SessionBus() + if err != nil { + log.Println(err) + return err + } + defer conn.Close() + + s = &StreamDeckDBus{ + Cols: int(dev.Columns), + Rows: int(dev.Rows), + IconSize: int(dev.Pixels), + Page: p, + } + conn.ExportAll(s, "/com/unixstreamdeck/streamdeckd", "com.unixstreamdeck.streamdeckd") + reply, err := conn.RequestName("com.unixstreamdeck.streamdeckd", + dbus.NameFlagDoNotQueue) + if err != nil { + log.Println(err) + return err + } + if reply != dbus.RequestNameReplyPrimaryOwner { + return errors.New("DBus: Name already taken") + } + select {} +} + +func EmitPage(page int) { + if conn != nil { + conn.Emit("/com/unixstreamdeck/streamdeckd", "com.unixstreamdeck.streamdeckd.Page", page) + } + if s != nil { + s.Page = page + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index 0f9396c..0d6bdc3 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,13 @@ -module streamdeckd +module github.com/unix-streamdeck/streamdeckd go 1.14 require ( github.com/fogleman/gg v1.3.0 + github.com/godbus/dbus/v5 v5.0.4-0.20200513180336-df5ef3eb7cca github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 - github.com/unix-streamdeck/streamdeck v0.0.0-20200727161137-d2d416a422d2 + github.com/unix-streamdeck/api v0.0.0-20200818180846-6942d99617b2 + github.com/unix-streamdeck/driver v0.0.0-20200817173808-cdaf123c076b golang.org/x/image v0.0.0-20200119044424-58c23975cae1 ) diff --git a/go.sum b/go.sum index 8c34da6..c887392 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus/v5 v5.0.4-0.20200513180336-df5ef3eb7cca h1:ewc47M3S8MAZgSO1yEnPrbmHjtQz6caAhYWOQzPHBok= +github.com/godbus/dbus/v5 v5.0.4-0.20200513180336-df5ef3eb7cca/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= @@ -40,8 +42,6 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/h2non/bimg v1.1.2 h1:J75W2eM5FT0KjcwsL2aiy1Ilu0Xy0ENb0sU+HHUJAvw= -github.com/h2non/bimg v1.1.2/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -91,12 +91,10 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ungerik/go-cairo v0.0.0-20191014050614-4a03f432a432 h1:luu+HbKrZKlIjiaclwrldreEEvVPYX/ujRbTGkKz61c= -github.com/ungerik/go-cairo v0.0.0-20191014050614-4a03f432a432/go.mod h1:0ErpLiOxxE1oY+R4stiKut6/DbUJHnOp6U+e4d8zcTs= -github.com/unix-streamdeck/streamdeck v0.0.0-20200727161137-d2d416a422d2 h1:rLVbYju4iaW8jP64Pafffu9T0wXitOOtu3+qCxzGV1c= -github.com/unix-streamdeck/streamdeck v0.0.0-20200727161137-d2d416a422d2/go.mod h1:Tm8jArLxpQy5vGBbHSreHpNRAIYtDV3J3U3UMGRWTXc= -github.com/unix-streamdeck/streamdeck-lib v0.0.0-20200720132322-072961fa1fdd h1:oX6jtZ+/gBcryYn49alkMPbCzqqwyo/7VA17mA4khrQ= -github.com/unix-streamdeck/streamdeck-lib v0.0.0-20200720132322-072961fa1fdd/go.mod h1:EPLAk+jRYZ0zGW7S7bJctr3CLc4bc+tgBrJN7+G1bvc= +github.com/unix-streamdeck/api v0.0.0-20200818180846-6942d99617b2 h1:du+OoTUOp1mf/VRdQ8xQMWdR6JrOGbZvDG1nb9BBxHM= +github.com/unix-streamdeck/api v0.0.0-20200818180846-6942d99617b2/go.mod h1:rweAXRgCWdCACCuVhmleidq7HnJEO38zBGDe8uQqZ0w= +github.com/unix-streamdeck/driver v0.0.0-20200817173808-cdaf123c076b h1:27gVti9+OevmBC2BnWlKC0dQ0eiIHh7PvYTWxt4vb6A= +github.com/unix-streamdeck/driver v0.0.0-20200817173808-cdaf123c076b/go.mod h1:i3Eg6kJBslgUk2VIPJ3Cclta2fpV1KJrOnOnR8gnVKY= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= diff --git a/interface.go b/interface.go new file mode 100644 index 0000000..893eacf --- /dev/null +++ b/interface.go @@ -0,0 +1,153 @@ +package main + +import ( + "github.com/fogleman/gg" + "github.com/nfnt/resize" + "github.com/unix-streamdeck/api" + "github.com/unix-streamdeck/driver" + "golang.org/x/image/font/inconsolata" + "image" + "image/color" + "image/draw" + "log" + "os" +) + +var p int + +func LoadImage(path string) (image.Image, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + img, _, err := image.Decode(f) + if err != nil { + return nil, err + } + + return ResizeImage(img), nil +} + +func ResizeImage(img image.Image) image.Image { + return resize.Resize(dev.Pixels, dev.Pixels, img, resize.Lanczos3) +} + +func SetImage(img image.Image, i int, page int, dev streamdeck.Device) { + if p == page { + dev.SetImage(uint8(i), img) + } +} + +func SetKey(currentKey *api.Key, i int) { + if currentKey.Buff == nil { + if currentKey.Icon == "" { + img := image.NewRGBA(image.Rect(0, 0, int(dev.Pixels), int(dev.Pixels))) + draw.Draw(img, img.Bounds(), image.NewUniform(color.RGBA{0, 0, 0, 255}), image.ZP, draw.Src) + currentKey.Buff = img + } else { + img, err := LoadImage(currentKey.Icon) + if err != nil { + log.Println(err) + } + currentKey.Buff = img + } + if currentKey.Text != "" { + img := gg.NewContextForImage(currentKey.Buff) + img.SetRGB(1, 1, 1) + img.SetFontFace(inconsolata.Regular8x16) + img.DrawStringAnchored(currentKey.Text, 72/2, 72/2, 0.5, 0.5) + img.Clip() + currentKey.Buff = img.Image() + } + } + SetImage(currentKey.Buff, i, p, dev) +} + +func SetPage(config *api.Config, page int, dev streamdeck.Device) { + p = page + currentPage := config.Pages[page] + for i := 0; i < len(currentPage); i++ { + currentKey := ¤tPage[i] + if currentKey.Buff == nil { + if currentKey.IconHandler == "" { + SetKey(currentKey, i) + + } else if currentKey.IconHandlerStruct == nil { + var handler api.IconHandler + if currentKey.IconHandler == "Gif" { + handler = &GifIconHandler{true} + } else if currentKey.IconHandler == "Counter" { + handler = &CounterIconHandler{0, true} + } else if currentKey.IconHandler == "Time" { + handler = &TimeIconHandler{true} + } + if handler == nil { + continue + } + handler.Icon(page, i, currentKey, dev) + currentKey.IconHandlerStruct = handler + } + } else { + SetImage(currentKey.Buff, i, p, dev) + } + } + EmitPage(p) +} + +func HandleInput(key *api.Key, page int, index int, dev streamdeck.Device) { + if key.Command != "" { + runCommand(key.Command) + } + if key.Keybind != "" { + runCommand("xdotool key " + key.Keybind) + } + if key.SwitchPage != 0 { + page = key.SwitchPage - 1 + SetPage(config, page, dev) + } + if key.Brightness != 0 { + _ = dev.SetBrightness(uint8(key.Brightness)) + } + if key.Url != "" { + runCommand("xdg-open " + key.Url) + } + if key.KeyHandler != "" { + if key.KeyHandlerStruct == nil { + var handler api.KeyHandler + if key.KeyHandler == "Counter" { + handler = CounterKeyHandler{} + } + if handler == nil { + return + } + key.KeyHandlerStruct = handler + } + key.KeyHandlerStruct.Key(page, index, key, dev) + } +} + +func Listen() { + kch, err := dev.ReadKeys() + if err != nil { + log.Println(err) + } + for { + select { + case k, ok := <-kch: + if !ok { + err = dev.Open() + if err != nil { + log.Println(err) + } + continue + } + if k.Pressed == true { + if len(config.Pages)-1 >= p && len(config.Pages[p])-1 >= int(k.Index) { + HandleInput(&config.Pages[p][k.Index], p, int(k.Index), dev) + } + } + } + } +} diff --git a/main.go b/main.go index bdc5ba4..6e8a72d 100644 --- a/main.go +++ b/main.go @@ -2,13 +2,8 @@ package main import ( "encoding/json" - "github.com/fogleman/gg" - "github.com/nfnt/resize" - "github.com/unix-streamdeck/streamdeck" - "golang.org/x/image/font/inconsolata" - "image" - "image/color" - "image/draw" + "github.com/unix-streamdeck/driver" + "github.com/unix-streamdeck/api" _ "image/gif" _ "image/jpeg" _ "image/png" @@ -17,145 +12,81 @@ import ( "os" "os/exec" "os/signal" - "strings" "syscall" ) -var page = 0 var dev streamdeck.Device -var config Config +var config *api.Config +var configPath = os.Getenv("HOME") + "/.streamdeck-config.json" + +var basicConfig = api.Config{ + Pages: []api.Page{ + { + api.Key{ + }, + }, + }, +} func main() { d, err := streamdeck.Devices() if err != nil { - log.Fatal(err) + log.Println(err) } if len(d) == 0 { - log.Fatal("No Stream Deck devices found.") + log.Println("No Stream Deck devices found.") } dev = d[0] err = dev.Open() if err != nil { - log.Fatal(err) + log.Println(err) } config, err = readConfig() if err != nil && !os.IsNotExist(err) { - log.Fatal(err) + log.Println(err) + } else if os.IsNotExist(err) { + file, err := os.Create(configPath) + if err != nil { + log.Println(err) + } + err = file.Close() + if err != nil { + log.Println(err) + } + config = &basicConfig + err = SaveConfig() + if err != nil { + log.Println(err) + } } if len(config.Pages) == 0 { - config.Pages = append(config.Pages, Page{}) + config.Pages = append(config.Pages, api.Page{}) } cleanupHook() - setPage() - kch, err := dev.ReadKeys() - if err != nil { - log.Fatal(err) - } - for { - select { - case k, ok := <-kch: - if !ok { - err = dev.Open() - if err != nil { - log.Fatal(err) - } - continue - } - if k.Pressed == true { - if len(config.Pages)-1 >= page && len(config.Pages[page])-1 >= int(k.Index) { - handleInput(config.Pages[page][k.Index]) - } - } - } - } + SetPage(config, 0, dev) + go InitDBUS() + Listen() } -func readConfig() (Config, error) { - data, err := ioutil.ReadFile(os.Getenv("HOME") + "/.streamdeck-config.json") +func readConfig() (*api.Config, error) { + data, err := ioutil.ReadFile(configPath) if err != nil { - return Config{}, err + return &api.Config{}, err } - var config Config + var config api.Config err = json.Unmarshal(data, &config) if err != nil { - return Config{}, err - } - return config, nil -} - -func setImage(img image.Image, i int, p int) { - if p == page { - dev.SetImage(uint8(i), img) + return &api.Config{}, err } + return &config, nil } -func setPage() { - currentPage := config.Pages[page] - for i, currentKey := range currentPage { - if currentKey.buff == nil { - if currentKey.Icon == "" { - img := image.NewRGBA(image.Rect(0, 0, int(dev.Pixels), int(dev.Pixels))) - draw.Draw(img, img.Bounds(), image.NewUniform(color.RGBA{0, 0, 0, 255}), image.ZP, draw.Src) - currentKey.buff = img - } else { - img, err := loadImage(currentKey.Icon) - if err != nil { - log.Fatal(err) - } - currentKey.buff = img - } - if currentKey.Text != "" { - img := gg.NewContextForImage(currentKey.buff) - img.SetRGB(1, 1, 1) - img.SetFontFace(inconsolata.Regular8x16) - img.DrawStringAnchored(currentKey.Text, 72/2, 72/2, 0.5, 0.5) - img.Clip() - currentKey.buff = img.Image() - } - } - setImage(currentKey.buff, i, page) - } -} - -func loadImage(path string) (image.Image, error) { - f, err := os.Open(path) - if err != nil { - return nil, err - } - defer f.Close() - - img, _, err := image.Decode(f) - if err != nil { - return nil, err - } - - return resize.Resize(72, 72, img, resize.Lanczos3), nil -} - -func handleInput(key Key) { - if key.Command != "" { - runCommand(key.Command) - } - if key.Keybind != "" { - runCommand("xdotool key " + key.Keybind) - } - if key.SwitchPage != nil { - page = (*key.SwitchPage) - 1 - setPage() - } - if key.Brightness != nil { - _ = dev.SetBrightness(uint8(*key.Brightness)) - } - if key.Url != "" { - runCommand("xdg-open " + key.Url) - } -} func runCommand(command string) { - args := strings.Split(command, " ") - c := exec.Command(args[0], args[1:]...) + //args := strings.Split(command, " ") + c := exec.Command("/bin/sh", "-c", command) if err := c.Start(); err != nil { - panic(err) + log.Println(err) } err := c.Wait() if err != nil { @@ -172,3 +103,66 @@ func cleanupHook() { os.Exit(0) }() } + +func SetConfig(configString string) error { + unmountHandlers() + var err error + config = nil + err = json.Unmarshal([]byte(configString), &config) + if err != nil { + return err + } + if len(config.Pages) == 0 { + config.Pages = append(config.Pages, api.Page{}) + } + SetPage(config, p, dev) + return nil +} + +func ReloadConfig() error { + unmountHandlers() + var err error + config, err = readConfig() + if err != nil && !os.IsNotExist(err) { + return err + } + if len(config.Pages) == 0 { + config.Pages = append(config.Pages, api.Page{}) + } + SetPage(config, p, dev) + return nil +} + +func SaveConfig() error { + f, err := os.OpenFile(configPath, os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + return err + } + defer f.Close() + var configString []byte + configString, err = json.Marshal(config) + if err != nil { + return err + } + _, err = f.Write(configString) + if err != nil { + return err + } + err = f.Sync() + if err != nil { + return err + } + return nil +} + +func unmountHandlers() { + for i := range config.Pages { + page := config.Pages[i] + for i2 := range page { + key := page[i2] + if key.IconHandlerStruct != nil { + key.IconHandlerStruct.Stop() + } + } + } +} \ No newline at end of file