Sasha Koshka
02a27447b9
In X, clipboard/selection data is specific to each window, and it may take some time before the clipboard data is fully transferred. This actually makes sense because there can be entire images in the clipboard and it is important the clipboard API supports large file transfer. Because of this, the Copy and Paste methods have been moved into Window, and Paste now returns a channel.
116 lines
2.9 KiB
Go
116 lines
2.9 KiB
Go
package tomo
|
|
|
|
import "os"
|
|
import "io"
|
|
import "path/filepath"
|
|
import "git.tebibyte.media/sashakoshka/tomo/dirs"
|
|
import "git.tebibyte.media/sashakoshka/tomo/theme"
|
|
import "git.tebibyte.media/sashakoshka/tomo/config"
|
|
import "git.tebibyte.media/sashakoshka/tomo/elements"
|
|
|
|
var backend Backend
|
|
|
|
// Run initializes a backend, calls the callback function, and begins the event
|
|
// loop in that order. This function does not return until Stop() is called, or
|
|
// the backend experiences a fatal error.
|
|
func Run (callback func ()) (err error) {
|
|
backend, err = instantiateBackend()
|
|
if err != nil { return }
|
|
config := parseConfig()
|
|
backend.SetConfig(config)
|
|
backend.SetTheme(parseTheme(config.ThemePath()))
|
|
if callback != nil { callback() }
|
|
err = backend.Run()
|
|
backend = nil
|
|
return
|
|
}
|
|
|
|
// Stop gracefully stops the event loop and shuts the backend down. Call this
|
|
// before closing your application.
|
|
func Stop () {
|
|
if backend != nil { backend.Stop() }
|
|
}
|
|
|
|
// Do executes the specified callback within the main thread as soon as
|
|
// possible. This function can be safely called from other threads.
|
|
func Do (callback func ()) {
|
|
assertBackend()
|
|
backend.Do(callback)
|
|
}
|
|
|
|
// NewWindow creates a new window using the current backend, and returns it as a
|
|
// MainWindow. If the window could not be created, an error is returned
|
|
// explaining why.
|
|
func NewWindow (width, height int) (window elements.MainWindow, err error) {
|
|
assertBackend()
|
|
return backend.NewWindow(width, height)
|
|
}
|
|
|
|
// SetTheme sets the theme of all open windows.
|
|
func SetTheme (theme theme.Theme) {
|
|
backend.SetTheme(theme)
|
|
}
|
|
|
|
// SetConfig sets the configuration of all open windows.
|
|
func SetConfig (config config.Config) {
|
|
backend.SetConfig(config)
|
|
}
|
|
|
|
func parseConfig () (config.Config) {
|
|
return parseMany [config.Config] (
|
|
dirs.ConfigDirs("tomo/tomo.conf"),
|
|
config.Parse,
|
|
config.Default { })
|
|
}
|
|
|
|
func parseTheme (path string) (theme.Theme) {
|
|
if path == "" { return theme.Default { } }
|
|
path = filepath.Join(path, "tomo")
|
|
|
|
// find all tomo pattern graph files in the directory
|
|
directory, err := os.Open(path)
|
|
if err != nil { return theme.Default { } }
|
|
names, _ := directory.Readdirnames(0)
|
|
paths := []string { }
|
|
for _, name := range names {
|
|
if filepath.Ext(name) == ".tpg" {
|
|
paths = append(paths, filepath.Join(path, name))
|
|
}
|
|
}
|
|
|
|
// parse them
|
|
return parseMany [theme.Theme] (
|
|
paths,
|
|
theme.Parse,
|
|
theme.Default { })
|
|
}
|
|
|
|
func parseMany [OBJECT any] (
|
|
paths []string,
|
|
parser func (...io.Reader) OBJECT,
|
|
fallback OBJECT,
|
|
) (
|
|
object OBJECT,
|
|
) {
|
|
// convert all paths into readers
|
|
sources := []io.Reader { }
|
|
for _, path := range paths {
|
|
file, err := os.Open(path)
|
|
if err != nil { continue }
|
|
sources = append(sources, file)
|
|
defer file.Close()
|
|
}
|
|
|
|
if sources == nil {
|
|
// if there are no readers, return the fallback object
|
|
return fallback
|
|
} else {
|
|
// if there are readers, parse them
|
|
return parser(sources...)
|
|
}
|
|
}
|
|
|
|
func assertBackend () {
|
|
if backend == nil { panic("no backend is running") }
|
|
}
|