// When a cross is declared in its object literal form, it may not have the appropriate window. // this function creates a new X11 window for the cross with the correct geometry depending // on its Icon* parameters. func (c *Cross) CreateWindow(X *xgbutil.XUtil, icons_per_direction int, bg_color uint32) (*xwindow.Window, error) { // calculate the dimensions of the spars of our cross + // width/height reflect the vertical-orientation rectangle width := c.IconMargin*2 + c.IconSize // padding between the icons, margin on the edges height := c.IconSize*icons_per_direction + c.IconPadding*(icons_per_direction-1) + c.IconMargin*2 // intitialize a basic window for the cross win, err := xwindow.Generate(X) if err != nil { return nil, err } win.Create(X.RootWin(), 0, 0, height, height, xproto.CwBackPixel|xproto.CwOverrideRedirect, bg_color, 1) // the rects we will be adding together to form the shape of the cross vert := xrect.New(0, 0, width, height) horiz := xrect.New(0, 0, height, width) struts := []xrect.Rect{vert, horiz} geom, err := win.Geometry() if err != nil { return nil, err } // center struts over window x, y := util.CenterChild(vert, geom) vert.XSet(x) vert.YSet(y) x, y = util.CenterChild(horiz, geom) horiz.XSet(x) horiz.YSet(y) // build the cross shape from our friendly rectangles err = ComposeShape(X, win.Id, struts) if err != nil { return nil, err } // add the window to our cross struct c.Window = win // create icons from our images clr := RGB(bg_color) if c.imagesToBecomeIcons != nil { icons := make(map[string]*Icon, len(c.imagesToBecomeIcons)) for name, img := range c.imagesToBecomeIcons { icon := NewIcon(X, img, win.Id) icon.Background = clr icons[name] = icon } c.Icons = icons } else { return nil, errors.New("Cross: you must create crosses using the NewCross function (this cross has now iconsToBecomeImage)") } return win, nil }
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) }
// PhyiscalHeads returns the list of heads in a physical ordering. // Namely, left to right then top to bottom. (Defined by (X, Y).) // Xinerama must have been initialized, otherwise the xinerama.QueryScreens // request will panic. // PhysicalHeads also checks to make sure each rectangle has a unique (x, y) // tuple, so as not to return the geometry of cloned displays. // (At present moment, xgbutil initializes Xinerama automatically during // initial connection.) func PhysicalHeads(xu *xgbutil.XUtil) (Heads, error) { xinfo, err := xinerama.QueryScreens(xu.Conn()).Reply() if err != nil { return nil, err } hds := make(Heads, 0) for _, info := range xinfo.ScreenInfo { head := xrect.New(int(info.XOrg), int(info.YOrg), int(info.Width), int(info.Height)) // Maybe Xinerama is enabled, but we have cloned displays... unique := true for _, h := range hds { if h.X() == head.X() && h.Y() == head.Y() { unique = false break } } if unique { hds = append(hds, head) } } sort.Sort(hds) return hds, nil }
func query(X *xgbutil.XUtil) xinerama.Heads { if X.ExtInitialized("XINERAMA") { heads, err := xinerama.PhysicalHeads(X) if err != nil || len(heads) == 0 { if err == nil { logger.Warning.Printf("Could not find any physical heads " + "with the Xinerama extension.") } else { logger.Warning.Printf("Could not load physical heads via "+ "Xinerama: %s", err) } logger.Warning.Printf("Assuming one head with size equivalent " + "to the root window.") } else { return heads } } // If we're here, then something went wrong or the Xinerama extension // isn't available. So query the root window for its geometry and use that. rgeom := xwindow.RootGeometry(X) return xinerama.Heads{ xrect.New(rgeom.X(), rgeom.Y(), rgeom.Width(), rgeom.Height()), } }
func (hds *Heads) ApplyStruts(clients Clients) { hds.workarea = make(xinerama.Heads, len(hds.geom)) for i, hd := range hds.geom { hds.workarea[i] = xrect.New(hd.X(), hd.Y(), hd.Width(), hd.Height()) } rgeom := xwindow.RootGeometry(hds.X) for i := 0; i < clients.Len(); i++ { c := clients.Get(i) strut, _ := ewmh.WmStrutPartialGet(hds.X, c.Id()) if strut == nil { continue } xrect.ApplyStrut(hds.workarea, rgeom.Width(), rgeom.Height(), strut.Left, strut.Right, strut.Top, strut.Bottom, strut.LeftStartY, strut.LeftEndY, strut.RightStartY, strut.RightEndY, strut.TopStartX, strut.TopEndX, strut.BottomStartX, strut.BottomEndX) } for _, wrk := range hds.workspaces.Wrks { wrk.Place() } }
func (cmd Focus) Run() gribble.Value { return syncRun(func() gribble.Value { return withClient(cmd.Client, func(c *xclient.Client) { if c == nil { focus.Root() // Use the mouse coordinates to find which workspace it was // clicked in. If a workspace can be found (i.e., no clicks in // dead areas), then activate it. xc, rw := wm.X.Conn(), wm.X.RootWin() qp, err := xproto.QueryPointer(xc, rw).Reply() if err != nil { logger.Warning.Printf("Could not query pointer: %s", err) return } geom := xrect.New(int(qp.RootX), int(qp.RootY), 1, 1) if wrk := wm.Heads.FindMostOverlap(geom); wrk != nil { wm.SetWorkspace(wrk, false) } } else { c.Focus() xevent.ReplayPointer(wm.X) } }) }) }
// New creates a new window value from a window id and an XUtil type. // Geom is initialized to zero values. Use Window.Geometry to load it. // Note that the geometry is the size of this particular window and nothing // else. If you want the geometry of a client window including decorations, // please use Window.DecorGeometry. func New(xu *xgbutil.XUtil, win xproto.Window) *Window { return &Window{ X: xu, Id: win, Geom: xrect.New(0, 0, 1, 1), Destroyed: false, } }
// RawGeometry isn't smart. It just queries the window given for geometry. func RawGeometry(xu *xgbutil.XUtil, win xproto.Drawable) (xrect.Rect, error) { xgeom, err := xproto.GetGeometry(xu.Conn(), win).Reply() if err != nil { return nil, err } return xrect.New(int(xgeom.X), int(xgeom.Y), int(xgeom.Width), int(xgeom.Height)), nil }
func main() { X, _ := xgbutil.NewConn() heads, err := xinerama.PhysicalHeads(X) if err != nil { fmt.Printf("ERROR: %v\n", err) } // Test intersection r1 := xrect.New(0, 0, 100, 100) r2 := xrect.New(100, 100, 100, 100) fmt.Println(xrect.IntersectArea(r1, r2)) // Test largest overlap window := xrect.New(1800, 0, 300, 200) fmt.Println(xrect.LargestOverlap(window, heads)) // Test ApplyStrut rgeom, _ := xwindow.RawGeometry(X, xproto.Drawable(X.RootWin())) fmt.Println("---------------------------") for i, head := range heads { fmt.Printf("%d - %v\n", i, head) } // Let's actually apply struts to the current environment clients, _ := ewmh.ClientListGet(X) for _, client := range clients { strut, err := ewmh.WmStrutPartialGet(X, client) if err == nil { xrect.ApplyStrut(heads, rgeom.Width(), rgeom.Height(), strut.Left, strut.Right, strut.Top, strut.Bottom, strut.LeftStartY, strut.LeftEndY, strut.RightStartY, strut.RightEndY, strut.TopStartX, strut.TopEndX, strut.BottomStartX, strut.BottomEndX) } } fmt.Println("---------------------------") fmt.Println("After applying struts...") for i, head := range heads { fmt.Printf("%d - %v\n", i, head) } }
// given a slice of rects, return a rect that covers all of them! func Bound(rects []xrect.Rect) xrect.Rect { min_x, min_y, max_x, max_y := coords(rects[0]) for _, rect := range rects[1:] { x1, y1, x2, y2 := coords(rect) min_x = min(x1, min_x) min_y = min(y1, min_y) max_x = max(x2, max_x) max_y = max(y2, max_y) } return xrect.New(min_x, min_y, max_x-min_x, max_y-min_y) }
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 handleMotionNotify(X *xgbutil.XUtil, ev xevent.MotionNotifyEvent) { qp, err := xproto.QueryPointer(X.Conn(), X.RootWin()).Reply() if err != nil { logger.Warning.Printf("Could not query pointer: %s", err) return } geom := xrect.New(int(qp.RootX), int(qp.RootY), 1, 1) if wrk := wm.Heads.FindMostOverlap(geom); wrk != nil { if wrk != wm.Workspace() { wm.SetWorkspace(wrk, false) wm.FocusFallback() } } }
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()) }