Exemple #1
0
// find the EWHM window under the mouse cursor
func FindManagedWindowUnderMouse(X *xgbutil.XUtil) (xproto.Window, error) {
	// construct a hashset of the managed windows
	clients, err := ewmh.ClientListGet(X)
	if err != nil {
		return 0, fmt.Errorf("FindUnderMouse: could not retrieve EWHM client list: %v", err)
	}

	managed := make(map[xproto.Window]bool, len(clients))
	for _, win := range clients {
		managed[win] = true
	}

	cur_window := X.RootWin()

	// descend the QueryTree to the first child that is a EWMH managed window
	for {
		// return the parent if it is an EWHM window
		if _, ok := managed[cur_window]; ok {
			return cur_window, nil
		}

		cur_window, _, err = FindNextUnderMouse(X, cur_window)
		if err != nil {
			break
		}
	}

	// we didn't find the window :(
	return 0, errors.New("FindUnderMouse: no EWMH window found under mouse")
}
Exemple #2
0
//GetClients get windows list
func GetClients() []Client {
	clients := []Client{}
	var err error
	X, err = xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}
	wids, err := ewmh.ClientListGet(X)
	if err != nil {
		log.Fatal(err)
	}
	a, _ := ewmh.ActiveWindowGet(X)
	for _, wid := range wids {
		name, err := ewmh.WmNameGet(X, wid)
		if name == "Shadow" {
			SHADOW = wid
			continue
		}
		if err != nil { // not a fatal error
			log.Println(err)
			name = ""
		}
		desk, _ := ewmh.WmDesktopGet(X, wid)
		class, _ := icccm.WmClassGet(X, wid)
		clients = append(clients, Client{
			wid, name, desk, wid == a, class.Class,
		})
	}
	return clients

}
Exemple #3
0
func (r *Region) getDockWindow() (xproto.Window, error) {
	windows, _ := ewmh.ClientListGet(XU)
	for _, xid := range windows {
		res, err := icccm.WmClassGet(XU, xid)
		if err == nil && res.Instance == "dde-dock" {
			return xid, nil
		}
	}
	return 0, errors.New("find dock window failed, it's not existed.")
}
func (self *SessionModule) GetAllWindows() ([]SessionWindow, error) {
	clients, _ := ewmh.ClientListGet(self.X)

	//  allocate window objects
	windows := make([]SessionWindow, len(clients))

	//  for each window...
	for i, id := range clients {
		windows[i], _ = self.GetWindow(strconv.Itoa(int(id)))
	}

	return windows, nil
}
func walkClientList(pre func(xproto.Window) bool) bool {
	list, err := ewmh.ClientListGet(XU)
	if err != nil {
		logger.Warning("Can't get _NET_CLIENT_LIST", err)
		return false
	}

	for _, c := range list {
		if pre(c) {
			return true
		}
	}

	return false
}
func (rs RunningSource) GetMatches(inp string, ct *CommandTray) []Command {
	cmds := make([]Command, 0)

	if ct.X == nil {
		return []Command{}
	}

	clis, err := ewmh.ClientListGet(ct.X)

	if err != nil {
		return cmds
	}

	for _, xwin := range clis {
		dt, err := ewmh.CurrentDesktopGet(ct.X)

		if err != nil {
			dt = 0xFFFFFFFF
		}

		wdt, err := ewmh.WmDesktopGet(ct.X, xwin)
		if err != nil {
			wdt = dt
		}

		if dt != wdt {
			continue
		}

		name, err := xprop.PropValStr(xprop.GetProperty(ct.X, xwin, "_NET_WM_NAME"))
		if err != nil {
			//print("Err1: ", err.Error(), "\n")
			name, err = xprop.PropValStr(xprop.GetProperty(ct.X, xwin, "WM_NAME"))
			if err != nil {
				//print("Err2: ", err.Error(), "\n")
				name = "Unnamed Window"
			}
		}

		if strings.Contains(strings.ToLower(name), inp) {
			cmds = append(cmds, RunningCommand{X: ct.X, win: xwin, name: name})
		}
	}

	return cmds
}
func main() {
	X, err := xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	// Get the list of window ids managed by the window manager.
	clients, err := ewmh.ClientListGet(X)
	if err != nil {
		log.Fatal(err)
	}

	// For each client, try to find its icon. If we find one, blend it with
	// a nice background color and show it in its own window.
	// Otherwise, skip it.
	for _, wid := range clients {
		// FindIcon will find an icon closest to the size specified.
		// If one can't be found, the resulting image will be scaled
		// automatically.
		// To avoid scaling the icon, specify '0' for both the width and height.
		// In this case, the largest icon found will be returned.
		xicon, err := xgraphics.FindIcon(X, wid, iconWidth, iconHeight)
		if err != nil {
			log.Printf("Could not find icon for window %d.", wid)
			continue
		}

		// Get the name of this client. (It will be set as the icon window's
		// name.)
		name, err := ewmh.WmNameGet(X, wid)
		if err != nil { // not a fatal error
			log.Println(err)
			name = ""
		}

		// Blend a pink background color so its easy to see that alpha blending
		// works.
		xgraphics.BlendBgColor(xicon, color.RGBA{0xff, 0x0, 0xff, 0xff})
		xicon.XShowExtra(name, false)
	}

	// All we really need to do is block, so a 'select{}' would be sufficient.
	// But running the event loop will emit errors if anything went wrong.
	xevent.Main(X)
}
Exemple #8
0
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)
	}
}
func main() {
	// Connect to the X server using the DISPLAY environment variable.
	X, err := xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	// Get a list of all client ids.
	clientids, err := ewmh.ClientListGet(X)
	if err != nil {
		log.Fatal(err)
	}

	// Iterate through each client, find its name and find its size.
	for _, clientid := range clientids {
		name, err := ewmh.WmNameGet(X, clientid)

		// If there was a problem getting _NET_WM_NAME or if its empty,
		// try the old-school version.
		if err != nil || len(name) == 0 {
			name, err = icccm.WmNameGet(X, clientid)

			// If we still can't find anything, give up.
			if err != nil || len(name) == 0 {
				name = "N/A"
			}
		}

		// Now find the geometry, including decorations, of the client window.
		// Note that DecorGeometry actually traverses the window tree by
		// issuing QueryTree requests until a top-level window (i.e., its
		// parent is the root window) is found. The geometry of *that* window
		// is then returned.
		dgeom, err := xwindow.New(X, clientid).DecorGeometry()
		if err != nil {
			log.Printf("Could not get geometry for %s (0x%X) because: %s",
				name, clientid, err)
			continue
		}

		fmt.Printf("%s (0x%x)\n", name, clientid)
		fmt.Printf("\tGeometry: %s\n", dgeom)
	}
}
Exemple #10
0
func main() {
	var err error
	X, err = xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	clientids, err := ewmh.ClientListGet(X)
	if err != nil {
		log.Fatal(err)
	}

	for _, clientid := range clientids {
		name, err := ewmh.WmNameGet(X, clientid)
		if err != nil {
			continue
		}

		if name == "Super Hexagon" {
			HexWindow = xwindow.New(X, clientid)
			break
		}
	}

	if HexWindow == nil {
		log.Fatal("Couldn't find Super Hexagon window.")
	}

	//Create a window
	DisplayWindow, err = xwindow.Generate(X)
	if err != nil {
		log.Fatalf("Could not generate a new window X id: %s", err)
	}
	dgeom, _ := HexWindow.DecorGeometry()
	DisplayWindow.Create(X.RootWin(), 0, 0, dgeom.Width(), dgeom.Height(), xproto.CwBackPixel, 0)
	DisplayWindow.Map()

	//Start the routine that updates the window
	go updater()

	xevent.Main(X)
}
func (m *ClientManager) listenRootWindow() {
	var update = func() {
		list, err := ewmh.ClientListGet(XU)
		if err != nil {
			logger.Warning("Can't Get _NET_CLIENT_LIST", err)
		}
		isLauncherShown = false
		for _, xid := range list {
			if !isDeepinLauncher(xid) {
				continue
			}

			winProps, err :=
				xproto.GetWindowAttributes(XU.Conn(),
					xid).Reply()
			if err != nil {
				break
			}
			if winProps.MapState == xproto.MapStateViewable {
				isLauncherShown = true
			}
			break
		}
		ENTRY_MANAGER.runtimeAppChanged(list)
	}

	xwindow.New(XU, XU.RootWin()).Listen(xproto.EventMaskPropertyChange)
	xevent.PropertyNotifyFun(func(XU *xgbutil.XUtil, ev xevent.PropertyNotifyEvent) {
		switch ev.Atom {
		case _NET_CLIENT_LIST:
			update()
		case _NET_ACTIVE_WINDOW:
			var err error
			isLauncherShown = false
			if activeWindow, err = ewmh.ActiveWindowGet(XU); err == nil {
				appId := find_app_id_by_xid(activeWindow,
					DisplayModeType(setting.GetDisplayMode()))
				logger.Debug("current active window:", appId)
				if rApp, ok := ENTRY_MANAGER.runtimeApps[appId]; ok {
					logger.Debug("find runtime app")
					rApp.setLeader(activeWindow)
					rApp.updateState(activeWindow)
				}

				logger.Debug("active window is", appId)
				if appId != DDELauncher {
					LAUNCHER, err := launcher.NewLauncher(
						"com.deepin.dde.launcher",
						"/com/deepin/dde/launcher",
					)
					if err != nil {
						logger.Debug(err)
					} else {
						LAUNCHER.Hide()
						launcher.DestroyLauncher(LAUNCHER)
					}
				} else {
					isLauncherShown = true
				}

				lastActive = appId
				dbus.Emit(m, "ActiveWindowChanged", uint32(activeWindow))
			}

			hideMode := HideModeType(setting.GetHideMode())
			if hideMode == HideModeSmartHide || hideMode == HideModeKeepHidden {
				hideModemanager.UpdateState()
			}
		case _NET_SHOWING_DESKTOP:
			dbus.Emit(m, "ShowingDesktopChanged")
		case DEEPIN_SCREEN_VIEWPORT:
			updateCurrentViewport()
		}
	}).Connect(XU, XU.RootWin())

	update()
	xevent.Main(XU)
}
Exemple #12
0
func main() {
	// Connect to the X server using the DISPLAY environment variable.
	X, err := xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	// Wrap the root window in a nice Window type.
	root := xwindow.New(X, X.RootWin())

	// Get the geometry of the root window.
	rgeom, err := root.Geometry()
	if err != nil {
		log.Fatal(err)
	}

	// Get the rectangles for each of the active physical heads.
	// These are returned sorted in order from left to right and then top
	// to bottom.
	// But first check if Xinerama is enabled. If not, use root geometry.
	var heads xinerama.Heads
	if X.ExtInitialized("XINERAMA") {
		heads, err = xinerama.PhysicalHeads(X)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		heads = xinerama.Heads{rgeom}
	}

	// Fetch the list of top-level client window ids currently managed
	// by the running window manager.
	clients, err := ewmh.ClientListGet(X)
	if err != nil {
		log.Fatal(err)
	}

	// Output the head geometry before modifying them.
	fmt.Println("Workarea for each head:")
	for i, head := range heads {
		fmt.Printf("\tHead #%d: %s\n", i+1, head)
	}

	// For each client, check to see if it has struts, and if so, apply
	// them to our list of head rectangles.
	for _, clientid := range clients {
		strut, err := ewmh.WmStrutPartialGet(X, clientid)
		if err != nil { // no struts for this client
			continue
		}

		// Apply the struts to our heads.
		// This modifies 'heads' in place.
		xrect.ApplyStrut(heads, uint(rgeom.Width()), uint(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)
	}

	// Now output the head geometry again after modification.
	fmt.Println("Workarea for each head after applying struts:")
	for i, head := range heads {
		fmt.Printf("\tHead #%d: %s\n", i+1, head)
	}
}
Exemple #13
0
func main() {
	X, Xerr = xgbutil.NewConn()
	if Xerr != nil {
		panic(Xerr)
	}

	active, _ := ewmh.ActiveWindowGet(X)

	parent, _ := xwindow.ParentWindow(X, active)
	actOpacity, _ := ewmh.WmWindowOpacityGet(X, parent)
	fmt.Printf("Opacity for active window: %f\n", actOpacity)

	showDesk, _ := ewmh.ShowingDesktopGet(X)
	fmt.Printf("Showing desktop? %v\n", showDesk)

	wmName, err := ewmh.GetEwmhWM(X)
	if err != nil {
		fmt.Printf("No conforming window manager found... :-(\n")
		fmt.Println(err)
	} else {
		fmt.Printf("Window manager: %s\n", wmName)
	}

	pager := xproto.Window(0x160001e)
	middle := xproto.Window(0x3200016)
	geom, _ := ewmh.DesktopGeometryGet(X)
	desktops, _ := ewmh.DesktopNamesGet(X)
	curdesk, _ := ewmh.CurrentDesktopGet(X)
	clients, _ := ewmh.ClientListGet(X)
	activeName, _ := ewmh.WmNameGet(X, active)

	fmt.Printf("Active window: %x\n", active)
	fmt.Printf("Current desktop: %d\n", curdesk)
	fmt.Printf("Client list: %v\n", clients)
	fmt.Printf("Desktop geometry: (width: %d, height: %d)\n",
		geom.Width, geom.Height)
	fmt.Printf("Active window name: %s\n", activeName)
	fmt.Printf("Desktop names: %s\n", desktops)

	var desk string
	if curdesk < len(desktops) {
		desk = desktops[curdesk]
	} else {
		desk = string(curdesk)
	}
	fmt.Printf("Current desktop: %s\n", desk)

	// fmt.Printf("\nChanging current desktop to 25 from %d\n", curdesk)
	ewmh.CurrentDesktopSet(X, curdesk)
	// fmt.Printf("Current desktop is now: %d\n", ewmh.CurrentDesktop(X))

	fmt.Printf("Setting active win to %x\n", middle)
	// ewmh.ActiveWindowReq(X, middle)

	rand.Seed(int64(time.Now().Nanosecond()))
	randStr := make([]byte, 20)
	for i, _ := range randStr {
		if rf := rand.Float32(); rf < 0.40 {
			randStr[i] = byte('a' + rand.Intn('z'-'a'))
		} else if rf < 0.80 {
			randStr[i] = byte('A' + rand.Intn('Z'-'A'))
		} else {
			randStr[i] = ' '
		}
	}

	ewmh.WmNameSet(X, active, string(randStr))
	newName, _ := ewmh.WmNameGet(X, active)
	fmt.Printf("New name: %s\n", newName)

	// deskNames := ewmh.DesktopNamesGet(X)
	// fmt.Printf("Desktop names: %s\n", deskNames)
	// deskNames[len(deskNames) - 1] = "xgbutil"
	// ewmh.DesktopNamesSet(X, deskNames)
	// fmt.Printf("Desktop names: %s\n", ewmh.DesktopNamesGet(X))

	supported, _ := ewmh.SupportedGet(X)
	fmt.Printf("Supported hints: %v\n", supported)
	fmt.Printf("Setting supported hints...\n")
	ewmh.SupportedSet(X, []string{"_NET_CLIENT_LIST", "_NET_WM_NAME",
		"_NET_WM_DESKTOP"})

	numDesks, _ := ewmh.NumberOfDesktopsGet(X)
	fmt.Printf("Number of desktops: %d\n", numDesks)
	// ewmh.NumberOfDesktopsReq(X.EwmhNumberOfDesktops(X) + 1)
	// time.Sleep(time.Second)
	// fmt.Printf("Number of desktops: %d\n", ewmh.NumberOfDesktops(X))

	viewports, _ := ewmh.DesktopViewportGet(X)
	fmt.Printf("Viewports (%d): %v\n", len(viewports), viewports)

	// viewports[2].X = 50
	// viewports[2].Y = 293
	// ewmh.DesktopViewportSet(X, viewports)
	// time.Sleep(time.Second)
	//
	// viewports = ewmh.DesktopViewport(X)
	// fmt.Printf("Viewports (%d): %v\n", len(viewports), viewports)

	// ewmh.CurrentDesktopReq(X, 3)

	visDesks, _ := ewmh.VisibleDesktopsGet(X)
	workarea, _ := ewmh.WorkareaGet(X)
	fmt.Printf("Visible desktops: %v\n", visDesks)
	fmt.Printf("Workareas: %v\n", workarea)
	// fmt.Printf("Virtual roots: %v\n", ewmh.VirtualRoots(X))
	// fmt.Printf("Desktop layout: %v\n", ewmh.DesktopLayout(X))
	fmt.Printf("Closing window %x\n", 0x2e004c5)
	ewmh.CloseWindow(X, 0x1e00cdf)

	fmt.Printf("Moving/resizing window: %x\n", 0x2e004d0)
	ewmh.MoveresizeWindow(X, 0x2e004d0, 1920, 30, 500, 500)

	// fmt.Printf("Trying to initiate a moveresize...\n")
	// ewmh.WmMoveresize(X, 0x2e004db, xgbutil.EwmhMove)
	// time.Sleep(5 * time.Second)
	// ewmh.WmMoveresize(X, 0x2e004db, xgbutil.EwmhCancel)

	// fmt.Printf("Stacking window %x...\n", 0x2e00509)
	// ewmh.RestackWindow(X, 0x2e00509)

	fmt.Printf("Requesting frame extents for active window...\n")
	ewmh.RequestFrameExtents(X, active)

	activeDesk, _ := ewmh.WmDesktopGet(X, active)
	activeType, _ := ewmh.WmWindowTypeGet(X, active)
	fmt.Printf("Active window's desktop: %d\n", activeDesk)
	fmt.Printf("Active's types: %v\n", activeType)
	// fmt.Printf("Pager's types: %v\n", ewmh.WmWindowType(X, 0x180001e))

	// fmt.Printf("Pager's state: %v\n", ewmh.WmState(X, 0x180001e))

	// ewmh.WmStateReq(X, active, xgbutil.EwmhStateToggle,
	// "_NET_WM_STATE_HIDDEN")
	// ewmh.WmStateReqExtra(X, active, xgbutil.EwmhStateToggle,
	// "_NET_WM_STATE_MAXIMIZED_VERT",
	// "_NET_WM_STATE_MAXIMIZED_HORZ", 2)

	activeAllowed, _ := ewmh.WmAllowedActionsGet(X, active)
	fmt.Printf("Allowed actions on active: %v\n", activeAllowed)

	struts, err := ewmh.WmStrutGet(X, pager)
	if err != nil {
		fmt.Printf("Pager struts: %v\n", err)
	} else {
		fmt.Printf("Pager struts: %v\n", struts)
	}

	pstruts, err := ewmh.WmStrutPartialGet(X, pager)
	if err != nil {
		fmt.Printf("Pager struts partial: %v - %v\n", pstruts, err)
	} else {
		fmt.Printf("Pager struts partial: %v\n", pstruts.BottomStartX)
	}

	// fmt.Printf("Icon geometry for active: %v\n",
	// ewmh.WmIconGeometry(X, active))

	icons, _ := ewmh.WmIconGet(X, active)
	fmt.Printf("Active window's (%x) icon data: (length: %v)\n",
		active, len(icons))
	for _, icon := range icons {
		fmt.Printf("\t(%d, %d)", icon.Width, icon.Height)
		fmt.Printf(" :: %d == %d\n", icon.Width*icon.Height, len(icon.Data))
	}
	// fmt.Printf("Now set them again...\n")
	// ewmh.WmIconSet(X, active, icons[:len(icons) - 1])
}
Exemple #14
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)

}