Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions conf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package main

import (
_ "embed"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
import (
"github.com/gonutz/w32/v2"
"gopkg.in/yaml.v3"
)

type KeyBinding struct {
// A repeated value of key modifiers.
// Valid values include:
// SHIFT, ALT, CTRL, WIN (SUPER/META).
Modifier []string `yaml: "modifier"`
// When this is set, this overrides Modifier
ModifierCode []int32
// Calculated bitwise OR result of modifiers
CombinedMod int32
// Valid values are:
// A - Z, 0 - 9, UP_ARROW, =, -
// Anything not covered here could be set directly via KeyCode
Key string `yaml: "key"`
// Automatically converted from Key.
// When this is set, it overrides Key.
KeyCode int32 `yaml: "key_code"`
// The feature in RectangleWin to bind to.
// Valid values:
// moveToTop
// moveToBottom
// moveToLeft
// moveToRight
// moveToTopLeft
// moveToTopRight
// moveToBottomLeft
// moveToBottomRight
// makeSmaller
// makeLarger
// makeFullHeight
//
BindFeature string `yaml: "bindfeature"`
}

type Configuration struct {
Keybindings []KeyBinding `yaml: "key_binding"`
}

// This mini config is returned if we can't load a valid file
// and cannot write the detailed example yaml config.example.yaml
// into the expected path at %HOME%
var DEFAULT_CONF = Configuration{
Keybindings: []KeyBinding{
{
Modifier: []string{"Ctrl", "Alt"},
Key: "UP_ARROW",
KeyCode: 0x26,
BindFeature: "moveToTop",
},
},
}

//go:embed config.example.yaml
var configExampleYaml []byte

// Expected config path at %HOME%/.config/RectangleWin/config.yaml
var DEFAULT_CONF_PATH_PREFIX = ".config/RectangleWin/"
var DEFAULT_CONF_NAME = "config.yaml"

func convertModifier(keyName string) (int32, error) {
switch strings.ToLower(keyName) {
case "ctrl":
return MOD_CONTROL, nil
case "alt":
return MOD_ALT, nil
case "shift":
return MOD_SHIFT, nil
case "win":
case "meta":
case "super":
return MOD_WIN, nil
default:
return 0, fmt.Errorf("invalid keyname: %s", keyName)
}
return 0, errors.New("unreachable")
}

func convertKeyCode(key string) (int32, error) {
k := strings.ToLower(key)
if len(k) == 1 {
if k[0] >= 'a' && k[0] <= 'z' {
return int32(k[0]) - 32, nil
}
if k[0] >= '0' && k[0] <= '9' {
return int32(k[0]), nil
}
}
switch k {
case "up_arrow":
return w32.VK_UP, nil
case "down_arrow":
return w32.VK_DOWN, nil
case "left_arrow":
return w32.VK_LEFT, nil
case "right_arrow":
return w32.VK_RIGHT, nil
case "-":
return 189, nil
case "=":
return 187, nil
default:
return 0, fmt.Errorf("Unknown key %s", key)
}
}

func bitwiseOr(nums []int32) int32 {
if len(nums) == 0 {
return 0
}
result := nums[0]
for _, n := range nums[1:] {
result |= n // bitwise OR
}
return result
}

func getValidConfigPathOrCreate() (string, error) {
homeDir := os.Getenv("HOME")
if homeDir == "" {
homeDir = os.Getenv("USERPROFILE")
}
if homeDir == "" {
// Give up generating a valid path.
// read or write the conf in current folder.
return DEFAULT_CONF_NAME, errors.New("Failed to find user home directory")
}
configDir := filepath.Join(homeDir, filepath.FromSlash(DEFAULT_CONF_PATH_PREFIX))
err := os.MkdirAll(configDir, 0755)
if err != nil {
fmt.Printf("Error creating directory under user's home folder: %s", err)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return the err actually, we wouldn't wanna ignore a failure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

// read or write the conf in current folder
return DEFAULT_CONF_NAME, fmt.Errorf("Failed to create folders under user's home directory: %s", configDir)
}
configPath := filepath.Join(configDir, DEFAULT_CONF_NAME)
return configPath, nil
}

func maybeDropExampleConfigFile(target string) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we should just fail/crash. you wanna do things like "ignore error and do this instead" in 1 place in the code, as close to the top of the main() as possible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you prefer to move the calling places to main.go, please?

// Check if the file exists, if not, create it with some content
if _, err := os.Stat(target); os.IsNotExist(err) {
// Create the file and write the sample content
err := ioutil.WriteFile(target, configExampleYaml, 0644)
if err != nil {
fmt.Println("Failed to create file created: %s %v", target, err)
}
fmt.Println("File created: %s", target)
}
}

