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
15 changes: 14 additions & 1 deletion compositor/compositor.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,22 @@ func (c *Compositor) OutputCreated(o wlc.Output) bool {
return true
}

// OutputResolution is the callback triggered when output resolution changes.
func (c *Compositor) OutputResolution(o wlc.Output, from *wlc.Size, to *wlc.Size) {
c.layout.Arrange(c.layout.OutputByBackend(o))
}

// ViewRequestGeometry is the callback triggered when a new view geometry is
// requested. Note this callback must allways be be set (even if it's stubbed)
// otherwise wlc won't accept your geometry requests.
func (c *Compositor) ViewRequestGeometry(view wlc.View, geometry *wlc.Geometry) {
// stub intentionally to ignore geometry requests.
}

// ViewCreated is the callback triggered when a view is added by the backend.
func (c *Compositor) ViewCreated(v wlc.View) bool {
c.layout.NewView(c.ctx, v)
view := c.layout.NewView(c.ctx, v)
c.layout.Arrange(view.Parent())

// TODO: return false on failure
return true
Expand Down
12 changes: 12 additions & 0 deletions compositor/compositor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ func TestOutputCreated(t *testing.T) {
}
}

// Test OutputResolution cb.
func TestOutputResolution(t *testing.T) {
c := New(nil, nil, layout.Mock{})
c.OutputResolution(0, nil, nil)
}

// Test ViewRequestGeometry cb.
func TestViewRequestGeometry(t *testing.T) {
c := New(nil, nil, nil)
c.ViewRequestGeometry(0, nil)
}

