Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
vcraescu committed Dec 30, 2018
0 parents commit 7d24beb
Show file tree
Hide file tree
Showing 11 changed files with 1,088 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
config.json
release.sh
.idea
dist
29 changes: 29 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
builds:
-
env:
- CGO_ENABLED=0
binary: rescreen
goos:
- linux
goarch:
- amd64
- i386
main: ./cmd/rescreen/main.go
before:
hooks:
- go mod download
archive:
replacements:
linux: Linux
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
language: go
env:
- GO111MODULE=on
go:
- 1.11.x
before_install:
- go get github.com/mattn/goveralls
script:
- $GOPATH/bin/goveralls -service=travis-ci
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# rescreen [![Build Status](https://travis-ci.com/vcraescu/grandr.svg?branch=master)](https://travis-ci.com/vcraescu/grandr) [![Coverage Status](https://coveralls.io/repos/github/vcraescu/rescreen/badge.svg?branch=master)](https://coveralls.io/github/vcraescu/rescreen?branch=master)

Configure screen layout

**config.json**
```json
{
"layout": [
"HDMI-0", "eDP-1-1", 0, 0,
0, 0, 0, 0
],
"monitors": {
"HDMI-0": {
"scale": 1.333,
"primary": true
}
}
}
```

## Command

`rescreen /path/to/config.json`

If no config file path is given, it will look up for a config.json inside current folder.

#### Dry run

`rescreen --dry-run /path/to/config.json`

It will just display the commands which will be run under the hood.
106 changes: 106 additions & 0 deletions cmd/rescreen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package main

import (
"errors"
"fmt"
"github.com/logrusorgru/aurora"
"github.com/vcraescu/go-xrandr"
"github.com/vcraescu/rescreen/config"
"github.com/vcraescu/rescreen/layout"
"gopkg.in/urfave/cli.v1"
"os"
"strings"
)

const version = "0.0.1"

var dryRun bool

func main() {
app := cli.NewApp()
app.Name = "rescreen"
app.Usage = "Configure screen layout"
app.UsageText = "rescreen [command options] config-file"
app.ArgsUsage = "Config file"
app.Version = version
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "dry-run",
Usage: "Just print the xrandr commands",
Destination: &dryRun,
},
}
app.Action = doAction

err := app.Run(os.Args)
if err != nil {
fmt.Println(aurora.Red(err))
os.Exit(1)
}
}

func doAction(c *cli.Context) error {
var configPath string
if c.NArg() == 0 {
return errors.New("config file path is mandatory")
}

configPath = c.Args().First()
cfg, err := config.LoadFile(configPath)
if err != nil {
return err
}

screens, err := xrandr.GetScreens()
if err != nil {
return err
}

lt, err := layout.New(cfg, screens)
if err != nil {
return err
}

cmd := xrandr.
Command().
DPI(lt.DPI).
ScreenSize(lt.Resolution)

for _, node := range lt.Nodes {
ocmd := cmd.
Output(node.Monitor).
Scale(node.Scale).
SetPrimary(node.Primary).
Position(node.Position)

if node.Right != nil {
ocmd = ocmd.LeftOf(node.Right.Monitor)
}

cmd = ocmd.EndOutput()
}

if !dryRun {
if err := cmd.Run(); err != nil {
return err
}

return nil
}

return execDryRun(cmd)
}

func execDryRun(cmd xrandr.CommandBuilder) error {
fmt.Println(aurora.Bold(aurora.Black(aurora.BgGreen(" DRY RUN "))))
cmds, err := cmd.RunnableCommands()
if err != nil {
return err
}

for _, cmd := range cmds {
fmt.Println(aurora.Green(strings.Join(cmd.Args, " ")))
}

return nil
}
139 changes: 139 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package config

import (
"encoding/json"
"fmt"
"io/ioutil"
)

const layoutCols = 4
const layoutRows = 4

type Config struct {
Layout LayoutConfig
Monitors MonitorsConfig `json:"monitors"`
}

type MonitorConfig struct {
Scale float32 `json:"scale"`
Primary bool `json:"primary"`
}

type MonitorsConfig map[string]MonitorConfig

type LayoutConfig []string

func (c Config) validate() error {
err := c.Layout.validate()
if err != nil {
return fmt.Errorf("invalid layout config: %s", err)
}

return nil
}

func (l LayoutConfig) Size() (int, int) {
return layoutRows, layoutCols
}

func (l LayoutConfig) validate() error {
u := make(map[string]bool)
for _, ID := range l {
if ID == "" {
continue
}

_, ok := u[ID]
if ok {
return fmt.Errorf(`"%s" appears twice`, ID)
}

u[ID] = true
}

if len(u) == 0 {
return fmt.Errorf("empty layout")
}

return nil
}

func (l LayoutConfig) Row(index int) ([]string, error) {
rowsCount, colsCount := l.Size()

if index >= rowsCount || index < 0 {
return nil, fmt.Errorf("index must be between 0 and %d", rowsCount-1)
}

var rc int
row := make([]string, colsCount)
for i, v := range l {
ci := i % colsCount
row[ci] = v
if ci != colsCount-1 {
continue
}

if rc == index {
return row, nil
}

rc++
row = make([]string, colsCount)
}

return row, nil
}

func (l LayoutConfig) RowsCount() int {
c := len(l)
if c == 0 {
return 0
}

return c / layoutCols
}

func (l LayoutConfig) Matrix() [][]string {
matrix := make([][]string, l.RowsCount())
for i := 0; i < l.RowsCount(); i++ {
row, err := l.Row(i)
if err != nil {
panic(err)
}

matrix[i] = row
}

return matrix
}

func (mc MonitorConfig) IsScaled() bool {
return mc.Scale > 0
}

func (mc MonitorConfig) Scaling() float32 {
if !mc.IsScaled() {
return 1
}

return mc.Scale
}

func LoadFile(filename string) (Config, error) {
var cfg Config
b, err := ioutil.ReadFile(filename)
if err != nil {
return cfg, err
}

json.Unmarshal(b, &cfg)

err = cfg.validate()
if err != nil {
return cfg, err
}

return cfg, nil
}

8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/vcraescu/rescreen

require (
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e
github.com/stretchr/testify v1.2.2
github.com/vcraescu/go-xrandr v0.0.0-20181229110639-d274803e375e
gopkg.in/urfave/cli.v1 v1.20.0
)
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
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/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/vcraescu/go-xrandr v0.0.0-20181229110639-d274803e375e h1:UOCgsYTSQbl5SOxRDUWPwDimEhBg0F2zeBlYIKfjVEs=
github.com/vcraescu/go-xrandr v0.0.0-20181229110639-d274803e375e/go.mod h1:LVPmVEv6GKVAswrHXu0Fuhsg7YZwcKQS78uqLLdvHEA=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
10 changes: 10 additions & 0 deletions layout/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package layout

var (
FindLayoutNodeById = findLayoutNodeByID
CalculateRescaledNodeResolution = calculateRescaledNodeResolution
CalculateNodeResolution = calculateNodeResolution
CalculateNodePosition = calculateNodePosition
CalculateLayoutResolution = calculateLayoutResolution
CalculateDPI = calculateDPI
)
Loading

0 comments on commit 7d24beb

Please sign in to comment.