add hyprland plugin subcommands

parent 631f8e23
......@@ -13,6 +13,7 @@ type HyprEnv struct {
Config string
SystemModulesDir string
UserModulesDir string
PluginsDir string
}
type RepoEnv struct {
......@@ -40,13 +41,14 @@ func InitConfig() error {
home, _ := os.UserHomeDir()
Env.Create = &CreateEnv{
Config: "/etc/ximperdistro/ximperconf/profiles.json",
Config: "/etc/ximperdistro/ximperconf/profiles.yaml",
}
if Env.Hyprland != nil {
userModulesPath := filepath.Join(home, ".config", "hypr")
systemModulesPath := "/etc/ximperdistro/hyprland/hypr"
confPath := filepath.Join(userModulesPath, "hyprland.conf")
pluginsPath := "/usr/lib64/hyprland"
if _, err := os.Stat(userModulesPath); os.IsNotExist(err) {
_ = os.MkdirAll(userModulesPath, 0755)
......@@ -59,6 +61,12 @@ func InitConfig() error {
Env.Hyprland.SystemModulesDir = systemModulesPath
}
if _, err := os.Stat(pluginsPath); os.IsNotExist(err) {
Env.Hyprland.PluginsDir = ""
} else {
Env.Hyprland.PluginsDir = pluginsPath
}
if _, err := os.Stat(confPath); err == nil {
Env.Hyprland.Config = confPath
} else {
......
......@@ -46,12 +46,12 @@ complete -c ximperconf -n '__fish_seen_subcommand_from create' -f -l dry-run -s
complete -c ximperconf -n '__fish_seen_subcommand_from create' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_ximperconf_no_subcommand' -a 'hyprland' -d 'Hyprland Management'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and not __fish_seen_subcommand_from check sync-xkb-layouts module var' -a 'check' -d 'Check the Hyprland config'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and not __fish_seen_subcommand_from check sync-xkb-layouts module var plugin' -a 'check' -d 'Check the Hyprland config'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from check' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and not __fish_seen_subcommand_from check sync-xkb-layouts module var' -a 'sync-xkb-layouts' -d 'Sync layouts with xkb'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and not __fish_seen_subcommand_from check sync-xkb-layouts module var plugin' -a 'sync-xkb-layouts' -d 'Sync layouts with xkb'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from sync-xkb-layouts' -f -l force -s f -d 'Forced update'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from sync-xkb-layouts' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and not __fish_seen_subcommand_from check sync-xkb-layouts module var' -a 'module' -d 'Hyprland modules'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and not __fish_seen_subcommand_from check sync-xkb-layouts module var plugin' -a 'module' -d 'Hyprland modules'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from module' -f -l user -s u -d 'use user modules'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from module' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from module; and not __fish_seen_subcommand_from status info enable disable toggle' -a 'status' -d 'Hyprland module status'
......@@ -65,7 +65,7 @@ complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_s
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from module; and __fish_seen_subcommand_from disable' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from module; and not __fish_seen_subcommand_from status info enable disable toggle' -a 'toggle' -d 'toggle module'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from module; and __fish_seen_subcommand_from toggle' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and not __fish_seen_subcommand_from check sync-xkb-layouts module var' -a 'var' -d 'Hyprland vars'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and not __fish_seen_subcommand_from check sync-xkb-layouts module var plugin' -a 'var' -d 'Hyprland vars'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from var' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from var; and not __fish_seen_subcommand_from list info get set unset' -a 'list' -d 'vars list'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from var; and __fish_seen_subcommand_from list' -f -l help -s h -d 'show help'
......@@ -77,6 +77,21 @@ complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_s
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from var; and __fish_seen_subcommand_from set' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from var; and not __fish_seen_subcommand_from list info get set unset' -a 'unset' -d 'unset var'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from var; and __fish_seen_subcommand_from unset' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and not __fish_seen_subcommand_from check sync-xkb-layouts module var plugin' -a 'plugin' -d 'Hyprland plugins'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from list status info load unload toggle' -a 'list' -d 'Hyprland plugins list'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from list' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from list status info load unload toggle' -a 'status' -d 'Hyprland plugin status'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from status' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from list status info load unload toggle' -a 'info' -d 'Information about plugins'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from info' -f -l filter -s f -r -d 'status filter'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from info' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from list status info load unload toggle' -a 'load' -d 'load plugin'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from load' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from list status info load unload toggle' -a 'unload' -d 'unload plugin'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from unload' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from list status info load unload toggle' -a 'toggle' -d 'toggle plugin'
complete -c ximperconf -n '__fish_seen_subcommand_from hyprland; and __fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from toggle' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_ximperconf_no_subcommand' -a 'help' -d 'show help'
complete -c ximperconf -n '__fish_seen_subcommand_from completion' -f -l help -s h -d 'show help'
complete -x -c ximperconf -n '__fish_seen_subcommand_from completion; and not __fish_seen_subcommand_from help h' -a 'help' -d 'Shows a list of commands or help for one command'
......@@ -120,6 +120,58 @@ func CommandList() *cli.Command {
},
},
},
{
Name: "plugin",
Usage: "Hyprland plugins",
Commands: []*cli.Command{
{
Name: "list",
Usage: "Hyprland plugins list",
Action: HyprlandPluginListCommand,
},
{
Name: "status",
Usage: "Hyprland plugin status",
ArgsUsage: "plugin",
Action: HyprlandPluginStatusCommand,
ShellComplete: ShellCompletePlugin("all"),
},
{
Name: "info",
Usage: "Information about plugins",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "filter",
Usage: "status filter",
Aliases: []string{"f"},
Value: "all",
},
},
Action: HyprlandPluginInfoCommand,
},
{
Name: "load",
Usage: "load plugin",
ArgsUsage: "plugin",
Action: HyprlandPluginLoadCommand,
ShellComplete: ShellCompletePlugin("unloaded"),
},
{
Name: "unload",
Usage: "unload plugin",
ArgsUsage: "plugin",
Action: HyprlandPluginUnloadCommand,
ShellComplete: ShellCompletePlugin("loaded"),
},
{
Name: "toggle",
Usage: "toggle plugin",
ArgsUsage: "plugin",
Action: HyprlandPluginToggleCommand,
ShellComplete: ShellCompletePlugin("all"),
},
},
},
},
}
}
......@@ -148,3 +200,16 @@ func ShellCompleteModule(filter string) func(ctx context.Context, cmd *cli.Comma
}
}
}
func ShellCompletePlugin(filter string) func(ctx context.Context, cmd *cli.Command) {
return func(ctx context.Context, cmd *cli.Command) {
if cmd.NArg() > 0 {
return
}
data := hyprlandPluginList(filter)
for _, t := range data {
fmt.Println(t)
}
}
}
package hyprland
import (
"ximperconf/config"
"ximperconf/utils"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/fatih/color"
"github.com/urfave/cli/v3"
)
type HyprlandPlugin struct {
Name string `json:"name"`
Author string `json:"author"`
Handle string `json:"handle"`
Version string `json:"version"`
Description string `json:"description"`
}
func preparePlugin(cmd *cli.Command) (string, string, error) {
name := cmd.Args().Get(0)
if name == "" {
return "", "", fmt.Errorf("плагин не указан")
}
path := filepath.Join(config.Env.Hyprland.PluginsDir, name+".so")
if !utils.FileExists(path) {
return "", "", fmt.Errorf("плагин не найден")
}
status := hyprlandPluginStatus(name)
return path, status, nil
}
func fetchLoadedPlugins() ([]HyprlandPlugin, error) {
out, err := exec.Command("hyprctl", "plugin", "list", "-j").Output()
if err != nil {
return nil, fmt.Errorf("hyprctl error: %w", err)
}
var plugins []HyprlandPlugin
if err := json.Unmarshal(out, &plugins); err != nil {
return nil, fmt.Errorf("invalid JSON: %w", err)
}
return plugins, nil
}
func hyprlandPluginStatus(plugin string) string {
loaded, err := fetchLoadedPlugins()
if err != nil {
return "error"
}
for _, p := range loaded {
if p.Name == plugin {
return "loaded"
}
}
return "unloaded"
}
func hyprlandPluginList(filter string) []string {
entries, err := os.ReadDir(config.Env.Hyprland.PluginsDir)
if err != nil {
return nil
}
var list []string
for _, e := range entries {
if e.IsDir() || !strings.HasSuffix(e.Name(), ".so") {
continue
}
name := strings.TrimSuffix(e.Name(), ".so")
status := hyprlandPluginStatus(name)
if filter == "" || filter == "all" || filter == status {
list = append(list, name)
}
}
return list
}
func hyprlandPluginLoad(path string) error {
time.Sleep(1 * time.Second)
cmd := exec.Command("hyprctl", "plugin", "load", path)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("%v (%s)", err, out)
}
return nil
}
func hyprlandPluginUnload(path string) error {
time.Sleep(1 * time.Second)
cmd := exec.Command("hyprctl", "plugin", "unload", path)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("%v (%s)", err, out)
}
return nil
}
func hyprlandPluginInfo(filter string) {
plugins := hyprlandPluginList(filter)
if len(plugins) == 0 {
color.Red("Нет доступных плагинов.")
return
}
color.Blue("Plugins:")
statusMap := map[string]struct {
symbol string
color func(string, ...interface{}) string
}{
"loaded": {"●", color.GreenString},
"unloaded": {"○", color.RedString},
}
for i, name := range plugins {
status := hyprlandPluginStatus(name)
info := statusMap[status]
prefix := "├──"
if i == len(plugins)-1 {
prefix = "└──"
}
fmt.Printf("%s %s %s\n",
prefix,
info.color(info.symbol),
info.color(name),
)
}
}
func HyprlandPluginListCommand(ctx context.Context, cmd *cli.Command) error {
fmt.Println(strings.Join(hyprlandPluginList(cmd.String("filter")), "\n"))
return nil
}
func HyprlandPluginStatusCommand(ctx context.Context, cmd *cli.Command) error {
fmt.Println(hyprlandPluginStatus(cmd.Args().Get(0)))
return nil
}
func HyprlandPluginInfoCommand(ctx context.Context, cmd *cli.Command) error {
hyprlandPluginInfo(cmd.String("filter"))
return nil
}
func HyprlandPluginLoadCommand(ctx context.Context, cmd *cli.Command) error {
path, status, err := preparePlugin(cmd)
if err != nil {
color.Red(err.Error())
return nil
}
if status == "loaded" {
color.Red("плагин уже загружен")
return nil
}
if err := hyprlandPluginLoad(path); err != nil {
color.Red(err.Error())
return err
}
fmt.Println("OK")
return nil
}
func HyprlandPluginUnloadCommand(ctx context.Context, cmd *cli.Command) error {
path, status, err := preparePlugin(cmd)
if err != nil {
color.Red(err.Error())
return nil
}
if status == "unloaded" {
color.Red("плагин не загружен")
return nil
}
if err := hyprlandPluginUnload(path); err != nil {
color.Red(err.Error())
return err
}
fmt.Println("OK")
return nil
}
func HyprlandPluginToggleCommand(ctx context.Context, cmd *cli.Command) error {
path, status, err := preparePlugin(cmd)
if err != nil {
color.Red(err.Error())
return nil
}
var execErr error
switch status {
case "loaded":
execErr = hyprlandPluginUnload(path)
case "unloaded":
execErr = hyprlandPluginLoad(path)
}
if execErr != nil {
color.Red(execErr.Error())
return execErr
}
fmt.Println("OK")
return nil
}
package utils
import "os"
func FileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment