320 lines
7.6 KiB
Go
320 lines
7.6 KiB
Go
package init
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"sectorinf.com/emilis/hlctl/cmdlets"
|
|
"sectorinf.com/emilis/hlctl/config"
|
|
"sectorinf.com/emilis/hlctl/ctllog"
|
|
"sectorinf.com/emilis/hlctl/groupctl"
|
|
"sectorinf.com/emilis/hlctl/hlcl"
|
|
"sectorinf.com/emilis/hlctl/hlcl/cmd"
|
|
"sectorinf.com/emilis/hlctl/panelctl"
|
|
"sectorinf.com/emilis/hlctl/svcctl"
|
|
)
|
|
|
|
const (
|
|
Return = "Return"
|
|
Shift = "Shift"
|
|
Tab = "Tab"
|
|
Left = "Left"
|
|
Right = "Right"
|
|
Up = "Up"
|
|
Down = "Down"
|
|
Space = "space"
|
|
Control = "Control"
|
|
Backtick = "grave"
|
|
)
|
|
|
|
var log = ctllog.Logger{}.New("init", ctllog.Green)
|
|
|
|
type command struct{}
|
|
|
|
var Command command
|
|
|
|
func (command) Name() string {
|
|
return "init"
|
|
}
|
|
|
|
func (command) Usage() cmdlets.Usage {
|
|
return cmdlets.Usage{
|
|
Name: Command.Name(),
|
|
Short: "Initialize herbstluftwm, should be run from " +
|
|
"the autostart script",
|
|
Long: []string{
|
|
"`init` starts the configuration of herbstluftwm with the",
|
|
"default configuration, setting up keybinds, hlctl groups,",
|
|
"and other commands.",
|
|
},
|
|
Flags: map[string]string{},
|
|
Examples: []string{
|
|
"hcctl init",
|
|
},
|
|
}
|
|
}
|
|
|
|
func logError(err error) {
|
|
if err != nil {
|
|
log.Error(err.Error())
|
|
}
|
|
}
|
|
|
|
type Keybind struct {
|
|
Keys []string
|
|
Command cmd.Command
|
|
Args []string
|
|
}
|
|
|
|
func (k Keybind) Set() {
|
|
err := hlcl.Keybind(k.Keys, k.Command, k.Args...)
|
|
if err != nil {
|
|
log.Errorf(
|
|
"Keybind [%s]: %s", strings.Join(k.Args, "-"), err,
|
|
)
|
|
} else {
|
|
binds := strings.Join(k.Keys, "-")
|
|
command := strings.Join(
|
|
append([]string{k.Command.String()}, k.Args...),
|
|
" ",
|
|
)
|
|
log.Printf("%s -> [%s]", binds, command)
|
|
}
|
|
}
|
|
|
|
func Keybinds(cfg config.HlwmConfig) []Keybind {
|
|
// convenience for setting []string
|
|
c := func(v ...string) []string {
|
|
return v
|
|
}
|
|
key := func(keys []string, cm cmd.Command, args ...string) Keybind {
|
|
return Keybind{
|
|
Keys: keys,
|
|
Command: cm,
|
|
Args: args,
|
|
}
|
|
}
|
|
Mod := cfg.Mod
|
|
step := "+0.05"
|
|
base := []Keybind{
|
|
key(c(Mod, Shift, "r"), cmd.Reload),
|
|
key(c(Mod, Shift, "c"), cmd.Close),
|
|
key(c(Mod, Return), cmd.Spawn, "dmenu_run"),
|
|
key(c(Mod, "s"), cmd.Spawn, "flameshot", "gui"),
|
|
key(c(Mod, cfg.TermSpawnKey), cmd.Spawn, cfg.Term),
|
|
|
|
// Focus
|
|
key(c(Mod, Left), cmd.Focus, "left"),
|
|
key(c(Mod, Right), cmd.Focus, "right"),
|
|
key(c(Mod, Up), cmd.Focus, "up"),
|
|
key(c(Mod, Down), cmd.Focus, "down"),
|
|
key(c(Mod, Tab), cmd.Cycle),
|
|
key(c(Mod, "i"), cmd.JumpTo, "urgent"),
|
|
|
|
// Move frames
|
|
key(c(Mod, Shift, Left), cmd.Shift, "left"),
|
|
key(c(Mod, Shift, Right), cmd.Shift, "right"),
|
|
key(c(Mod, Shift, Up), cmd.Shift, "up"),
|
|
key(c(Mod, Shift, Down), cmd.Shift, "down"),
|
|
|
|
// Frames
|
|
key(c(Mod, "u"), cmd.Split, "bottom", "0.7"),
|
|
key(c(Mod, "o"), cmd.Split, "right", "0.5"),
|
|
key(c(Mod, "r"), cmd.Remove),
|
|
key(c(Mod, "f"), cmd.Fullscreen, "toggle"),
|
|
key(c(Mod, Space), cmd.CycleLayout),
|
|
|
|
// Resizing
|
|
key(c(Mod, Control, Left), cmd.Resize, "left", step),
|
|
key(c(Mod, Control, Right), cmd.Resize, "right", step),
|
|
key(c(Mod, Control, Up), cmd.Resize, "up", step),
|
|
key(c(Mod, Control, Down), cmd.Resize, "down", step),
|
|
|
|
// Commands
|
|
key(c(Mod, Backtick), cmd.Spawn, "hlctl", "group", "+"),
|
|
}
|
|
|
|
for i := 0; i < int(cfg.Groups.Groups); i++ {
|
|
base = append(
|
|
base,
|
|
key(c(Mod, fmt.Sprintf("F%d", i+1)), cmd.Spawn,
|
|
"hlctl", "group", strconv.Itoa(i+1)),
|
|
)
|
|
}
|
|
return base
|
|
}
|
|
|
|
type set struct {
|
|
name string
|
|
value any
|
|
}
|
|
|
|
func (s set) String() string {
|
|
return fmt.Sprint(s.value)
|
|
}
|
|
|
|
func Theme(cfg config.HlwmConfig) {
|
|
s := func(name string, value any) set {
|
|
return set{
|
|
name: name,
|
|
value: value,
|
|
}
|
|
}
|
|
col := cfg.Theme.Colors
|
|
// Theme sets
|
|
for _, row := range [...]set{
|
|
s("frame_border_active_color", col.Active),
|
|
s("frame_border_normal_color", "black"),
|
|
s("frame_bg_active_color", col.Active),
|
|
s("frame_border_width", 2),
|
|
s("show_frame_decorations", "focused_if_multiple"),
|
|
s("tree_style", "╾│ ├└╼─┐"),
|
|
s("frame_bg_transparent", "on"),
|
|
s("smart_window_surroundings", "off"), // TODO
|
|
s("smart_frame_surroundings", "on"),
|
|
s("frame_transparent_width", 5),
|
|
s("frame_gap", 3),
|
|
s("window_gap", 0),
|
|
s("frame_padding", 0),
|
|
s("mouse_recenter_gap", 0),
|
|
} {
|
|
value := row.String()
|
|
if err := hlcl.Set(row.name, value); err != nil {
|
|
log.Errorf("Set [%s] -> [%s]: %s", row.name, value, err)
|
|
}
|
|
}
|
|
// Theme attrs
|
|
for _, row := range [...]set{
|
|
s("reset", 1),
|
|
s("tiling.reset", 1),
|
|
s("floating.reset", 1),
|
|
s("title_height", 15),
|
|
s("title_when", "multiple_tabs"),
|
|
s("title_font", cfg.FontBold),
|
|
s("title_depth", 3),
|
|
s("inner_width", 0),
|
|
s("inner_color", "black"),
|
|
s("border_width", 1),
|
|
s("floating.border_width", 4),
|
|
s("floating.outer_width", 1),
|
|
s("tiling.outer_width", 1),
|
|
s("background_color", "black"),
|
|
|
|
s("active.color", col.Active),
|
|
s("active.inner_color", col.Active),
|
|
s("active.outer_color", col.Active),
|
|
s("normal.color", col.Normal),
|
|
s("normal.inner_color", col.Normal),
|
|
s("normal.outer_color", col.Normal),
|
|
s("urgent.color", col.Urgent),
|
|
s("urgent.inner_color", col.Urgent),
|
|
s("urgent.outer_color", col.Urgent),
|
|
s("title_color", "white"),
|
|
s("normal.title_color", col.Text),
|
|
} {
|
|
attr := fmt.Sprintf("theme.%s", row.name)
|
|
v := row.String()
|
|
log.Printf("Setting [%s] -> [%s]", attr, v)
|
|
if err := hlcl.SetAttr(attr, v); err != nil {
|
|
log.Errorf("Attr [%s] -> [%s]: %s", attr, v, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func SetRules() {
|
|
sFmt := "windowtype~_NET_WM_WINDOW_TYPE_%s"
|
|
logError(hlcl.Rule("focus=on"))
|
|
logError(hlcl.Rule("floatplacement=smart"))
|
|
logError(hlcl.Rule("fixedsize", "floating=on"))
|
|
logError(hlcl.Rule(
|
|
fmt.Sprintf(sFmt, "(DIALOG|UTILITY|SPLASH)"),
|
|
"floating=on",
|
|
))
|
|
|
|
logError(hlcl.Rule(
|
|
fmt.Sprintf(sFmt, "(NOTIFICATION|DOCK|DESKTOP)"),
|
|
"manage=off",
|
|
))
|
|
}
|
|
|
|
func AddTags(groupCount, tagsPerGroup uint8) error {
|
|
for group := 0; group < int(groupCount); group++ {
|
|
for tag := 0; tag < int(tagsPerGroup); tag++ {
|
|
err := hlcl.AddTag(fmt.Sprintf("%d|%d", group+1, tag+1))
|
|
if err != nil {
|
|
return fmt.Errorf(
|
|
"Adding tag [%d] for group [%d]: %s",
|
|
tag+1, group+1, err,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
defaultAttr, err := hlcl.GetAttr("tags.by-name.default.index")
|
|
if err != nil {
|
|
// no default
|
|
log.Print("No 'default' tag, continuing...")
|
|
return nil
|
|
}
|
|
focus, err := config.GetFocusedTag()
|
|
if err != nil {
|
|
return fmt.Errorf("(removing default) getting current tag: %s", err)
|
|
}
|
|
def, err := strconv.ParseUint(defaultAttr, 10, 8)
|
|
if err != nil {
|
|
return fmt.Errorf("Bug? default index not uint: %s", err)
|
|
}
|
|
if uint8(def) == focus {
|
|
if err := hlcl.UseIndex(focus + 1); err != nil {
|
|
log.Warnf("Tried switching away from default: %s", err)
|
|
}
|
|
}
|
|
if err := hlcl.MergeTag("default"); err != nil {
|
|
return fmt.Errorf("merging default: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (command) Invoke(args ...string) error {
|
|
log.Print("begining herbstluftwm setup via hlctl")
|
|
log.Print("loading config")
|
|
cfg := config.Load()
|
|
if err := cfg.Validate(); err != nil {
|
|
return fmt.Errorf("validating config: %s", err)
|
|
}
|
|
if err := cfg.SetAll(); err != nil {
|
|
return fmt.Errorf("setting cfg values to hlwm: %s", err)
|
|
}
|
|
|
|
exec.Command("xsetroot", "-solid", "black").Run()
|
|
logError(hlcl.KeyUnbind())
|
|
|
|
log.Print("Adding tags")
|
|
logError(AddTags(cfg.Groups.Groups, cfg.Groups.Tags))
|
|
logError(groupctl.ResetActive())
|
|
|
|
for _, key := range Keybinds(cfg) {
|
|
key.Set()
|
|
}
|
|
log.Print("Resetting groups")
|
|
logError(groupctl.ResetActive())
|
|
|
|
log.Print("Theming...")
|
|
Theme(cfg)
|
|
|
|
log.Print("Rules")
|
|
SetRules()
|
|
|
|
log.Print("Unlocking")
|
|
if err := hlcl.Unlock(); err != nil {
|
|
return fmt.Errorf("failed unlock: %w", err)
|
|
}
|
|
|
|
svcctl.RestartServices(cfg.Services)
|
|
|
|
log.Print("Running panels")
|
|
return panelctl.Run()
|
|
}
|