Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Ignore binaries
cmd/tyde/tyde
cmd/tyde_ctl/tyde_ctl
cmd/tyde_runner/tyde_runner
tyde
tyde_ctl
tyde_runner

.idea
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ PREFIX ?= /usr$(LOCAL)

build:
go build ./cmd/tyde_runner
go build ./cmd/tyde_ctl
go build ./cmd/tyde

install:
install -Dm00755 tyde_runner $(DESTDIR)$(PREFIX)/bin/tyde_runner
install -Dm00755 tyde_runner $(DESTDIR)$(PREFIX)/bin/tyde_ctl
install -Dm00755 tyde $(DESTDIR)$(PREFIX)/bin/tyde
install -Dm00644 tyde.desktop $(DESTDIR)$(PREFIX)/share/xsessions/tyde.desktop

uninstall:
-rm $(DESTDIR)$(PREFIX)/bin/tyde_runner
-rm $(DESTDIR)$(PREFIX)/bin/tyde_ctl
-rm $(DESTDIR)$(PREFIX)/bin/tyde
-rm $(DESTDIR)$(PREFIX)/share/xsessions/tyde.desktop

Expand Down
1 change: 1 addition & 0 deletions cmd/tyde/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
_ "fyshos.com/tyde/modules/fyles"
_ "fyshos.com/tyde/modules/launcher"
_ "fyshos.com/tyde/modules/quaketerm"
_ "fyshos.com/tyde/modules/rpc"
_ "fyshos.com/tyde/modules/status"
_ "fyshos.com/tyde/modules/systray"
wmtheme "fyshos.com/tyde/theme"
Expand Down
83 changes: 83 additions & 0 deletions cmd/tyde_ctl/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package main

import (
"fmt"
"net/rpc"
"os"
"strings"

frpc "fyshos.com/tyde/modules/rpc"
)

func main() {
if len(os.Args) < 2 || os.Args[1] == "-h" || os.Args[1] == "--help" || os.Args[1] == "help" {
printHelp()
return
}

client, err := rpc.Dial("unix", frpc.SocketPath())
if err != nil {
fmt.Fprintln(os.Stderr, "failed to connect to fynedesk:", err)
os.Exit(1)
}
defer client.Close()

if os.Args[1] == "list" {
var modules []string
if err := client.Call("Service.ListModules", "", &modules); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println("Available suggestion modules:")
for _, m := range modules {
fmt.Println(" -", m)
}
return
}

input := strings.Join(os.Args[1:], " ")
var reply string
if err := client.Call("Service.Launch", input, &reply); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(reply)
}

func printHelp() {
fmt.Print(`fynedesk_ctl - command line interface for FyneDesk

Usage:
fynedesk_ctl <command> [args...]
fynedesk_ctl list
fynedesk_ctl help

Commands are passed to FyneDesk's launch suggestion modules, the same
way text typed into the app launcher is processed.

Examples:
fynedesk_ctl brightness up Increase screen brightness
fynedesk_ctl brightness 50 Set brightness to 50%
fynedesk_ctl big Hello World Show "Hello World" in large type
fynedesk_ctl 2+2 Evaluate expression

Built-in commands:
list Show loaded suggestion modules
help Show this help message
`)

client, err := rpc.Dial("unix", frpc.SocketPath())
if err != nil {
return
}
defer client.Close()

var modules []string
if err := client.Call("Service.ListModules", "", &modules); err != nil {
return
}
fmt.Println("Loaded modules:")
for _, m := range modules {
fmt.Println(" -", m)
}
}
7 changes: 7 additions & 0 deletions modules/rpc/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package rpc

import "fyshos.com/tyde"

func init() {
tyde.RegisterModule(rpcMeta)
}
141 changes: 141 additions & 0 deletions modules/rpc/rpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package rpc

import (
"errors"
"fmt"
"net"
"net/rpc"
"os"
"path/filepath"
"strconv"
"strings"

"fyne.io/fyne/v2"

"fyshos.com/tyde"
)

// LaunchInput passes the input string through each LaunchSuggestionModule
// and executes the first match. Search module is ignored in this context.
func LaunchInput(input string) (string, error) {
desk := tyde.Instance()
if desk == nil {
return "", errors.New("desktop not running")
}

for _, m := range desk.Modules() {
suggest, ok := m.(tyde.LaunchSuggestionModule)
if !ok {
continue
}
items := suggest.LaunchSuggestions(input)
if len(items) == 0 {
continue
}

// Search module is a catch-all fallbacks which we don't want.
if strings.Contains(strings.ToLower(m.Metadata().Name), "search") {
continue
}

item := items[0]
done := make(chan struct{})
fyne.Do(func() {
item.Launch()
close(done)
})
<-done
return item.Title(), nil
}

return "", fmt.Errorf("no match for %q", input)
}

var rpcMeta = tyde.ModuleMetadata{
Name: "RPC",
NewInstance: newRPC,
}

// SocketPath returns the Unix socket path used for RPC communication.
func SocketPath() string {
dir := os.Getenv("XDG_RUNTIME_DIR")
if dir != "" {
return filepath.Join(dir, "fynedesk.sock")
}
return "/tmp/fynedesk-" + strconv.Itoa(os.Getuid()) + ".sock"
}

type rpcModule struct {
listener net.Listener
}

func (r *rpcModule) Metadata() tyde.ModuleMetadata {
return rpcMeta
}

func (r *rpcModule) Destroy() {
if r.listener != nil {
r.listener.Close()
}
os.Remove(SocketPath())
}

// Service is the RPC service exposed over the Unix socket.
type Service struct{}

// Launch passes the input string through the launch suggestion modules
// and executes the first match.
func (s *Service) Launch(input string, reply *string) error {
title, err := LaunchInput(input)
if err != nil {
return err
}
*reply = title
return nil
}

// ListModules returns the names of loaded LaunchSuggestionModules.
func (s *Service) ListModules(_ string, reply *[]string) error {
desk := tyde.Instance()
if desk == nil {
return errors.New("desktop not running")
}

var names []string
for _, m := range desk.Modules() {
if _, ok := m.(tyde.LaunchSuggestionModule); !ok {
continue
}
name := m.Metadata().Name
name = strings.TrimPrefix(name, "Launcher: ")
names = append(names, name)
}
*reply = names
return nil
}

func newRPC() tyde.Module {
sock := SocketPath()
os.Remove(sock) // clean up stale socket

srv := rpc.NewServer()
srv.Register(&Service{})

ln, err := net.Listen("unix", sock)
if err != nil {
fyne.LogError("RPC: failed to listen on "+sock, err)
return &rpcModule{}
}

go func() {
for {
conn, err := ln.Accept()
if err != nil {
return // listener closed
}
go srv.ServeConn(conn)
}
}()

return &rpcModule{listener: ln}
}
Loading
Loading