diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b5a5bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Created by https://www.toptal.com/developers/gitignore/api/go +# Edit at https://www.toptal.com/developers/gitignore?templates=go + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +# End of https://www.toptal.com/developers/gitignore/api/go + +# nix +result +hyprman diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7048b81 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Daylin Morgan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/cmd/eww.go b/cmd/eww.go new file mode 100644 index 0000000..6e02195 --- /dev/null +++ b/cmd/eww.go @@ -0,0 +1,16 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var ewwCmd = &cobra.Command{ + Use:"eww" , + Short: "eww integration", +} + +func init() { + rootCmd.AddCommand(ewwCmd) +} + + diff --git a/cmd/eww_start.go b/cmd/eww_start.go new file mode 100644 index 0000000..211a22e --- /dev/null +++ b/cmd/eww_start.go @@ -0,0 +1,17 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var startCmd = &cobra.Command{ + Use: "start", + Short: "Launch eww", + Run: func(cmd *cobra.Command, args []string) { + hm.LaunchEww() + }, +} + +func init() { + ewwCmd.AddCommand(startCmd) +} diff --git a/cmd/eww_watch.go b/cmd/eww_watch.go new file mode 100644 index 0000000..5c9ad61 --- /dev/null +++ b/cmd/eww_watch.go @@ -0,0 +1,17 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var watchCmd = &cobra.Command{ + Use: "watch", + Short: "Watch hyprland events and propagate to eww", + Run: func(cmd *cobra.Command, args []string) { + hm.Watch() + }, +} + +func init() { + ewwCmd.AddCommand(watchCmd) +} diff --git a/cmd/mako.go b/cmd/mako.go new file mode 100644 index 0000000..13f7fb1 --- /dev/null +++ b/cmd/mako.go @@ -0,0 +1,22 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + // "git.dayl.in/daylin/hyprman/internal" +) + +var makoCmd = &cobra.Command{ + Use:"mako" , + Short: "mako integration", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("hahah mako baby") + }, +} + +func init() { + rootCmd.AddCommand(makoCmd) +} + + diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..318732e --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,46 @@ +package cmd + +import ( + "os" + cc "github.com/ivanpirog/coloredcobra" + "git.dayl.in/daylin/hyprman/internal" + "github.com/spf13/cobra" +) + + +func Execute() { + cc.Init(&cc.Config{ + RootCmd: rootCmd, + Headings: cc.HiMagenta + cc.Bold, + Commands: cc.Bold, + Example: cc.Italic, + ExecName: cc.Bold, + Flags: cc.Bold, + NoExtraNewlines: true, + NoBottomNewline: true, + }) + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +var configPath string +var hm = &hyprman.Hyprman{} + +var rootCmd = &cobra.Command{ + Use: "hyprman", + Short: "hyprland companion app", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + hm.LoadConfig(configPath) + }, +} + + + +func init() { + rootCmd.CompletionOptions.HiddenDefaultCmd = true + rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", hyprman.DefaultConfigPath(), "path to config file") +} + + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..9020e1f --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1716312448, + "narHash": "sha256-PH3w5av8d+TdwCkiWN4UPBTxrD9MpxIQPDVWctlomVo=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "e381a1288138aceda0ac63db32c7be545b446921", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..96be18d --- /dev/null +++ b/flake.nix @@ -0,0 +1,48 @@ +{ + description = "hyprman"; + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + }; + + outputs = + inputs@{ + nixpkgs, + self, + ... + }: + let + inherit (nixpkgs.lib) genAttrs cleanSource; + supportedSystems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ]; + forAllSystems = f: genAttrs supportedSystems (system: f nixpkgs.legacyPackages.${system}); + in + { + devShells = forAllSystems (pkgs: { + default = pkgs.mkShell { + packages = with pkgs; [go]; + }; + }); + packages = forAllSystems (pkgs: { + default = self.packages.${pkgs.system}.hyprman; + hyprman = pkgs.buildGoModule { + pname = "hyprman"; + version = "unstable"; + src = cleanSource ./.; + vendorHash = "sha256-eKeUhS2puz6ALb+cQKl7+DGvm9Cl+miZAHX0imf9wdg="; + + nativeBuildInputs = with pkgs; [installShellFiles]; + + postInstall = '' + installShellCompletion --cmd hyprman \ + --zsh <($out/bin/hyprman completion zsh) + ''; + }; + + }); + formatter = forAllSystems (pkgs: pkgs.nixfmt-rfc-style); + }; +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..512215c --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module git.dayl.in/daylin/hyprman + +go 1.22.2 + +require ( + github.com/goccy/go-yaml v1.11.3 + github.com/ivanpirog/coloredcobra v1.0.1 + github.com/spf13/cobra v1.8.0 +) + +require ( + github.com/fatih/color v1.17.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect +) + +replace github.com/ivanpirog/coloredcobra => github.com/daylinmorgan/coloredcobra v0.0.0-20240527152736-9d3ce38297a6 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..91eaaec --- /dev/null +++ b/go.sum @@ -0,0 +1,45 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/daylinmorgan/coloredcobra v0.0.0-20240527152736-9d3ce38297a6 h1:Zst7HlWvQj8LPJ3mhIWtGH2DxqUo6VEq0oekNyAho6M= +github.com/daylinmorgan/coloredcobra v0.0.0-20240527152736-9d3ce38297a6/go.mod h1:csDZxFD5oWCy1x8hxXVD4txpYQgo12WOcLS7JON5SVE= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= +github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/hyprman.go b/internal/hyprman.go new file mode 100644 index 0000000..c9a6f40 --- /dev/null +++ b/internal/hyprman.go @@ -0,0 +1,280 @@ +package hyprman + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "log" + "net" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + yaml "github.com/goccy/go-yaml" +) + +func DefaultConfigPath() string { + configPath, exists := os.LookupEnv("XDG_CONFIG_HOME") + if !exists { + home, exists := os.LookupEnv("HOME") + if !exists { + log.Fatalln("failed to set default icons.json path is $HOME or $XDG_CONFIG_HOME set?") + } + configPath = filepath.Join(home, ".config") + } + return filepath.Join(configPath, "hyprman", "config.yml") +} + +func hyprSocketBase() string { + runtimeDir, exists := os.LookupEnv("XDG_RUNTIME_DIR") + if !exists { + log.Fatalln("XDG_RUNTIME_DIR not set") + } + instanceSig, exists := os.LookupEnv("HYPRLAND_INSTANCE_SIGNATURE") + if !exists { + log.Fatalln("HYPRLAND_INSTANCE_SIGNATURE not set") + } + return filepath.Join(runtimeDir, "hypr", instanceSig) +} + +func hyprSocket() string { + return filepath.Join(hyprSocketBase(), ".socket.sock") +} + +func hyprSocket2() string { + return filepath.Join(hyprSocketBase(), ".socket2.sock") +} + +type Workspace struct { + Name string + Id int +} + +type Client struct { + Class string + Workspace Workspace +} + +type Icons map[string]string + +type Hyprman struct { + Config Config +} + +type Config struct { + Classes Icons + NoClientIcon string `yaml:"no-client"` + DefaultIcon string `yaml:"default-icon"` +} + +func (hm *Hyprman) LoadConfig(path string) { + var config Config + data, err := os.ReadFile(path) + if err != nil { + log.Fatal(fmt.Errorf("failed to read config at %s:\n%w", path, err)) + } + if err := yaml.Unmarshal([]byte(data), &config); err != nil { + log.Fatal(fmt.Errorf("failed to read config at %s:\n%w", path, err)) + } + hm.Config = config +} + +func hyprctl(cmd string) []byte { + socketPath := hyprSocket() + conn, err := net.Dial("unix", socketPath) + if err != nil { + log.Fatalln("Error connecting to hyprland IPC:", err) + } + defer conn.Close() + _, err = conn.Write([]byte(cmd)) + if err != nil { + log.Fatal(err) + } + + buf := make([]byte, 1024*8) // Bigger? + n, err := conn.Read(buf) + if err != nil { + log.Fatal(err) + } + return buf[0:n] +} + +type ActiveWorkspace struct { + Name string + Id int +} + +type Monitor struct { + ActiveWorkspace ActiveWorkspace + Id int +} + +// generic or interface? +func getMonitors() []Monitor { + data := hyprctl("[-j]/monitors") + var monitors []Monitor + err := json.Unmarshal(data, &monitors) + if err != nil { + log.Fatalln(err) + } + return monitors +} + +func getClients() []Client { + data := hyprctl("[-j]/clients") + var clients []Client + err := json.Unmarshal(data, &clients) + if err != nil { + log.Fatalln(err) + } + return clients +} + +type EwwWorkspace struct { + Icon string `json:"icon"` + Class string `json:"class"` + Id int `json:"id"` +} + +func (hm *Hyprman) pickIcon(class string) string { + for k, v := range hm.Config.Classes { + if strings.Contains(k, class) { + return v + } + } + return hm.Config.DefaultIcon +} + +func openWorkspaces(monitors []Monitor) []int { + open := make([]int, len(monitors)) + for i, m := range monitors { + open[i] = m.ActiveWorkspace.Id + } + return open +} + +func (hm *Hyprman) generateEwwClasses() { + monitors := getMonitors() + clients := getClients() + + var ewwClasses [][]EwwWorkspace + + monitor := make([]EwwWorkspace, 9) + + for i := range 9 { + monitor[i] = EwwWorkspace{ + hm.Config.NoClientIcon, + fmt.Sprintf("ws-button-%d", i+1), + i + 1, + } + } + + for _, client := range clients { + id := client.Workspace.Id - 1 + currentIcon := monitor[id].Icon + if currentIcon == hm.Config.NoClientIcon { + monitor[id].Icon = hm.pickIcon(client.Class) + } else { + monitor[id].Icon = fmt.Sprintf("%s%s", currentIcon, hm.pickIcon(client.Class)) + } + } + + for _, id := range openWorkspaces(monitors) { + // activeId := m.ActiveWorkspace.Id + monitor[id-1].Class = fmt.Sprintf("%s %s", monitor[id-1].Class, "ws-button-open") + } + + for i, m := range monitors { + activeId := m.ActiveWorkspace.Id - 1 + ewwClasses = append(ewwClasses, make([]EwwWorkspace, 9)) + copy(ewwClasses[i], monitor) + ewwClasses[i][activeId].Class = fmt.Sprintf("%s %s-%d", monitor[activeId].Class, "ws-button-active", activeId+1) + + } + + bytes, err := json.Marshal(ewwClasses) + if err != nil { + panic(err) + } + fmt.Println(string(bytes)) +} + +func ewwBar1(cmd string) { + time.Sleep(200 * time.Second) + if err := exec.Command("eww", cmd, "bar1").Run(); err != nil { + log.Fatal(err) + } +} + +func notify(message string) { + cmd := exec.Command("notify-send", "hyprman", message) + cmd.Run() +} + +func (hm *Hyprman) handleHyprEvent(line string) { + s := strings.Split(line, ">>") + event, _ := s[0], s[1] + switch event { + case "monitorremoved": + notify("Monitor removed closing bar1") + go ewwBar1("close") + hm.generateEwwClasses() + case "monitoradded": + notify("Monitor added opening bar1") + go ewwBar1("open") + hm.generateEwwClasses() + case "workspace", + "focusedmon", + "activewindow", + "createworkspace", + "destroyworkspace", + "moveworkspace", + "renameworkspace", + "openwindow", + "closewindow", + "movewindow", + "movewindowv2", + "changefloatingmode", + "windowtitle", + "togglegroup", + "moveintogroup", + "moveoutofgroup", + "configreloaded": + hm.generateEwwClasses() + } +} + +func (hm *Hyprman) LaunchEww() { + for i := range getMonitors() { + if err := exec.Command("eww", "open", fmt.Sprintf("bar%d", i)).Run(); err != nil { + notify(fmt.Sprintf("Error lanching eww:\n%v", err)) + } + } +} + +func (hm *Hyprman) Watch() { + socketPath := hyprSocket2() + conn, err := net.Dial("unix", socketPath) + if err != nil { + log.Fatalln("Error connecting to hyprland IPC:", err) + } + defer conn.Close() + reader := bufio.NewReader(conn) + hm.generateEwwClasses() + for { + line, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + // log.Printf("reached EOF for %s\n", socketPath) + break + } + // log.Println("error reading line") + continue + } + hm.handleHyprEvent(line) + } +} + diff --git a/internal/mako.go b/internal/mako.go new file mode 100644 index 0000000..dfccb24 --- /dev/null +++ b/internal/mako.go @@ -0,0 +1,36 @@ +package hyprman + +// import ( +// "log" +// "os/exec" +// ) +// +// type MakoHistory struct { +// Type string `json:"type"` +// Data [][]MakoNotification `json:"data"` +// } +// +// type MakoNotification struct { +// AppName MakoNotificationData `json:"app-name"` +// // AppIcon MakoNotificationData `json:"app-icon"` +// // Category MakoNotificationData +// // DesktopEntry MakoNotificationData `json:"desktop-entry"` +// Summary MakoNotificationData `json:"summary"` +// Body MakoNotificationData +// // Id MakoNotificationData +// // Urgency MakoNotificationData +// // Actions MakoNotificationData +// } +// +// type MakoNotificationData struct { +// Type string `json:"type"` +// Data string `json:"data"` +// } +// +// func getHistory() MakoHistory { +// makoOutput, err := exec.Command("makoctl", "history").CombinedOutput() +// if err != nil { +// log.Fatal(err) +// } +// } + diff --git a/main.go b/main.go new file mode 100644 index 0000000..a43e769 --- /dev/null +++ b/main.go @@ -0,0 +1,7 @@ +package main + +import "git.dayl.in/daylin/hyprman/cmd" + +func main() { + cmd.Execute() +} diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..7527c2b --- /dev/null +++ b/todo.md @@ -0,0 +1,8 @@ +# hyprman-go todo's + +- [ ] include in class the 'other' active monitor? +- [ ] use goroutine with slightly delay to "open/close" + bar to give hyprland time to draw/setup monitor + + +