16 Commits

Author SHA1 Message Date
Piotr Miller
223e16f77c Merge pull request #106 from nwg-piotr/otput-hypr
Make binding to an output work on Hyprland
2023-11-26 23:40:28 +01:00
piotr
55c095c053 fix -wm help 2023-11-26 23:34:48 +01:00
piotr
ee2008348f bump to 0.4.3 2023-11-26 23:17:00 +01:00
piotr
0570f90bec update help 2023-11-26 23:16:39 +01:00
piotr
a2917d68dd support output assignment on Hyprland 2023-11-26 23:14:18 +01:00
piotr
b05b0f484a improve help 2023-11-25 15:54:05 +01:00
piotr
b61e665c71 bump to 0.4.2 2023-11-22 01:23:59 +01:00
piotr
8408c669ef remove unused function 2023-11-22 01:13:48 +01:00
Piotr Miller
d66066c3b6 Merge pull request #105 from nwg-piotr/fix-wm
remove `XDG_CURRENT_DESKTOP` as default `-wm` value; allow capitalized `Hyprland`
2023-11-21 14:53:22 +01:00
piotr
dac55cdd0d remove setting XDG_CURRENT_DESKTOP as dafault -wm value; allow capitalized Hyprland value 2023-11-21 14:26:04 +01:00
Piotr Miller
03365d24e5 Merge pull request #104 from Schuldkroete/main
Make the description line at the bottom of the drawer themable
2023-11-21 14:20:22 +01:00
Schuldkröte
8e58654336 add gtk name property to statusLineWrapper and statusLabel 2023-11-21 11:47:01 +01:00
piotr
33e0032725 bump to 0.4.1 2023-11-18 01:41:53 +01:00
Piotr Miller
c05fff189d Merge pull request #103 from gouvinb/main
Change command execution logic and use `/usr/bin/env` command
2023-11-18 01:40:09 +01:00
gouvinb
57d130ca51 Restore the code to trim % and everything that follows 2023-11-09 15:49:50 +01:00
gouvinb
954a6ba9c9 change command execution logic and use env command 2023-11-09 04:24:41 +01:00
2 changed files with 147 additions and 94 deletions

60
main.go
View File

