diff --git a/breadcrumbs b/breadcrumbs deleted file mode 100644 index cf8cb1c..0000000 --- a/breadcrumbs +++ /dev/null @@ -1,33 +0,0 @@ -Tue Apr 17 01:56:21 EDT 2012 - -There's a pretty nasty problem when moving windows between monitors when -the monitors haven't different kinds of layouts active. Namely, if one monitor -is floating while the other is tiling, there are a few problems: - -1. When dragging a window from a tiling layout to a floating layout on another -monitor, the window is removed from the tiling layout---and in the process, its -geometry is "refreshed." This is desirable in that it will restore maximization -state if necessary, and most importantly, revert back to the decorations used -before tiling start. The only undesirable side effect is reverting the geometry -of the client. It's quite awkward *only* when manually moving the window, so I -think loadGeom will have to look and see if the client is in the middle of -moving or resizing. - -2. When using HeadFocusWithClient from a tiler to floating, the wrong state is -saved and reloaded. A possible solution to this is to allow geometry state -"copying." i.e., copy the "layout_before_tiling" to "monitor_switch", and load -that geometry after all is said and done. - - -Wed Apr 25 22:39:57 EDT 2012 - -So the problem is that when both montitors are tiling and we use -'HeadFocusWithClient', the client is removed from its current workspace and it -is automatically restored to 'before tiling' state. It is then added to the new -workspace as is, and since that new workspace is also tiling, its current state -on the old monitor is saved---but it is saved under the context of the new -monitor. This screws everything up. - -My brain isn't thinking too clearly, but I suspect that I may need to rethink -how clients are added and removed from workspaces. - diff --git a/client.go b/client.go index a711f6b..52ff9a9 100644 --- a/client.go +++ b/client.go @@ -39,7 +39,7 @@ type client struct { transientFor xgb.Id wmclass *icccm.WmClass - geomStore map[string]*clientGeom + stateStore map[string]*clientState promptStore map[string]*window frame Frame @@ -72,7 +72,7 @@ func newClient(id xgb.Id) *client { protocols: nil, transientFor: 0, wmclass: nil, - geomStore: make(map[string]*clientGeom), + stateStore: make(map[string]*clientState), promptStore: make(map[string]*window), frame: nil, frameNada: nil, diff --git a/client_geometry.go b/client_geometry.go index 3be346f..b45fd1e 100644 --- a/client_geometry.go +++ b/client_geometry.go @@ -5,8 +5,6 @@ import ( "github.com/BurntSushi/xgbutil/icccm" "github.com/BurntSushi/xgbutil/xrect" - - "github.com/BurntSushi/wingo/logger" ) func (c *client) GravitizeX(x int, gravity int) int { @@ -131,7 +129,7 @@ func (c *client) maximize() { return } if !c.maximized { - c.saveGeom("unmaximized") + c.saveState("unmaximized") } c.maximizeRaw() @@ -143,7 +141,7 @@ func (c *client) unmaximize() { } c.unmaximizeRaw() - c.loadGeom("unmaximized") + c.loadState("unmaximized") } func (c *client) maximizeRaw() { @@ -211,20 +209,28 @@ func (c *client) moveresizeNoValid(x, y, w, h int) { c.geomChangeNoValid(DoX|DoY|DoW|DoH, x, y, w, h) } -type clientGeom struct { +const ( + clientStateGeom = 1 << iota + clientStateFrame + clientStateHead +) + +var clientStateAll = clientStateGeom | clientStateFrame | clientStateHead + +type clientState struct { xrect.Rect maximized bool frame Frame headGeom xrect.Rect } -func (c *client) newClientGeom() *clientGeom { +func (c *client) newClientState() *clientState { var headGeom xrect.Rect = nil if c.workspace.visible() { headGeom = xrect.New(xrect.Pieces(c.workspace.headGeom())) } - return &clientGeom{ + return &clientState{ Rect: xrect.New(xrect.Pieces(c.Frame().Geom())), maximized: c.maximized, frame: c.frame, @@ -232,52 +238,49 @@ func (c *client) newClientGeom() *clientGeom { } } -func (c *client) saveGeom(key string) { - c.geomStore[key] = c.newClientGeom() +func (c *client) saveState(key string) { + c.stateStore[key] = c.newClientState() } -func (c *client) saveGeomTransients(key string) { +func (c *client) saveStateTransients(key string) { for _, c2 := range WM.clients { if c.transient(c2) && c2.workspace != nil && c2.workspace.id == c.workspace.id { - c2.saveGeom(key) + c2.saveState(key) } } - c.saveGeom(key) + c.saveState(key) } -func (c *client) saveGeomNoClobber(key string) { - if _, ok := c.geomStore[key]; !ok { - c.saveGeom(key) +func (c *client) saveStateNoClobber(key string) { + if _, ok := c.stateStore[key]; !ok { + c.saveState(key) } } -func (c *client) copyGeom(src, dest string) { - c.geomStore[dest] = c.geomStore[src] +func (c *client) copyState(src, dest string) { + c.stateStore[dest] = c.stateStore[src] } -func (c *client) copyGeomTransients(src, dest string) { +func (c *client) copyStateTransients(src, dest string) { for _, c2 := range WM.clients { if c.transient(c2) && c2.workspace != nil && c2.workspace.id == c.workspace.id { - c2.copyGeom(src, dest) + c2.copyState(src, dest) } } - c.copyGeom(src, dest) + c.copyState(src, dest) } -func (c *client) loadGeom(key string) { - if cgeom, ok := c.geomStore[key]; ok { - c.frameSet(cgeom.frame) +func (c *client) loadState(key string) { + if cgeom, ok := c.stateStore[key]; ok { newGeom := cgeom.Rect - // let's convert head geometry if need be. if c.workspace.visible() && cgeom.headGeom != nil && c.workspace.headGeom() != cgeom.headGeom { - logger.Debug.Println(c, cgeom.headGeom, c.workspace.headGeom()) newGeom = WM.headConvert(cgeom, cgeom.headGeom, c.workspace.headGeom()) } @@ -287,39 +290,47 @@ func (c *client) loadGeom(key string) { // "restore" state the client has saved. c.maximizeRaw() } else { - c.Frame().ConfigureFrame( - DoX|DoY|DoW|DoH, - newGeom.X(), newGeom.Y(), newGeom.Width(), newGeom.Height(), - 0, 0, false, true) + // Only reset this geometry if it isn't finishing a move/resize + if !c.frame.Moving() && !c.frame.Resizing() { + c.Frame().ConfigureFrame( + DoX|DoY|DoW|DoH, + newGeom.X(), newGeom.Y(), newGeom.Width(), newGeom.Height(), + 0, 0, false, true) + } } - delete(c.geomStore, key) + + // This comes last, otherwise we might be inspecting the wrong frame + // for information (like whether the client is moving/resizing). + c.frameSet(cgeom.frame) + + delete(c.stateStore, key) } } -func (c *client) loadGeomTransients(key string) { +func (c *client) loadStateTransients(key string, flags int) { for _, c2 := range WM.clients { if c.transient(c2) && c2.workspace != nil && c2.workspace.id == c.workspace.id { - c2.loadGeom(key) + c2.loadState(key) } } - c.loadGeom(key) + c.loadState(key) } -func (c *client) deleteGeom(key string) { - if _, ok := c.geomStore[key]; ok { - delete(c.geomStore, key) +func (c *client) deleteState(key string) { + if _, ok := c.stateStore[key]; ok { + delete(c.stateStore, key) } } -func (c *client) deleteGeomTransients(key string) { +func (c *client) deleteStateTransients(key string) { for _, c2 := range WM.clients { if c.transient(c2) && c2.workspace != nil && c2.workspace.id == c.workspace.id { - c2.deleteGeom(key) + c2.deleteState(key) } } - c.deleteGeom(key) + c.deleteState(key) } diff --git a/command.go b/command.go index afa9c7d..81be96d 100644 --- a/command.go +++ b/command.go @@ -304,22 +304,30 @@ func cmdHeadFocus(withClient bool, args ...string) func() { // proper geometry and not its geometry while in a tiling // layout. if c.layout().floating() { - c.saveGeomTransients("monitor_switch") + c.saveStateTransients("monitor_switch") } else { - c.copyGeomTransients( + c.copyStateTransients( "layout_before_tiling", "monitor_switch") } wrk.add(c) // If the new layout is floating, then load geometry - // in the new monitor. Otherwise, trash the geometry. + // in the new monitor. + // Otherwise... things get tricky. We copy the monitor_switch + // state into "layout_before_tiling." This is because the + // monitor_switch state has the desirable "non-tiling" state. if c.layout().floating() { - c.loadGeomTransients("monitor_switch") + c.loadStateTransients("monitor_switch", clientStateAll) } else { - c.deleteGeomTransients("monitor_switch") + c.copyStateTransients( + "monitor_switch", "layout_before_tiling") + c.deleteStateTransients("monitor_switch") } - // c.Raise() + // Finally, if this window is active, raise it. + if c.state == StateActive { + c.Raise() + } }) } wrk.activate(true, greedy) diff --git a/frame_temp.go b/frame_temp.go index d939340..49e3e54 100644 --- a/frame_temp.go +++ b/frame_temp.go @@ -36,11 +36,12 @@ func frameMoveStep(f Frame, rx, ry, ex, ey int) { } func frameMoveEnd(f Frame, rx, ry, ex, ey int) { + FrameReset(f) + WM.headChoose(f.Client(), f.Geom()) + moving := f.MovingState() moving.moving = false moving.lastRootX, moving.lastRootY = 0, 0 - FrameReset(f) - WM.headChoose(f.Client(), f.Geom()) } func frameResizeBegin(f Frame, direction uint32, @@ -208,6 +209,14 @@ func frameResizeStep(f Frame, rx, ry, ex, ey int) { } func frameResizeEnd(f Frame, rx, ry, ex, ey int) { + // If windows are really slow to respond/resize, this may be necessary. + // If we don't, it's possible for the client to be out of whack inside + // the decorations. + // Example: Libreoffice in Xephyr. Try resizing it with the mouse and + // releasing the mouse button really quickly. + FrameReset(f) + WM.headChoose(f.Client(), f.Geom()) + // just zero out the resizing state resizing := f.ResizingState() resizing.resizing = false @@ -216,12 +225,4 @@ func frameResizeEnd(f Frame, rx, ry, ex, ey int) { resizing.width, resizing.height = 0, 0 resizing.xs, resizing.ys = false, false resizing.ws, resizing.hs = false, false - - // If windows are really slow to respond/resize, this may be necessary. - // If we don't, it's possible for the client to be out of whack inside - // the decorations. - // Example: Libreoffice in Xephyr. Try resizing it with the mouse and - // releasing the mouse button really quickly. - FrameReset(f) - WM.headChoose(f.Client(), f.Geom()) } diff --git a/layout_storage.go b/layout_storage.go index e155efd..bc0e5ba 100644 --- a/layout_storage.go +++ b/layout_storage.go @@ -85,7 +85,7 @@ func (ls *layoutStore) remove(c *client) bool { } if removed { - c.loadGeom("layout_before_tiling") + c.loadState("layout_before_tiling") } return removed diff --git a/layout_tile.go b/layout_tile.go index f375140..5c33503 100644 --- a/layout_tile.go +++ b/layout_tile.go @@ -48,7 +48,7 @@ func (ly *tileVertical) place() { mw = headGeom.Width() } for i, item := range ly.store.masters { - item.client.saveGeomNoClobber("layout_before_tiling") + item.client.saveStateNoClobber("layout_before_tiling") item.client.FrameBorders() item.client.moveresizeNoValid(mx, headGeom.Y()+i*mh, mw, mh) } @@ -60,7 +60,7 @@ func (ly *tileVertical) place() { sy := headGeom.Y() for _, item := range ly.store.slaves { sh := int(float64(headGeom.Height()) * item.proportion) - item.client.saveGeomNoClobber("layout_before_tiling") + item.client.saveStateNoClobber("layout_before_tiling") item.client.FrameBorders() item.client.moveresizeNoValid(sx, sy, sw, sh) sy += sh @@ -76,10 +76,10 @@ func (ly *tileVertical) unplace() { // with lots of windows appear quicker :-) for _, c := range WM.stack { if i := ly.store.mFindClient(c); i > -1 { - ly.store.masters[i].client.loadGeom("layout_before_tiling") + ly.store.masters[i].client.loadState("layout_before_tiling") } if i := ly.store.sFindClient(c); i > -1 { - ly.store.slaves[i].client.loadGeom("layout_before_tiling") + ly.store.slaves[i].client.loadState("layout_before_tiling") } } } diff --git a/state_head.go b/state_head.go index 627fe12..475141f 100644 --- a/state_head.go +++ b/state_head.go @@ -184,6 +184,11 @@ func (wm *state) headsApplyStruts() { c.maximize() } } + + // Now make workspaces that are tiling fix themselves. + for _, wrk := range WM.workspaces { + wrk.tile() + } } // fillWorkspaces is used when there are more heads than there are workspaces. diff --git a/state_workspace.go b/state_workspace.go index 38c8e34..90129c6 100644 --- a/state_workspace.go +++ b/state_workspace.go @@ -273,7 +273,7 @@ func (wrk *workspace) hide() { for _, c := range WM.clients { if c.workspace.id == wrk.id { if c.layout().floating() { - c.saveGeom("workspace_switch") + c.saveState("workspace_switch") } c.Unmap() } @@ -285,7 +285,7 @@ func (wrk *workspace) show() { for _, c := range WM.stack { if c.workspace.id == wrk.id { if c.layout().floating() { - c.loadGeom("workspace_switch") + c.loadState("workspace_switch") } c.Map() }