func fetchConfiguration() Configuration {
// Create a Configuration file.
myConfig := Configuration{}

// Yaml feeder
configFilePath, err := getValidConfigPathOrCreate()
if err == nil {
maybeDropExampleConfigFile(configFilePath)
}
data, err := os.ReadFile(configFilePath)
if err != nil {
fmt.Printf("Failed to load config file at expected path %s\n", configFilePath)
// use the last-ditch config
return DEFAULT_CONF
}

if err := yaml.Unmarshal(data, &myConfig); err != nil {
showMessageBox("Failed to parse config file at %s.\n")
return DEFAULT_CONF
}

for i := range myConfig.Keybindings {
if len(myConfig.Keybindings[i].ModifierCode) == 0 {
for _, mod := range myConfig.Keybindings[i].Modifier {
if modCode, err := convertModifier(mod); err == nil {
myConfig.Keybindings[i].ModifierCode = append(myConfig.Keybindings[i].ModifierCode, modCode)
} else {
fmt.Printf("warn: invalid key name %s", mod)
continue
}
}
}
myConfig.Keybindings[i].CombinedMod = bitwiseOr(myConfig.Keybindings[i].ModifierCode)
if myConfig.Keybindings[i].KeyCode == 0 {
if key, err := convertKeyCode(myConfig.Keybindings[i].Key); err == nil {
myConfig.Keybindings[i].KeyCode = key
} else {
fmt.Printf("warn: invalid key string %s", myConfig.Keybindings[i].Key)
continue
}
}
}
return myConfig
}
68 changes: 68 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
keybindings:
- modifier:
- Ctrl
- Alt
key: UP_ARROW
bindfeature: moveToTop

- modifier:
- Ctrl
- Alt
key: DOWN_ARROW
bindfeature: moveToBottom

- modifier:
- Ctrl
- Alt
key: LEFT_ARROW
bindfeature: moveToLeft

- modifier:
- Ctrl
- Alt
key: RIGHT_ARROW
bindfeature: moveToRight

- modifier:
- Ctrl
- Alt
key: U
bindfeature: moveToTopLeft

- modifier:
- Ctrl
- Alt
key: I
bindfeature: moveToTopRight

- modifier:
- Ctrl
- Alt
key: J
bindfeature: moveToBottomLeft

- modifier:
- Ctrl
- Alt
key: K
bindfeature: moveToBottomRight

- modifier:
- Ctrl
- Alt
key: =
bindfeature: makeLarger

- modifier:
- Ctrl
- Alt
key: "-"
bindfeature: makeSmaller

- modifier:
- Ctrl
- Alt
- Shift
key: UP_ARROW
bindfeature: makeFullHeight

6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@ module github.com/ahmetb/RectangleWin
go 1.17

require (
github.com/apenwarr/fixconsole v0.0.0-20191012055117-5a9f6489cc29
github.com/getlantern/systray v1.1.0
github.com/gonutz/w32/v2 v2.2.2
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/apenwarr/w32 v0.0.0-20190407065021-aa00fece76ab // indirect
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
github.com/stretchr/testify v1.8.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
32 changes: 30 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/apenwarr/fixconsole v0.0.0-20191012055117-5a9f6489cc29 h1:muXWUcay7DDy1/hEQWrYlBy+g0EuwT70sBHg65SeUc4=
github.com/apenwarr/fixconsole v0.0.0-20191012055117-5a9f6489cc29/go.mod h1:JYWahgHer+Z2xbsgHPtaDYVWzeHDminu+YIBWkxpCAY=
github.com/apenwarr/w32 v0.0.0-20190407065021-aa00fece76ab h1:CMGzRRCjnD50RjUFSArBLuCxiDvdp7b8YPAcikBEQ+k=
github.com/apenwarr/w32 v0.0.0-20190407065021-aa00fece76ab/go.mod h1:nfFtvHn2Hgs9G1u0/J6LHQv//EksNC+7G8vXmd1VTJ8=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So=
Expand All @@ -18,13 +24,35 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gonutz/w32/v2 v2.2.2 h1:y6Y337TpuCXjYdFTq5p5NmcujEdAQiTB43kisovMk+0=
github.com/gonutz/w32/v2 v2.2.2/go.mod h1:MgtHx0AScDVNKyB+kjyPder4xIi3XAcHS6LDDU2DmdE=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading
Loading