@@ -21,16 +21,17 @@ import (
"github.com/gotk3/gotk3/gtk" "github.com/gotk3/gotk3/gtk"
) )
const version = "0.4.0" const version = "0.4.3"
var ( var (
appDirs []string appDirs []string
configDirectory string configDirectory string
pinnedFile string pinnedFile string
pinned []string pinned []string
id2entry map[string]desktopEntry id2entry map[string]desktopEntry
preferredApps map[string]interface{} preferredApps map[string]interface{}
exclusions []string exclusions []string
hyprlandMonitors []monitor
) )
var categoryNames = [...]string{ var categoryNames = [...]string{
@@ -66,6 +67,30 @@ type desktopEntry struct {
NoDisplay bool NoDisplay bool
} }
type monitor struct {
Id int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Make string `json:"make"`
Model string `json:"model"`
Serial string `json:"serial"`
Width int `json:"width"`
Height int `json:"height"`
RefreshRate float64 `json:"refreshRate"`
X int `json:"x"`
Y int `json:"y"`
ActiveWorkspace struct {
Id int `json:"id"`
Name string `json:"name"`
} `json:"activeWorkspace"`
Reserved []int `json:"reserved"`
Scale float64 `json:"scale"`
Transform int `json:"transform"`
Focused bool `json:"focused"`
DpmsStatus bool `json:"dpmsStatus"`
Vrr bool `json:"vrr"`
}
// slices below will hold DesktopID strings // slices below will hold DesktopID strings
var ( var (
listUtility []string listUtility []string
@@ -114,24 +139,16 @@ func defaultTermIfBlank(s, fallback string) string {
return s return s
} }
func defaultStringIfBlank(s, fallback string) string {
s = strings.TrimSpace(s)
if s == "" {
return fallback
}
return s
}
func validateWm() { func validateWm() {
if !(*wm == "sway" || *wm == "hyprland") && *wm != "" { if !(*wm == "sway" || *wm == "hyprland" || *wm == "Hyprland") && *wm != "" {
*wm = "" *wm = ""
log.Warn("-wm argument supports only sway or hyprland string.") log.Warn("-wm argument supports only 'sway' or 'hyprland' string.")
} }
} }
// Flags // Flags
var cssFileName = flag.String("s", "drawer.css", "Styling: css file name") var cssFileName = flag.String("s", "drawer.css", "Styling: css file name")
var targetOutput = flag.String("o", "", "name of the Output to display the drawer on (sway only)") var targetOutput = flag.String("o", "", "name of the Output to display the drawer on (sway & Hyprland only)")
var displayVersion = flag.Bool("v", false, "display Version information") var displayVersion = flag.Bool("v", false, "display Version information")
var keyboard = flag.Bool("k", false, "set GTK layer shell Keyboard interactivity to 'on-demand' mode") var keyboard = flag.Bool("k", false, "set GTK layer shell Keyboard interactivity to 'on-demand' mode")
var overlay = flag.Bool("ovl", false, "use OVerLay layer") var overlay = flag.Bool("ovl", false, "use OVerLay layer")
@@ -149,7 +166,7 @@ var itemSpacing = flag.Uint("spacing", 20, "icon spacing")
var lang = flag.String("lang", "", "force lang, e.g. \"en\", \"pl\"") var lang = flag.String("lang", "", "force lang, e.g. \"en\", \"pl\"")
var fileManager = flag.String("fm", "thunar", "File Manager") var fileManager = flag.String("fm", "thunar", "File Manager")
var term = flag.String("term", defaultTermIfBlank(os.Getenv("TERM"), "foot"), "Terminal emulator") var term = flag.String("term", defaultTermIfBlank(os.Getenv("TERM"), "foot"), "Terminal emulator")
var wm = flag.String("wm", defaultStringIfBlank(os.Getenv("XDG_CURRENT_DESKTOP"), ""), "Use swaymsg (with 'sway' argument) or hyprctl (with 'hyprland')") var wm = flag.String("wm", "", "use swaymsg exec (with 'sway' argument) or hyprctl dispatch exec (with 'hyprland') to launch programs")
var nameLimit = flag.Int("fslen", 80, "File Search name LENgth Limit") var nameLimit = flag.Int("fslen", 80, "File Search name LENgth Limit")
var noCats = flag.Bool("nocats", false, "Disable filtering by category") var noCats = flag.Bool("nocats", false, "Disable filtering by category")
var noFS = flag.Bool("nofs", false, "Disable file search") var noFS = flag.Bool("nofs", false, "Disable file search")
@@ -380,6 +397,7 @@ func main() {
if *targetOutput != "" { if *targetOutput != "" {
// We want to assign layershell to a monitor, but we only know the output name! // We want to assign layershell to a monitor, but we only know the output name!
output2mon, err = mapOutputs() output2mon, err = mapOutputs()
fmt.Println(">>>", output2mon)
if err == nil { if err == nil {
monitor := output2mon[*targetOutput] monitor := output2mon[*targetOutput]
layershell.SetMonitor(win, monitor) layershell.SetMonitor(win, monitor)
@@ -542,8 +560,10 @@ func main() {
} }
statusLineWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) statusLineWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
statusLineWrapper.SetProperty("name", "status-line-wrapper")
outerVBox.PackStart(statusLineWrapper, false, false, 10) outerVBox.PackStart(statusLineWrapper, false, false, 10)
statusLabel, _ = gtk.LabelNew(status) statusLabel, _ = gtk.LabelNew(status)
statusLabel.SetProperty("name", "status-label")
statusLineWrapper.PackStart(statusLabel, true, false, 0) statusLineWrapper.PackStart(statusLabel, true, false, 0)
win.ShowAll() win.ShowAll()

181
tools.go
View File

@@ -5,8 +5,11 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/joshuarubin/go-sway"
log "github.com/sirupsen/logrus"
"io" "io"
"io/fs" "io/fs"
"net"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@@ -17,11 +20,8 @@ import (
"syscall" "syscall"
"time" "time"
log "github.com/sirupsen/logrus"
"github.com/gotk3/gotk3/gdk" "github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/gtk" "github.com/gotk3/gotk3/gtk"
"github.com/joshuarubin/go-sway"
) )
func wayland() bool { func wayland() bool {
@@ -548,6 +548,14 @@ func savePinned() {
} }
func launch(command string, terminal bool) { func launch(command string, terminal bool) {
// trim % and everything afterwards
if strings.Contains(command, "%") {
cutAt := strings.Index(command, "%")
if cutAt != -1 {
command = command[:cutAt-1]
}
}
themeToPrepend := "" themeToPrepend := ""
// add "GTK_THEME=<default_gtk_theme>" environment variable // add "GTK_THEME=<default_gtk_theme>" environment variable
if *forceTheme { if *forceTheme {
@@ -557,70 +565,31 @@ func launch(command string, terminal bool) {
themeToPrepend = th.(string) themeToPrepend = th.(string)
} }
} }
// trim % and everything afterwards
if strings.Contains(command, "%") {
cutAt := strings.Index(command, "%")
if cutAt != -1 {
command = command[:cutAt-1]
}
}
elements := strings.Split(command, " ")
envVarsNum := 0
if strings.Contains(elements[0], "=") {
for _, element := range elements {
if strings.Contains(element, "=") {
envVarsNum++
} else {
break
}
}
}
// find prepended env variables, if any
var envVars []string
if themeToPrepend != "" { if themeToPrepend != "" {
envVars = append(envVars, fmt.Sprintf("GTK_THEME=%s", themeToPrepend)) command = fmt.Sprintf("GTK_THEME=%q %s", themeToPrepend, command)
} }
cmdIdx := envVarsNum var elements = []string{"/usr/bin/env", "-S", command}
firstArgIdx := envVarsNum + 1
if envVarsNum > 0 { cmd := exec.Command(elements[0], elements[1:]...)
for idx, item := range elements {
if envVarsNum > idx && strings.Contains(item, "=") {
envVars = append(envVars, item)
}
}
}
cmd := exec.Command(elements[cmdIdx], elements[firstArgIdx:]...)
var prefixCommand string
var args []string
if terminal { if terminal {
prefixCommand = *term var prefixCommand = *term
if *term != "foot" { var args []string
args = []string{"-e", strings.Join(elements, " ")} if prefixCommand != "foot" {
args = []string{"-e", command}
} else { } else {
args = elements[cmdIdx:] args = elements
} }
cmd = exec.Command(prefixCommand, args...) cmd = exec.Command(prefixCommand, args...)
} else if *wm == "sway" { } else if *wm == "sway" {
cmd = exec.Command("swaymsg", "exec", strings.Join(elements, " ")) cmd = exec.Command("swaymsg", "exec", strings.Join(elements, " "))
} else if *wm == "hyprland" { } else if *wm == "hyprland" || *wm == "Hyprland" {
cmd = exec.Command("hyprctl", "dispatch", "exec", strings.Join(elements, " ")) cmd = exec.Command("hyprctl", "dispatch", "exec", strings.Join(elements, " "))
} }
// set env variables msg := fmt.Sprintf("command: %q; args: %q\n", cmd.Args[0], cmd.Args[1:])
if len(envVars) > 0 {
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, envVars...)
}
msg := fmt.Sprintf("env vars: %s; command: '%s'; args: %s\n", envVars, cmd.Args[0], cmd.Args[1:])
log.Info(msg) log.Info(msg)
cmd.SysProcAttr = &syscall.SysProcAttr{ cmd.SysProcAttr = &syscall.SysProcAttr{
@@ -682,35 +651,64 @@ func open(filePath string, xdgOpen bool) {
func mapOutputs() (map[string]*gdk.Monitor, error) { func mapOutputs() (map[string]*gdk.Monitor, error) {
result := make(map[string]*gdk.Monitor) result := make(map[string]*gdk.Monitor)
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) if os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") != "" {
defer cancel() err := listHyprlandMonitors()
if err == nil {
client, err := sway.New(ctx) display, err := gdk.DisplayGetDefault()
if err != nil { if err != nil {
return nil, err return nil, err
} }
outputs, err := client.GetOutputs(ctx) num := display.GetNMonitors()
if err != nil { for i := 0; i < num; i++ {
return nil, err mon, _ := display.GetMonitor(i)
} geometry := mon.GetGeometry()
// assign output to monitor on the basis of the same x, y coordinates
for _, output := range hyprlandMonitors {
if int(output.X) == geometry.GetX() && int(output.Y) == geometry.GetY() {
result[output.Name] = mon
}
}
}
} else {
return nil, err
}
display, err := gdk.DisplayGetDefault() } else if os.Getenv("SWAYSOCK") != "" {
if err != nil { ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
return nil, err defer cancel()
}
num := display.GetNMonitors() client, err := sway.New(ctx)
for i := 0; i < num; i++ { if err != nil {
monitor, _ := display.GetMonitor(i) return nil, err
geometry := monitor.GetGeometry() }
// assign output to monitor on the basis of the same x, y coordinates
for _, output := range outputs { outputs, err := client.GetOutputs(ctx)
if int(output.Rect.X) == geometry.GetX() && int(output.Rect.Y) == geometry.GetY() { if err != nil {
result[output.Name] = monitor return nil, err
}
display, err := gdk.DisplayGetDefault()
if err != nil {
return nil, err
}
num := display.GetNMonitors()
for i := 0; i < num; i++ {
monitor, _ := display.GetMonitor(i)
geometry := monitor.GetGeometry()
// assign output to monitor on the basis of the same x, y coordinates
for _, output := range outputs {
if int(output.Rect.X) == geometry.GetX() && int(output.Rect.Y) == geometry.GetY() {
result[output.Name] = monitor
}
} }
} }
} else {
return nil, errors.New("output assignment only supported on sway and Hyprland")
} }
return result, nil return result, nil
} }
@@ -729,3 +727,38 @@ func substring(s string, start int, end int) string {
} }
return s[startStrIdx:] return s[startStrIdx:]
} }
func hyprctl(cmd string) ([]byte, error) {
his := os.Getenv("HYPRLAND_INSTANCE_SIGNATURE")
socketFile := fmt.Sprintf("/tmp/hypr/%s/.socket.sock", his)
conn, err := net.Dial("unix", socketFile)
if err != nil {
return nil, err
}
message := []byte(cmd)
_, err = conn.Write(message)
if err != nil {
return nil, err
}
reply := make([]byte, 102400)
n, err := conn.Read(reply)
if err != nil {
return nil, err
}
defer conn.Close()
return reply[:n], nil
}
func listHyprlandMonitors() error {
reply, err := hyprctl("j/monitors")
if err != nil {
return err
} else {
err = json.Unmarshal([]byte(reply), &hyprlandMonitors)
}
return err
}