Beispiel #1
0
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)
}
Beispiel #2
0
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)
	}
}
Beispiel #3
0
// 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)
	}
}
Beispiel #4
0
// 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)
		}
	}
}
Beispiel #5
0
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)
}
Beispiel #6
0
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)

}