func (c *Client) newClientState() clientState { s := clientState{ geom: xrect.New(xrect.Pieces(c.frame.Geom())), headGeom: nil, frame: c.frame, maximized: c.maximized, } if c.workspace.IsVisible() { s.headGeom = xrect.New(xrect.Pieces(c.workspace.HeadGeom())) } return s }
func (c *Client) maybeInitPlace(presumedWorkspace workspace.Workspacer) { // This is a hack. Before a client gets sucked into some layout, we // always want to have some floating state to fall back on to. However, // by the time we're "allowed" to save the client's state, it will have // already been placed in the hands of some layout---which may or may // not be floating. So we inject our own state forcefully here. defer func() { wrk := presumedWorkspace if wrk.IsVisible() { c.states["last-floating"] = clientState{ geom: xrect.New(xrect.Pieces(c.frame.Geom())), headGeom: xrect.New(xrect.Pieces(wrk.HeadGeom())), frame: c.frame, maximized: c.maximized, } } }() // Any client that isn't normal doesn't get placed. // Let it do what it do, baby. if c.PrimaryType() != TypeNormal { return } // If it's sticky, let it do what it do. if _, ok := presumedWorkspace.(*workspace.Sticky); ok { return } // Transients never get placed. if c.transientFor != nil { return } // If a user/program position is specified, do not place. if c.nhints.Flags&icccm.SizeHintUSPosition > 0 || c.nhints.Flags&icccm.SizeHintPPosition > 0 { return } // We're good, do a placement unless we're already mapped or on a // hidden workspace.. if !presumedWorkspace.IsVisible() || !c.isAttrsUnmapped() { return } w := presumedWorkspace.(*workspace.Workspace) w.LayoutFloater().InitialPlacement(c) }
func (c *Client) DragMoveBegin(rx, ry, ex, ey int) { f := c.frame moving := f.MovingState() moving.Moving = true moving.RootX, moving.RootY = rx, ry // call for side-effect; makes sure parent window has a valid geometry f.Parent().Geometry() // unmax! c.EnsureUnmax() c.dragGeom = xrect.New(xrect.Pieces(f.Geom())) }
// Convert takes a source and a destination rect, along with a rect // in the source's rectangle, and returns a new rect translated into the // destination rect. func Convert(rect, src, dest xrect.Rect) xrect.Rect { nx, ny, nw, nh := xrect.Pieces(rect) rectRatio := func(r xrect.Rect) float64 { return float64(r.Width()) / float64(r.Height()) } ratio := rectRatio(dest) / rectRatio(src) nx = int(ratio*float64(nx-src.X())) + dest.X() ny = int(ratio*float64(ny-src.Y())) + dest.Y() // XXX: Allow window scaling as a config option. return xrect.New(nx, ny, nw, nh) }
func (c *Client) DragMoveBegin(rx, ry, ex, ey int) bool { if c.IsMaximized() { return false } f := c.frame moving := f.MovingState() moving.Moving = true moving.RootX, moving.RootY = rx, ry // call for side-effect; makes sure parent window has a valid geometry f.Parent().Geometry() c.dragGeom = xrect.New(xrect.Pieces(f.Geom())) return true }
func (c *Client) DragResizeBegin(direction uint32, rx, ry, ex, ey int) (bool, xproto.Cursor) { if c.IsMaximized() { return false, 0 } f := c.frame // call for side-effect; makes sure parent window has a valid geometry f.Parent().Geometry() resizing := f.ResizingState() dir := direction w, h := f.Geom().Width(), f.Geom().Height() // If we aren't forcing a direction, we need to infer it based on // where the mouse is in the window. // (ex, ey) is the position of the mouse. // We basically split the window into something like a tic-tac-toe board: // ------------------------- // | | | | // | A | | F | // | | D | | // --------- |-------- // | | | | // | B |-------| G | // | | | | // --------- |-------- // | | E | | // | C | | H | // | | | | // ------------------------- // Where A, B, C correspond to 'ex < w / 3' // and F, G, H correspond to 'ex > w * 2 / 3' // and D and E correspond to 'ex >= w / 3 && ex <= w * 2 / 3' // The direction is not only important for assigning which cursor to display // (where each of the above blocks gets its own cursor), but it is also // important for choosing which parts of the geometry to change. // For example, if the mouse is in 'H', then the width and height could // be changed, but x and y cannot. Conversely, if the mouse is in 'A', // all parts of the geometry can change: x, y, width and height. // As one last example, if the mouse is in 'D', only y and height of the // window can change. if dir == ewmh.Infer { if ex < w/3 { switch { case ey < h/3: dir = ewmh.SizeTopLeft case ey > h*2/3: dir = ewmh.SizeBottomLeft default: // ey >= h / 3 && ey <= h * 2 / 3 dir = ewmh.SizeLeft } } else if ex > w*2/3 { switch { case ey < h/3: dir = ewmh.SizeTopRight case ey > h*2/3: dir = ewmh.SizeBottomRight default: // ey >= h / 3 && ey <= h * 2 / 3 dir = ewmh.SizeRight } } else { // ex >= w / 3 && ex <= w * 2 / 3 switch { case ey < h/2: dir = ewmh.SizeTop default: // ey >= h / 2 dir = ewmh.SizeBottom } } } // Find the right cursor var cursor xproto.Cursor = 0 switch dir { case ewmh.SizeTop: cursor = cursors.TopSide case ewmh.SizeTopRight: cursor = cursors.TopRightCorner case ewmh.SizeRight: cursor = cursors.RightSide case ewmh.SizeBottomRight: cursor = cursors.BottomRightCorner case ewmh.SizeBottom: cursor = cursors.BottomSide case ewmh.SizeBottomLeft: cursor = cursors.BottomLeftCorner case ewmh.SizeLeft: cursor = cursors.LeftSide case ewmh.SizeTopLeft: cursor = cursors.TopLeftCorner } // Save some state that we'll need when computing a window's new geometry resizing.Resizing = true resizing.RootX, resizing.RootY = rx, ry resizing.X, resizing.Y = f.Geom().X(), f.Geom().Y() resizing.Width, resizing.Height = f.Geom().Width(), f.Geom().Height() // Our geometry calculations depend upon which direction we're resizing. // Namely, the direction determines which parts of the geometry need to // be modified. Pre-compute those parts (i.e., x, y, width and/or height) resizing.Xs = dir == ewmh.SizeLeft || dir == ewmh.SizeTopLeft || dir == ewmh.SizeBottomLeft resizing.Ys = dir == ewmh.SizeTop || dir == ewmh.SizeTopLeft || dir == ewmh.SizeTopRight resizing.Ws = dir == ewmh.SizeTopLeft || dir == ewmh.SizeTopRight || dir == ewmh.SizeRight || dir == ewmh.SizeBottomRight || dir == ewmh.SizeBottomLeft || dir == ewmh.SizeLeft resizing.Hs = dir == ewmh.SizeTopLeft || dir == ewmh.SizeTop || dir == ewmh.SizeTopRight || dir == ewmh.SizeBottomRight || dir == ewmh.SizeBottom || dir == ewmh.SizeBottomLeft c.dragGeom = xrect.New(xrect.Pieces(f.Geom())) return true, cursor }
func (c *Client) maybeInitPlace(presumedWorkspace workspace.Workspacer) { // This is a hack. Before a client gets sucked into some layout, we // always want to have some floating state to fall back on to. However, // by the time we're "allowed" to save the client's state, it will have // already been placed in the hands of some layout---which may or may // not be floating. So we inject our own state forcefully here. defer func() { wrk := presumedWorkspace if wrk.IsVisible() { c.states["last-floating"] = clientState{ geom: xrect.New(xrect.Pieces(c.frame.Geom())), headGeom: xrect.New(xrect.Pieces(wrk.HeadGeom())), frame: c.frame, maximized: c.maximized, } } else if wm.Startup { // This is a bit tricky. If the window manager is starting up and // has to manage existing clients, then we need to find which // head the client is on and save its state. This is so future // workspace switches will be able to place the client // appropriately. // (This is most common on a Wingo restart.) // We refer to detected workspace as "fake" because the client // isn't on a visible workspace (see above), and therefore the // visible workspace returned by FindMostOverlap *cannot* contain // this client. Therefore, we're only using the fake workspace // to get the geometry. // (This would make more sense if FindMostOverlap returned a head // geometry, but it turns out that a workspace geometry is more // useful.) cgeom := c.frame.Geom() if fakeWrk := wm.Heads.FindMostOverlap(cgeom); fakeWrk != nil { c.states["last-floating"] = clientState{ geom: xrect.New(xrect.Pieces(c.frame.Geom())), headGeom: xrect.New(xrect.Pieces(fakeWrk.HeadGeom())), frame: c.frame, maximized: c.maximized, } } } }() // Any client that isn't normal doesn't get placed. // Let it do what it do, baby. if c.PrimaryType() != TypeNormal { return } // If it's sticky, let it do what it do. if _, ok := presumedWorkspace.(*workspace.Sticky); ok { return } // Transients never get placed. if c.transientFor != nil { return } // If a user/program position is specified, do not place. if c.nhints.Flags&icccm.SizeHintUSPosition > 0 || c.nhints.Flags&icccm.SizeHintPPosition > 0 { return } // We're good, do a placement unless we're already mapped or on a // hidden workspace.. if !presumedWorkspace.IsVisible() || !c.isAttrsUnmapped() { return } w := presumedWorkspace.(*workspace.Workspace) w.LayoutFloater().InitialPlacement(c) }
func Maximize(f Frame) { hg := xrect.New(xrect.Pieces(f.Client().HeadGeom())) f.MoveResize(false, hg.X(), hg.Y(), hg.Width(), hg.Height()) }