diff --git a/compositor/compositor.go b/compositor/compositor.go index ff613ab..106ec52 100644 --- a/compositor/compositor.go +++ b/compositor/compositor.go @@ -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 diff --git a/compositor/compositor_test.go b/compositor/compositor_test.go index 62b487c..0904b5c 100644 --- a/compositor/compositor_test.go +++ b/compositor/compositor_test.go @@ -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{}) diff --git a/layout/i3/layout.go b/layout/i3/layout.go index a84fb3c..9c121d2 100644 --- a/layout/i3/layout.go +++ b/layout/i3/layout.go @@ -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() + // } } } diff --git a/layout/i3/output.go b/layout/i3/output.go index cf8c9b8..f623b0e 100644 --- a/layout/i3/output.go +++ b/layout/i3/output.go @@ -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) diff --git a/layout/i3/output_test.go b/layout/i3/output_test.go index cec176f..957e774 100644 --- a/layout/i3/output_test.go +++ b/layout/i3/output_test.go @@ -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" @@ -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 @@ -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 }{ @@ -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) } } } diff --git a/layout/i3/view.go b/layout/i3/view.go index e58cc5f..cbe856b 100644 --- a/layout/i3/view.go +++ b/layout/i3/view.go @@ -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 } diff --git a/layout/layout.go b/layout/layout.go index 1fbc0ef..721a351 100644 --- a/layout/layout.go +++ b/layout/layout.go @@ -41,7 +41,7 @@ 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() @@ -49,6 +49,10 @@ type Layout interface { // 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. diff --git a/layout/mock.go b/layout/mock.go index fc441f4..650c072 100644 --- a/layout/mock.go +++ b/layout/mock.go @@ -4,6 +4,7 @@ import ( "context" "github.com/mikkeloscar/flis/backend" + wlc "github.com/mikkeloscar/go-wlc" ) // Mock mocks the layout interface. @@ -28,7 +29,9 @@ 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() {} @@ -36,3 +39,33 @@ 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 } diff --git a/layout/mock_test.go b/layout/mock_test.go index 49e4e59..fe1c83c 100644 --- a/layout/mock_test.go +++ b/layout/mock_test.go @@ -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() } diff --git a/main.go b/main.go index fb43b0b..eb4bc8a 100644 --- a/main.go +++ b/main.go @@ -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) @@ -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) + } +}