Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
223e16f77c | ||
|
|
55c095c053 | ||
|
|
ee2008348f | ||
|
|
0570f90bec | ||
|
|
a2917d68dd | ||
|
|
b05b0f484a | ||
|
|
b61e665c71 | ||
|
|
8408c669ef | ||
|
|
d66066c3b6 | ||
|
|
dac55cdd0d | ||
|
|
03365d24e5 | ||
|
|
8e58654336 | ||
|
|
33e0032725 | ||
|
|
c05fff189d | ||
|
|
57d130ca51 | ||
|
|
954a6ba9c9 |
60
main.go
60
main.go
@@ -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
181
tools.go
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user