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"
)
const version = "0.4.0"
const version = "0.4.3"
var (
appDirs []string
configDirectory string
pinnedFile string
pinned []string
id2entry map[string]desktopEntry
preferredApps map[string]interface{}
exclusions []string
appDirs []string
configDirectory string
pinnedFile string
pinned []string
id2entry map[string]desktopEntry
preferredApps map[string]interface{}
exclusions []string
hyprlandMonitors []monitor
)
var categoryNames = [...]string{
@@ -66,6 +67,30 @@ type desktopEntry struct {
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
var (
listUtility []string
@@ -114,24 +139,16 @@ func defaultTermIfBlank(s, fallback string) string {
return s
}
func defaultStringIfBlank(s, fallback string) string {
s = strings.TrimSpace(s)
if s == "" {
return fallback
}
return s
}
func validateWm() {
if !(*wm == "sway" || *wm == "hyprland") && *wm != "" {
if !(*wm == "sway" || *wm == "hyprland" || *wm == "Hyprland") && *wm != "" {
*wm = ""
log.Warn("-wm argument supports only sway or hyprland string.")
log.Warn("-wm argument supports only 'sway' or 'hyprland' string.")
}
}
// Flags
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 keyboard = flag.Bool("k", false, "set GTK layer shell Keyboard interactivity to 'on-demand' mode")
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 fileManager = flag.String("fm", "thunar", "File Manager")
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 noCats = flag.Bool("nocats", false, "Disable filtering by category")
var noFS = flag.Bool("nofs", false, "Disable file search")
@@ -380,6 +397,7 @@ func main() {
if *targetOutput != "" {
// We want to assign layershell to a monitor, but we only know the output name!
output2mon, err = mapOutputs()
fmt.Println(">>>", output2mon)
if err == nil {
monitor := output2mon[*targetOutput]
layershell.SetMonitor(win, monitor)
@@ -542,8 +560,10 @@ func main() {
}
statusLineWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
statusLineWrapper.SetProperty("name", "status-line-wrapper")
outerVBox.PackStart(statusLineWrapper, false, false, 10)
statusLabel, _ = gtk.LabelNew(status)
statusLabel.SetProperty("name", "status-label")
statusLineWrapper.PackStart(statusLabel, true, false, 0)
win.ShowAll()

181
tools.go
View File

@@ -5,8 +5,11 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/joshuarubin/go-sway"
log "github.com/sirupsen/logrus"
"io"
"io/fs"
"net"
"os"
"os/exec"
"path"
@@ -17,11 +20,8 @@ import (
"syscall"
"time"
log "github.com/sirupsen/logrus"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/gtk"
"github.com/joshuarubin/go-sway"
)
func wayland() bool {
@@ -548,6 +548,14 @@ func savePinned() {
}
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 := ""
// add "GTK_THEME=<default_gtk_theme>" environment variable
if *forceTheme {
@@ -557,70 +565,31 @@ func launch(command string, terminal bool) {
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 != "" {
envVars = append(envVars, fmt.Sprintf("GTK_THEME=%s", themeToPrepend))
command = fmt.Sprintf("GTK_THEME=%q %s", themeToPrepend, command)
}
cmdIdx := envVarsNum
firstArgIdx := envVarsNum + 1
var elements = []string{"/usr/bin/env", "-S", command}
if envVarsNum > 0 {
for idx, item := range elements {
if envVarsNum > idx && strings.Contains(item, "=") {
envVars = append(envVars, item)
}
}
}
cmd := exec.Command(elements[0], elements[1:]...)
cmd := exec.Command(elements[cmdIdx], elements[firstArgIdx:]...)
var prefixCommand string
var args []string
if terminal {
prefixCommand = *term
if *term != "foot" {
args = []string{"-e", strings.Join(elements, " ")}
var prefixCommand = *term
var args []string
if prefixCommand != "foot" {
args = []string{"-e", command}
} else {
args = elements[cmdIdx:]
args = elements
}
cmd = exec.Command(prefixCommand, args...)
} else if *wm == "sway" {
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, " "))
}
// set env variables
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:])
msg := fmt.Sprintf("command: %q; args: %q\n", cmd.Args[0], cmd.Args[1:])
log.Info(msg)
cmd.SysProcAttr = &syscall.SysProcAttr{
@@ -682,35 +651,64 @@ func open(filePath string, xdgOpen bool) {
func mapOutputs() (map[string]*gdk.Monitor, error) {
result := make(map[string]*gdk.Monitor)
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
if os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") != "" {
err := listHyprlandMonitors()
if err == nil {
client, err := sway.New(ctx)
if err != nil {
return nil, err
}
display, err := gdk.DisplayGetDefault()
if err != nil {
return nil, err
}
outputs, err := client.GetOutputs(ctx)
if err != nil {
return nil, err
}
num := display.GetNMonitors()
for i := 0; i < num; i++ {
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()
if err != nil {
return nil, err
}
} else if os.Getenv("SWAYSOCK") != "" {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
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
client, err := sway.New(ctx)
if err != nil {
return nil, err
}
outputs, err := client.GetOutputs(ctx)
if err != nil {
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
}
@@ -729,3 +727,38 @@ func substring(s string, start int, end int) string {
}
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
}