func (mcmd mouseCommand) attachClick(wid xproto.Window, run func()) { mousebind.ButtonPressFun( func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) { // empty }).Connect(X, wid, mcmd.buttonStr, false, true) mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { run() }).Connect(X, wid, mcmd.buttonStr, false, false) }
func (mcmd mouseCommand) attach(wid xproto.Window, run func(), propagate, grab bool) { if mcmd.down { mousebind.ButtonPressFun( func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) { run() }).Connect(X, wid, mcmd.buttonStr, propagate, grab) } else { mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { run() }).Connect(X, wid, mcmd.buttonStr, propagate, grab) } }
// attachGrabRelease is a special case of 'attach' that is necessary when // attaching a mouse release event to either the client or frame window. // // TODO: Recall and document *why* this is needed. func (mcmd mouseCommand) attachGrabRelease(wid xproto.Window, run func()) { var err error err = mousebind.ButtonPressFun( func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) { // empty }).Connect(X, wid, mcmd.buttonStr, false, true) if err != nil { logger.Warning.Printf("Could not bind '%s': %s", mcmd.buttonStr, err) } err = mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { run() }).Connect(X, wid, mcmd.buttonStr, false, false) if err != nil { logger.Warning.Printf("Could not bind '%s': %s", mcmd.buttonStr, err) } }
// attach sets up the event handlers for a mouse button press OR release. func (mcmd mouseCommand) attach(wid xproto.Window, run func(), propagate, grab bool) { if mcmd.down { err := mousebind.ButtonPressFun( func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) { run() }).Connect(X, wid, mcmd.buttonStr, propagate, grab) if err != nil { logger.Warning.Printf("Could not bind '%s': %s", mcmd.buttonStr, err) } } else { err := mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { run() }).Connect(X, wid, mcmd.buttonStr, propagate, grab) if err != nil { logger.Warning.Printf("Could not bind '%s': %s", mcmd.buttonStr, err) } } }
func main() { // Connect to the X server using the DISPLAY environment variable. X, err := xgbutil.NewConn() if err != nil { log.Fatal(err) } // Anytime the mousebind (keybind) package is used, mousebind.Initialize // *should* be called once. In the case of the mousebind package, this // isn't strictly necessary, but the 'Drag' features of the mousebind // package won't work without it. mousebind.Initialize(X) // Before attaching callbacks, wrap them in a callback function type. // The mouse package exposes two such callback types: // mousebind.ButtonPressFun and mousebind.ButtonReleaseFun. cb1 := mousebind.ButtonPressFun( func(X *xgbutil.XUtil, e xevent.ButtonPressEvent) { log.Println("Button press!") }) // We can now attach the callback to a particular window and button // combination. This particular example grabs a button on the root window, // which makes it a global mouse binding. // Also, "Mod4-1" typically corresponds to pressing down the "Super" or // "Windows" key on your keyboard, and then pressing the left mouse button. // The last two parameters are whether to make a synchronous grab and // whether to actually issue a grab, respectively. // (The parameters used here are the common case.) // See the documentation for the Connect method for more details. err = cb1.Connect(X, X.RootWin(), "Mod4-1", false, true) // A mouse binding can fail if the mouse string could not be parsed, or if // you're trying to bind a button that has already been grabbed by another // client. if err != nil { log.Fatal(err) } // We can even attach multiple callbacks to the same button. err = mousebind.ButtonPressFun( func(X *xgbutil.XUtil, e xevent.ButtonPressEvent) { log.Println("A second handler always happens after the first.") }).Connect(X, X.RootWin(), "Mod4-1", false, true) if err != nil { log.Fatal(err) } // Finally, if we want this client to stop responding to mouse events, we // can attach another handler that, when run, detaches all previous // handlers. // This time, we'll show an example of a ButtonRelease binding. err = mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, e xevent.ButtonReleaseEvent) { // Use mousebind.Detach to detach the root window // from all ButtonPress *and* ButtonRelease handlers. mousebind.Detach(X, X.RootWin()) mousebind.Detach(X, X.RootWin()) log.Printf("Detached all Button{Press,Release}Events from the "+ "root window (%d).", X.RootWin()) }).Connect(X, X.RootWin(), "Mod4-Shift-1", false, true) if err != nil { log.Fatal(err) } // Finally, start the main event loop. This will route any appropriate // ButtonPressEvents to your callback function. log.Println("Program initialized. Start pressing mouse buttons!") xevent.Main(X) }
func ManageResizingWindows(X *xgbutil.XUtil) { var DRAG_DATA *ResizeDrag handleDragStart := func(X *xgbutil.XUtil, rx, ry, ex, ey int) (cont bool, cursor xproto.Cursor) { // get the clicked window win, err := wm.FindManagedWindowUnderMouse(X) if err != nil { log.Printf("ResizeStart: couldn't find window under mouse: %v\n", err) return false, 0 } // get coordinates inside the clicked window _, reply, err := wm.FindNextUnderMouse(X, win) if err != nil { log.Printf("ResizeStart: couldn't get coordinates of click inside win %v: %v\n", win, err) return false, 0 } // create an xwindow.Window so we can get a rectangle to find our bearings from xwin := xwindow.New(X, win) geom, err := xwin.DecorGeometry() if err != nil { log.Printf("ResizeStart: geometry error: %v\n", err) return false, 0 } // get what side of the rect our mouseclick was on x, y := int(reply.WinX), int(reply.WinY) dir := SideOfRectangle(geom, x, y) // get coordinate part for the edge. this is either X or Y. target_edge := EdgePos(geom, dir) log.Printf("ResizeStart: on window %v - %v. Direction/edge: %v/%v\n", win, geom, dir, target_edge) // find adjacent windows adjacent := list.New() // note that this is an intellegent request: the WM only gives us a list of visible, normal windows // we don't have to worry about moving hidden windows or something managed_windows, err := ewmh.ClientListGet(X) if err != nil { // we can safley ignore this error, because then we just fall back to resizing only this window log.Printf("ResizeStart: error getting EWMH client list: %v\n", err) } else { // select managed windows // always enough space // TODO: don't grossly overallocate for _, candidate_id := range managed_windows { // no need to run calculations for ourself! if candidate_id == win { continue } cand_window := xwindow.New(X, candidate_id) cand_geom, err := cand_window.DecorGeometry() if err != nil { log.Printf("ResizeStart: couldn't get geometry for ajacency candidate %v: %v\n", candidate_id, err) continue } cand_edge := EdgePos(cand_geom, dir.Opposite()) if abs(cand_edge-target_edge) <= AdjacencyEpsilon { // cool, edges are touching. // make sure this window isn't totally above or below the candidate // we do so by constructing a rect using the clicked window's edge // and the candidate's orthagonal dimension // if the rect overlaps, then this window is truly adjacent // // TODO: consider adding a mimumum overlap if dir == wm.Top || dir == wm.Bottom { // measuring X coords if EdgePos(cand_geom, wm.Right) < EdgePos(geom, wm.Left) { continue } if EdgePos(cand_geom, wm.Left) > EdgePos(geom, wm.Right) { continue } } else { if EdgePos(cand_geom, wm.Bottom) < EdgePos(geom, wm.Top) { continue } if EdgePos(cand_geom, wm.Top) > EdgePos(geom, wm.Bottom) { continue } } // if a window has made it to here, it is adgacent! // add it to the list log.Printf("ResizeStart: will resize adjacent window: %v - %v\n", candidate_id, cand_geom) adjacent.PushBack(cand_window) } } } // construct the drag data data := ResizeDrag{xwin, dir, adjacent, rx, ry} DRAG_DATA = &data // TODO: finish this // create an edge // find the adjacent windows // start the drag return true, 0 } handleResize := func(rx, ry int) { delta := rx - DRAG_DATA.LastX if DRAG_DATA.Direction == wm.Top || DRAG_DATA.Direction == wm.Bottom { delta = ry - DRAG_DATA.LastY } if DRAG_DATA.Direction == wm.Left || DRAG_DATA.Direction == wm.Top { delta = delta * -1 } target_geom, err := DRAG_DATA.Window.DecorGeometry() if err != nil { log.Printf("Geom retrieve err: %v\n", err) return } target_edge := EdgePos(target_geom, DRAG_DATA.Direction) // resize the target by the delta err = ResizeDirection(X, DRAG_DATA.Window, DRAG_DATA.Direction, delta) if err != nil { log.Printf("ResizeStep: can't resize target: %v\n", err) return } // calculate actual delta that occured, for resizing the adjacent windows // handles issues with window sizing hints on windows like terminals // making big differences for us target_geom_a, err := DRAG_DATA.Window.DecorGeometry() if err != nil { log.Printf("ResizeStep: Geom retrieve err: %v\n", err) return } target_edge_a := EdgePos(target_geom_a, DRAG_DATA.Direction) delta = target_edge_a - target_edge if DRAG_DATA.Direction == wm.Left || DRAG_DATA.Direction == wm.Top { delta = delta * -1 } // resize each adjacent window by the opposite for e := DRAG_DATA.Adjacent.Front(); e != nil; e = e.Next() { // extract window from the linked list adj_win := e.Value.(*xwindow.Window) adj_geom, err := adj_win.DecorGeometry() if err != nil { log.Printf("ResizeStep: can't query adjacent window %v geometry: %v", adj_win, err) } log.Printf("ResizeStep: resizing adjacent window %v - %v: edge/delta %v/%v\n", adj_win.Id, adj_geom, DRAG_DATA.Direction.Opposite(), -delta) // resize in the opposite direction, with the opposite delta // except the delta should be some actual delta calculated from our source window, // because issues with terminal windows happen err = ResizeDirection(X, adj_win, DRAG_DATA.Direction.Opposite(), -delta) // then to garuntee the edges touch... AdjoinEdge(DRAG_DATA.Window, adj_win, DRAG_DATA.Direction) if err != nil { log.Printf("ResizeStep: can't resize adjacent window %v: %v\n", adj_win, err) continue } } // save new coordinates DRAG_DATA.LastX = rx DRAG_DATA.LastY = ry } handleDragStep := func(X *xgbutil.XUtil, rx, ry, ex, ey int) { if DynamicDragResize { handleResize(rx, ry) } } handleDragEnd := func(X *xgbutil.XUtil, rx, ry, ex, ey int) { // only run on high enough deltas. Prevents windows from resizing when the user has gone "nah." // use the adjacency epsilon here too delta := abs(rx - DRAG_DATA.LastX) if DRAG_DATA.Direction == wm.Top || DRAG_DATA.Direction == wm.Bottom { delta = abs(ry - DRAG_DATA.LastY) } if delta > AdjacencyEpsilon { handleResize(rx, ry) } else { log.Printf("ResizeEnd: delta %v less than epsilon %v, skipping resize\n", delta, AdjacencyEpsilon) } DRAG_DATA = nil } // resizes the window by 1px vertically, then observes the actual change resizeBugHunt := func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) { // get xwindow from click clicked, err := wm.FindManagedWindowUnderMouse(X) if err != nil { log.Println(err) return } win := xwindow.New(X, clicked) names := []string{"PreDecor", "PostDecorPreMove", "PostDecor", "Pre", "Post"} geometries := make(map[string]xrect.Rect, 4) // take measurements pre_decor, err := win.DecorGeometry() if err != nil { log.Printf("Error fetching pre DecorGeom: %v\n", err) } geometries["PreDecor"] = pre_decor geo, err := win.Geometry() if err != nil { log.Printf("Error fetching pre Geom: %v\n", err) } geometries["Pre"] = geo // resize vertically by 1px log.Println("Resizing using window.Geometry() + 50, not DecorGeometry() + 1") //err = win.WMResize(geo.Width() + 50, geo.Height()) if err != nil { log.Println(err) } // wait to finish err = wm.PollFor(win, wm.GeometryDiffers(geo), wm.DecorDiffers(pre_decor)) if err != nil { log.Printf("Oops wjile waiting for resizing and things: %v\n", err) } post_decor_pre_move, _, err := wm.Geometries(win) if err != nil { log.Println(err) post_decor_pre_move = pre_decor } geometries["PostDecorPreMove"] = post_decor_pre_move // move zero pixels, then wait err = wm.Move(win, post_decor_pre_move.X(), post_decor_pre_move.Y()) if err != nil { log.Printf("error in wm.Move zero px: %v\n", err) } geo, err = win.DecorGeometry() if err != nil { log.Printf("Error fetching post DecorGeom: %v\n", err) } geometries["PostDecor"] = geo geo, err = win.Geometry() if err != nil { log.Printf("Error fetching post Geom: %v\n", err) } geometries["Post"] = geo for _, k := range names { log.Printf("%s: %v\n", k, geometries[k]) } // release X events // needed if the event binding is synchronous // see http://godoc.burntsushi.net/pkg/github.com/BurntSushi/xgbutil/mousebind/#hdr-When_to_use_a_synchronous_binding // xproto.AllowEvents(X.Conn(), xproto.AllowReplayPointer, 0) } // bind handler mousebind.Drag(X, X.RootWin(), X.RootWin(), KeyComboResize, true, handleDragStart, handleDragStep, handleDragEnd) mousebind.ButtonPressFun(resizeBugHunt).Connect(X, X.RootWin(), ui.KeyOption+"-Shift-Control-1", false, true) }