diff --git a/go.mod b/go.mod index dfe161b..1e122f3 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.18 require ( deedles.dev/wlr v0.0.0-20230609170122-c83b1a7b6114 deedles.dev/ximage v0.0.0-20230609170106-f29200600f32 - golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 - golang.org/x/image v0.7.0 + golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 + golang.org/x/image v0.11.0 ) -require golang.org/x/text v0.9.0 // indirect +require golang.org/x/text v0.12.0 // indirect diff --git a/go.sum b/go.sum index 19168e1..236305d 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ deedles.dev/ximage v0.0.0-20230609170106-f29200600f32/go.mod h1:1avHO+eEHmPAahfD github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw= -golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg= +golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= +golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= +golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -31,8 +31,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/input.go b/input.go index e8c9a36..9b16322 100644 --- a/input.go +++ b/input.go @@ -72,7 +72,7 @@ func (server *Server) onKeyboardKeyReleased(kb *Keyboard, code uint32, update bo func (server *Server) onCursorMotion(dev wlr.Pointer, t time.Time, dx, dy float64) { server.cursor.Move(dev.Base(), dx, dy) - m, ok := server.inputMode.(CursorMover) + m, ok := server.mode.(CursorMover) if ok { m.CursorMoved(server, t) } @@ -81,7 +81,7 @@ func (server *Server) onCursorMotion(dev wlr.Pointer, t time.Time, dx, dy float6 func (server *Server) onCursorMotionAbsolute(dev wlr.Pointer, t time.Time, x, y float64) { server.cursor.WarpAbsolute(dev.Base(), x, y) - m, ok := server.inputMode.(CursorMover) + m, ok := server.mode.(CursorMover) if ok { m.CursorMoved(server, t) } @@ -90,12 +90,12 @@ func (server *Server) onCursorMotionAbsolute(dev wlr.Pointer, t time.Time, x, y func (server *Server) onCursorButton(dev wlr.Pointer, t time.Time, b wlr.CursorButton, state wlr.ButtonState) { switch state { case wlr.ButtonPressed: - m, ok := server.inputMode.(CursorButtonPresser) + m, ok := server.mode.(CursorButtonPresser) if ok { m.CursorButtonPressed(server, dev, b, t) } case wlr.ButtonReleased: - m, ok := server.inputMode.(CursorButtonReleaser) + m, ok := server.mode.(CursorButtonReleaser) if ok { m.CursorButtonReleased(server, dev, b, t) } @@ -111,7 +111,7 @@ func (server *Server) onCursorFrame() { } func (server *Server) onRequestCursor(client wlr.SeatClient, surface wlr.Surface, serial uint32, hotspotX, hotspotY int32) { - m, ok := server.inputMode.(CursorRequester) + m, ok := server.mode.(CursorRequester) if !ok { return } diff --git a/mode.go b/mode.go index 799b8f8..37c5d36 100644 --- a/mode.go +++ b/mode.go @@ -9,7 +9,7 @@ import ( "golang.org/x/exp/slices" ) -type InputMode interface{} +type Mode interface{} type inputModeNormal struct { inView bool @@ -18,7 +18,7 @@ type inputModeNormal struct { func (server *Server) startNormal() { server.setCursor("left_ptr") - server.inputMode = &inputModeNormal{} + server.mode = &inputModeNormal{} } func (m *inputModeNormal) CursorMoved(server *Server, t time.Time) { @@ -108,7 +108,7 @@ func (server *Server) startMove(view *View) { server.focusView(view, view.Surface()) cc := server.cursorCoords() - server.inputMode = &inputModeMove{ + server.mode = &inputModeMove{ view: view, off: cc.Sub(view.Coords), } @@ -163,7 +163,7 @@ func (server *Server) startBorderResize(view *View, edges wlr.Edges) { func (server *Server) startBorderResizeFrom(view *View, edges wlr.Edges, from geom.Rect[float64]) { view.SetResizing(true) server.focusView(view, view.Surface()) - server.inputMode = &inputModeBorderResize{ + server.mode = &inputModeBorderResize{ view: view, edges: edges, cur: from, @@ -260,7 +260,7 @@ func (server *Server) startMenu(m *Menu, btn wlr.CursorButton) { btn: btn, } mode.CursorMoved(server, time.Now()) - server.inputMode = &mode + server.mode = &mode } func (m *inputModeMenu) CursorMoved(server *Server, t time.Time) { @@ -288,7 +288,7 @@ type inputModeSelectView struct { func (server *Server) startSelectView(b wlr.CursorButton, then func(*View)) { server.setCursor("hand1") - server.inputMode = &inputModeSelectView{ + server.mode = &inputModeSelectView{ startBtn: b, then: then, } @@ -317,7 +317,7 @@ type inputModeResize struct { func (server *Server) startResize(view *View) { server.setCursor("top_left_corner") - server.inputMode = &inputModeResize{ + server.mode = &inputModeResize{ view: view, } } @@ -383,7 +383,7 @@ type inputModeNew struct { func (server *Server) startNew() { server.setCursor("top_left_corner") - server.inputMode = &inputModeNew{} + server.mode = &inputModeNew{} } func (m *inputModeNew) CursorMoved(server *Server, t time.Time) { diff --git a/render.go b/render.go index cffd3b3..3bb5361 100644 --- a/render.go +++ b/render.go @@ -25,6 +25,12 @@ func (server *Server) onFrame(out *Output) { defer server.renderer.End() server.renderer.Clear(ColorBackground) + + if server.overview { + server.renderOverview(out) + return + } + server.renderBG(out) server.renderLayer(out, wlr.LayerShellV1LayerBackground) server.renderLayer(out, wlr.LayerShellV1LayerBottom) @@ -39,6 +45,18 @@ func (server *Server) onFrame(out *Output) { server.renderCursor(out) } +func (server *Server) renderOverview(out *Output) { + ob := server.outputTilingBounds(out).Inset(2 * WindowBorder) + + tiles := make([]geom.Rect[float64], server.numViews()) + geom.TileRows(tiles, ob, 4) + + for i, view := range server.views { + b := view.Bounds().FitTo(tiles[i].Size()).CenterAt(tiles[i].Center()).Inset(2 * WindowBorder) + server.renderViewTo(out, view, b) + } +} + func (server *Server) renderBG(out *Output) { if !server.bg.Valid() { return @@ -75,13 +93,17 @@ func (server *Server) renderViews(out *Output) { } func (server *Server) renderView(out *Output, view *View) { + server.renderViewTo(out, view, view.Bounds()) +} + +func (server *Server) renderViewTo(out *Output, view *View, to geom.Rect[float64]) { if !view.CSD { - server.renderViewBorder(out, view) + server.renderViewBorder(out, view, to) } - server.renderViewSurfaces(out, view) + server.renderViewSurfaces(out, view, to) } -func (server *Server) renderViewBorder(out *Output, view *View) { +func (server *Server) renderViewBorder(out *Output, view *View, bounds geom.Rect[float64]) { color := ColorInactiveBorder if view.Activated() { color = ColorActiveBorder @@ -90,14 +112,18 @@ func (server *Server) renderViewBorder(out *Output, view *View) { color = ColorSelectionBox } - r := view.Bounds().Inset(-WindowBorder) + r := bounds.Inset(-WindowBorder) server.renderRectBorder(out, geom.RConv[float64](r), color) } -func (server *Server) renderViewSurfaces(out *Output, view *View) { +func (server *Server) renderViewSurfaces(out *Output, view *View, r geom.Rect[float64]) { + bounds := view.Bounds() + view.ForEachSurface(func(s wlr.Surface, x, y int) { p := geom.Pt(x, y) - server.renderSurface(out, s, geom.PConv[int](view.Coords).Add(p)) + sb := geom.RConv[float64](surfaceBounds(s).Add(p)).Add(bounds.Min) + + server.renderSurface(out, s, geom.RConv[int](sb.PropShift(r, bounds))) }) } @@ -124,14 +150,13 @@ func (server *Server) renderSelectionBox(out *Output, r geom.Rect[float64]) { server.renderer.RenderRect(r.Inset(WindowBorder).ImageRect(), ColorSelectionBackground, out.Output.TransformMatrix()) } -func (server *Server) renderSurface(out *Output, s wlr.Surface, p geom.Point[int]) { +func (server *Server) renderSurface(out *Output, s wlr.Surface, r geom.Rect[int]) { texture := s.GetTexture() if !texture.Valid() { wlr.Log(wlr.Error, "invalid texture for surface") return } - r := surfaceBounds(s).Add(geom.PConv[int](p)) tr := s.Current().Transform().Invert() m := wlr.ProjectBoxMatrix(r.ImageRect(), tr, 0, out.Output.TransformMatrix()) @@ -156,7 +181,7 @@ func (server *Server) renderStatusBar() { } func (server *Server) renderMode(out *Output) { - m, ok := server.inputMode.(Framer) + m, ok := server.mode.(Framer) if !ok { return } diff --git a/server.go b/server.go index 0eebda8..420b0ae 100644 --- a/server.go +++ b/server.go @@ -18,6 +18,7 @@ var ( "Move", "Close", "Hide", + "Overview", } systemMenuText = []string{ @@ -62,7 +63,8 @@ type Server struct { statusBar *StatusBar - inputMode InputMode + mode Mode + overview bool onNewOutputListener wlr.Listener onNewInputListener wlr.Listener @@ -150,6 +152,9 @@ func (server *Server) initMainMenu() { server.onMainMenuMove, server.onMainMenuClose, server.onMainMenuHide, + func() { + server.overview = !server.overview + }, } items := make([]*MenuItem, 0, len(mainMenuText)) diff --git a/view.go b/view.go index 7e58648..4a9617f 100644 --- a/view.go +++ b/view.go @@ -109,7 +109,7 @@ func (p *Popup) Release() { } func (server *Server) targetView() *View { - m, ok := server.inputMode.(ViewTargeter) + m, ok := server.mode.(ViewTargeter) if !ok { return nil } @@ -599,3 +599,7 @@ func (server *Server) updateTitles() { } server.statusBar.SetTitle(server.renderer, focusedTitle) } + +func (server *Server) numViews() int { + return len(server.views) + len(server.tiled) + len(server.hidden) +}