// Test ViewCreated cb.
func TestViewCreated(t *testing.T) {
c := New(nil, nil, layout.Mock{})
Expand Down
13 changes: 13 additions & 0 deletions layout/i3/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,26 @@ func New() *Layout {
func (l *Layout) Arrange(start layout.Container) {
// TODO: implement with more than one view
switch c := start.(type) {
case *layout.Root:
l.Arrange(c.Focused())
case *layout.Output:
log.Debugf("OUTPUT VISIBILITY MASK %d", c.GetMask())
l.Arrange(c.Focused())
case *layout.Workspace:
l.Arrange(c.Focused())
case *layout.View:
c.SetMask(1) // TODO: don't set it here
pG := c.Parent().Geometry()
g := c.Geometry()
g.Origin = pG.Origin
g.Size = pG.Size
log.Debugf("Arranging view %s %dx%d (%d,%d)", c.Title(),
g.Size.W, g.Size.H, g.Origin.X, g.Origin.Y)
c.SetGeometry(0, *g)

// if c.Focused() == c {
c.Focus()
// }
}
}

Expand Down
10 changes: 10 additions & 0 deletions layout/i3/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ func (l *Layout) NewOutput(ctx context.Context, output backend.Output) {
l.NewWorkspace(ctx, o, name, num)
}

// OutputByBackend gets output container from backend output interface.
func (l *Layout) OutputByBackend(output backend.Output) layout.Container {
for _, child := range l.root.Children() {
if o, ok := child.(*layout.Output); ok && o.Output == output {
return child
}
}
return nil
}

// findNextWorkspace finds next available workspace name & number.
func findNextWorkspace(ws []*layout.Workspace, confWs map[uint]string) (string, uint) {
num := findAvailableWorkspaceNum(ws)
Expand Down
94 changes: 77 additions & 17 deletions layout/i3/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package i3

import (
"context"
"fmt"
"testing"

"github.com/mikkeloscar/flis/backend"
"github.com/mikkeloscar/flis/config"
"github.com/mikkeloscar/flis/layout"
wlc "github.com/mikkeloscar/go-wlc"
Expand All @@ -21,14 +23,76 @@ func TestNewOutput(t *testing.T) {
},
)

output := wlc.Output(0)
output := mockOutput(0)
layout := New()
layout.NewOutput(ctx, output)
}

type mockOutput int

func (o mockOutput) Focus() {}
func (o mockOutput) GetMask() uint32 { return 0 }
func (o mockOutput) GetMutableViews() []wlc.View { return nil }
func (o mockOutput) Name() string { return fmt.Sprintf("%d", o) }
func (o mockOutput) GetRenderer() wlc.Renderer { return wlc.NoRenderer }
func (o mockOutput) GetResolution() *wlc.Size { return nil }
func (o mockOutput) GetVirtualResolution() *wlc.Size { return nil }
func (o mockOutput) SetResolution(resolution wlc.Size, scale uint32) {}
func (o mockOutput) GetScale() uint32 { return 0 }
func (o mockOutput) GetSleep() bool { return false }
func (o mockOutput) GetViews() []wlc.View { return nil }
func (o mockOutput) ScheduleRender() {}
func (o mockOutput) SetMask(mask uint32) {}
func (o mockOutput) SetSleep(sleep bool) {}
func (o mockOutput) SetViews(views []wlc.View) bool { return false }

// TestOutputByBackend tests getting an output container from a backend output
// interface.
func TestOutputByBackend(t *testing.T) {
ctx := context.WithValue(
context.Background(),
"config",
&config.Config{
Workspaces: map[uint]string{
1: "one",
},
},
)

layout := New()

for i := 0; i < 5; i++ {
output := mockOutput(i)
layout.NewOutput(ctx, output)
}

for _, ti := range []struct {
output backend.Output
exists bool
}{
{
output: mockOutput(2),
exists: true,
},
{
output: mockOutput(10),
exists: false,
},
} {
c := layout.OutputByBackend(ti.output)
if c == nil && ti.exists {
t.Errorf("expected to find an output")
}

if c != nil && !ti.exists {
t.Errorf("did not expect to find an output")
}
}
}

// TestFindNextWorkspace tests finding the next available workspace.
func TestFindNextWorkspace(t *testing.T) {
tests := []struct {
for _, ti := range []struct {
current []*layout.Workspace
names map[uint]string
name string
Expand Down Expand Up @@ -64,23 +128,21 @@ func TestFindNextWorkspace(t *testing.T) {
"2",
2,
},
}

for _, test := range tests {
name, num := findNextWorkspace(test.current, test.names)
if name != test.name {
t.Errorf("expected to get workspace name '%s', got '%s'", test.name, name)
} {
name, num := findNextWorkspace(ti.current, ti.names)
if name != ti.name {
t.Errorf("expected to get workspace name '%s', got '%s'", ti.name, name)
}

if num != test.num {
t.Errorf("expected to get workspace num '%d', got '%d'", test.num, num)
if num != ti.num {
t.Errorf("expected to get workspace num '%d', got '%d'", ti.num, num)
}
}
}

// TestFindAvailableWorkspaceNum tests finding available workspace number.
func TestFindAvailableWorkspaceNum(t *testing.T) {
tests := []struct {
for _, ti := range []struct {
ws []*layout.Workspace
num uint
}{
Expand Down Expand Up @@ -108,12 +170,10 @@ func TestFindAvailableWorkspaceNum(t *testing.T) {
},
2,
},
}

for _, test := range tests {
num := findAvailableWorkspaceNum(test.ws)
if num != test.num {
t.Errorf("expected workspace num %d, got %d", test.num, num)
} {
num := findAvailableWorkspaceNum(ti.ws)
if num != ti.num {
t.Errorf("expected workspace num %d, got %d", ti.num, num)
}
}
}
3 changes: 2 additions & 1 deletion layout/i3/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import (

// NewView adds a new view to the layout. The view will be added to the
// currently focused container.
func (l *Layout) NewView(ctx context.Context, view backend.View) {
func (l *Layout) NewView(ctx context.Context, view backend.View) layout.Container {
// TODO: check if sibling or parent
parent := l.FocusedByType(ctx, layout.CWorkspace)
v := layout.NewView(view, parent)
parent.AddChild(v)
return v
}
6 changes: 5 additions & 1 deletion layout/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,18 @@ type Layout interface {
NewWorkspace(ctx context.Context, output *Output, name string, num uint)

// NewView initializes a new view.
NewView(ctx context.Context, view backend.View)
NewView(ctx context.Context, view backend.View) Container

// ArrangeRoot arranges the whole layout from the root and down.
ArrangeRoot()

// Arrange arranges a subbranch of the layout starting from the
// specified container and moving down the layout.
Arrange(start Container)

// OutputByBackend gets output container from backend output interface.
OutputByBackend(output backend.Output) Container
// ViewByBackend(output backend.Output) Container
}

// Get layout from context.
Expand Down
35 changes: 34 additions & 1 deletion layout/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/mikkeloscar/flis/backend"
wlc "github.com/mikkeloscar/go-wlc"
)

// Mock mocks the layout interface.
Expand All @@ -28,11 +29,43 @@ func (m Mock) NewOutput(ctx context.Context, output backend.Output) {}
func (m Mock) NewWorkspace(ctx context.Context, output *Output, name string, num uint) {}

// NewView mocks initializing a new view.
func (m Mock) NewView(ctx context.Context, view backend.View) {}
func (m Mock) NewView(ctx context.Context, view backend.View) Container {
return MockView{}
}

// ArrangeRoot mocks arranging the whole layout from the root and down.
func (m Mock) ArrangeRoot() {}

// Arrange mocks arranging a subbranch of the layout starting from the
// specified container and moving down the layout.
func (m Mock) Arrange(start Container) {}

// OutputByBackend gets output container from backend output interface.
func (m Mock) OutputByBackend(output backend.Output) Container { return nil }

// MockView mocks a view container.
type MockView struct{}

// Geometry mocks geometry of the container.
func (v MockView) Geometry() *wlc.Geometry { return nil }

// Children mocks children of the container.
func (v MockView) Children() []Container { return nil }

// Floating mocks floating children of the container.
func (v MockView) Floating() []Container { return nil }

// Focused mocks focused child of the container.
func (v MockView) Focused() Container { return nil }

// Parent mocks getting parent container of the container.
func (v MockView) Parent() Container { return nil }

// Visible mocks return the visibility state of the container.
func (v MockView) Visible() bool { return false }

// AddChild mocks adding a child container.
func (v MockView) AddChild(Container) {}

// Type mocks returning the type of the container.
func (v MockView) Type() ContainerType { return CView }
12 changes: 12 additions & 0 deletions layout/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,16 @@ func TestMock(t *testing.T) {
m.NewView(nil, nil)
m.ArrangeRoot()
m.Arrange(nil)
m.OutputByBackend(nil)

v := MockView{}

v.Geometry()
v.Children()
v.Floating()
v.Focused()
v.Parent()
v.Visible()
v.AddChild(nil)
v.Type()
}
17 changes: 17 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@ func main() {
comp := compositor.New(conf, &backend.WLC{}, i3.New())

wlc.SetOutputCreatedCb(comp.OutputCreated)
wlc.SetOutputResolutionCb(comp.OutputResolution)
wlc.SetViewCreatedCb(comp.ViewCreated)
wlc.SetViewRequestGeometryCb(comp.ViewRequestGeometry)
wlc.SetPointerMotionCb(comp.PointerMotion)
wlc.SetKeyboardKeyCb(comp.KeyboardKey)
wlc.LogSetHandler(wlcLogHandler)

if !wlc.Init() {
os.Exit(1)
Expand All @@ -68,3 +71,17 @@ func main() {
wlc.Run()
os.Exit(0)
}

func wlcLogHandler(typ wlc.LogType, msg string) {
format := "[WLC] %s"
switch typ {
case wlc.LogInfo:
log.Debugf(format, msg)
case wlc.LogWarn:
log.Warnf(format, msg)
case wlc.LogError:
log.Errorf(format, msg)
case wlc.LogWayland:
log.Debugf("[WLC - Wayland] %s", msg)
}
}