Beispiel #1
0
// DrawText is a convenience function that will create a new image, render
// the provided text to it, paint the image to the provided window, and resize
// the window to fit the text snugly.
//
// An error can occur when rendering the text to an image.
func DrawText(win *xwindow.Window, font *truetype.Font, size float64,
	fontClr, bgClr render.Color, text string) error {

	// BUG(burntsushi): If `text` is zero-length, very bad things happen.
	if len(text) == 0 {
		text = " "
	}

	// Over estimate the extents.
	ew, eh := xgraphics.Extents(font, size, text)

	// Create an image using the over estimated extents.
	img := xgraphics.New(win.X, image.Rect(0, 0, ew, eh))
	xgraphics.BlendBgColor(img, bgClr.ImageColor())

	// Now draw the text, grab the (x, y) position advanced by the text, and
	// check for an error in rendering.
	_, _, err := img.Text(0, 0, fontClr.ImageColor(), size, font, text)
	if err != nil {
		return err
	}

	// Resize the window to the geometry determined by (x, y).
	win.Resize(ew, eh)

	// Now draw the image to the window and destroy it.
	img.XSurfaceSet(win.Id)
	// subimg := img.SubImage(image.Rect(0, 0, ew, eh))
	img.XDraw()
	img.XPaint(win.Id)
	img.Destroy()

	return nil
}
Beispiel #2
0
// DrawText is a convenience function that will create a new image, render
// the provided text to it, paint the image to the provided window, and resize
// the window to fit the text snugly.
//
// An error can occur when rendering the text to an image.
func DrawText(win *xwindow.Window, font *truetype.Font, size float64,
	fontClr, bgClr color.RGBA, text string) error {

	// Over estimate the extents.
	ew, eh := xgraphics.TextMaxExtents(font, size, text)
	eh += misc.TextBreathe // <-- this is the bug

	// Create an image using the over estimated extents.
	img := xgraphics.New(win.X, image.Rect(0, 0, ew, eh))
	xgraphics.BlendBgColor(img, bgClr)

	// Now draw the text, grab the (x, y) position advanced by the text, and
	// check for an error in rendering.
	x, y, err := img.Text(0, 0, fontClr, size, font, text)
	if err != nil {
		return err
	}

	// Resize the window to the geometry determined by (x, y).
	w, h := x, y+misc.TextBreathe // <-- also part of the bug
	win.Resize(w, h)

	// Now draw the image to the window and destroy it.
	img.XSurfaceSet(win.Id)
	subimg := img.SubImage(image.Rect(0, 0, w, h))
	subimg.XDraw()
	subimg.XPaint(win.Id)
	img.Destroy()

	return nil
}
Beispiel #3
0
// Swap the position and size of the target and incoming windows
func Swap(target, incoming *xwindow.Window) error {
	// get bounds for both windows
	target_bounds, err := target.DecorGeometry()
	if err != nil {
		log.Printf("Swap: error getting bounds of target: %v\n", err)
		return err
	}
	incoming_bounds, err := incoming.DecorGeometry()
	if err != nil {
		log.Printf("Swap: error getting bounds of incoming: %v\n", err)
		return err
	}

	// configure windows, easy as pie!
	err = MoveResize(target, incoming_bounds.X(), incoming_bounds.Y(),
		incoming_bounds.Width(), incoming_bounds.Height())
	if err != nil {
		log.Printf("Swap: error configuring target: %v\n", err)
		return err
	}
	err = MoveResize(incoming, target_bounds.X(), target_bounds.Y(),
		target_bounds.Width(), target_bounds.Height())
	if err != nil {
		log.Printf("Swap: error configuring incoming: %v\n", err)
		return err
	}

	// cool
	return nil
}
Beispiel #4
0
// cutting windows in half on the Y axis
func splitHorizontal(target, incoming *xwindow.Window, incomingOnLeft bool) error {
	bounds, err := target.DecorGeometry()
	if err != nil {
		log.Printf("splitHorizontal: error getting bounds of target: %v\n", err)
		return err
	}

	left_width := bounds.Width() / 2
	right_width := bounds.Width() - left_width

	var left, right *xwindow.Window
	if incomingOnLeft {
		left = incoming
		right = target
	} else {
		left = target
		right = incoming
	}

	err = MoveResize(right, bounds.X()+left_width, bounds.Y(), right_width, bounds.Height())
	if err != nil {
		log.Printf("splitHorizontal: error configuring right: %v\n", err)
		return err
	}

	err = MoveResize(left, bounds.X(), bounds.Y(), left_width, bounds.Height())
	if err != nil {
		log.Printf("splitHorizontal: error configuring left: %v\n", err)
		return err
	}

	// cool
	return nil
}
Beispiel #5
0
func newWindow(controlCh *controlCh, X *xgbutil.XUtil, width, height int) *xwindow.Window {
	var (
		err error
		win *xwindow.Window
	)

	win, err = xwindow.Generate(X)
	if err != nil {
		panic(err)
	}

	win.Create(X.RootWin(), 0, 0, width, height,
		xproto.CwBackPixel|xproto.CwEventMask,
		0, xproto.EventMaskButtonRelease)

	// Xorg application exits when the window is closed.
	win.WMGracefulClose(
		func(w *xwindow.Window) {
			xevent.Detach(w.X, w.Id)
			mousebind.Detach(w.X, w.Id)
			w.Destroy()
			xevent.Quit(X)
			controlCh.exit <- true
		})

	// In order to get ConfigureNotify events, we must listen to the window
	// using the 'StructureNotify' mask.
	win.Listen(xproto.EventMaskButtonPress |
		xproto.EventMaskButtonRelease |
		xproto.EventMaskKeyPress |
		xproto.EventMaskKeyRelease |
		xproto.EventMaskStructureNotify)
	win.Map()
	return win
}
Beispiel #6
0
func Geometries(win *xwindow.Window) (xrect.Rect, xrect.Rect, error) {
	decor, err := win.DecorGeometry()
	if err != nil {
		return nil, nil, err
	}
	base, err := win.Geometry()
	if err != nil {
		return nil, nil, err
	}
	return decor, base, nil
}
Beispiel #7
0
// same as above, but moveresize instead of just move at the first step,
// then resize to the provided w/h instead of a snapshotted one
// this implementation differs from Move in that it makes no effort to be end-synchronous
// This function waits only on the window's inner geometry resizing, not on actual movement occuring
func MoveResize(win *xwindow.Window, x, y, width, height int) error {
	// snapshot window dimensions
	base, err := win.Geometry()
	if err != nil {
		return err
	}

	// move window then wait...
	err = win.WMMoveResize(x, y, width, height)
	if err != nil {
		return err
	}
	err = PollFor(win, GeometryDiffers(base))
	if err != nil {
		return err
	}

	// check that the new geometry is what we requested
	// this may be inadvisable: what about window hints?
	geom, err := win.Geometry()
	if err != nil {
		return err
	}
	if geom.Width() != width || geom.Height() != height {
		// something derped! resize to make it right!
		// if window hints constrained us, this won't upset them
		log.Println("MoveResize: resizing again after incorrect new dimensions")
		err = win.WMResize(width, height)
		if err != nil {
			return err
		}
	}

	return nil
}
Beispiel #8
0
func newWindow(X *xgbutil.XUtil, width, height int) *xwindow.Window {
	var (
		err error
		win *xwindow.Window
	)
	win, err = xwindow.Generate(X)
	if err != nil {
		log.Fatal(err)
	}
	win.Create(X.RootWin(), 0, 0, width, height,
		xproto.CwBackPixel|xproto.CwEventMask,
		0, xproto.EventMaskButtonRelease)
	win.WMGracefulClose(
		func(w *xwindow.Window) {
			xevent.Detach(w.X, w.Id)
			mousebind.Detach(w.X, w.Id)
			w.Destroy()
			xevent.Quit(X)
		})

	win.Map()

	if err != nil {
		log.Fatal(err)
	}
	return win
}
Beispiel #9
0
// Put the incoming window on the `dir` side of the target,
// and transform the orthagonal dimension (eg, if `dir` is Up, then dim is `Width`
// to be the same as the target's dimension
// TODO: clip windows to display boundry
func Shove(target, incoming *xwindow.Window, dir Direction) error {
	// get geometries
	i, err := incoming.DecorGeometry()
	if err != nil {
		return err
	}

	t, err := target.DecorGeometry()
	if err != nil {
		return err
	}

	// move in the correct direction
	if dir == Top {
		err := MoveResize(incoming, t.X(), t.Y()-i.Height(), t.Width(), i.Height())
		if err != nil {
			return err
		}
	}

	if dir == Bottom {
		err := MoveResize(incoming, t.X(), t.Y()+t.Height(), t.Width(), i.Height())
		if err != nil {
			return err
		}
	}

	if dir == Left {
		err := MoveResize(incoming, t.X()-i.Width(), t.Y(), i.Width(), t.Height())
		if err != nil {
			return err
		}
	}

	if dir == Right {
		err := MoveResize(incoming, t.X()+t.Width(), t.Y(), i.Width(), t.Height())
		if err != nil {
			return err
		}
	}

	return nil
}
Beispiel #10
0
// move the incoming window so that it is directly adjacent to the target's edge
func AdjoinEdge(target, incoming *xwindow.Window, dir wm.Direction) error {
	t, err := target.DecorGeometry()
	if err != nil {
		return err
	}
	i, err := incoming.DecorGeometry()
	if err != nil {
		return err
	}

	delta := EdgePos(t, dir) - EdgePos(i, dir.Opposite())

	if dir == wm.Left || dir == wm.Right {
		return wm.Move(incoming, delta+i.X(), i.Y())
	} else {
		return wm.Move(incoming, i.X(), delta+i.Y())
	}
	return nil
}
Beispiel #11
0
func newWindow(X *xgbutil.XUtil, width, height int) *xwindow.Window {
	var (
		err error
		win *xwindow.Window
	)
	win, err = xwindow.Generate(X)
	if err != nil {
		log.Fatal(err)
	}
	win.Create(X.RootWin(), 0, 0, width, height,
		xproto.CwBackPixel|xproto.CwEventMask,
		0, xproto.EventMaskButtonRelease)
	win.WMGracefulClose(
		func(w *xwindow.Window) {
			xevent.Detach(w.X, w.Id)
			mousebind.Detach(w.X, w.Id)
			// w.Destroy()
			xevent.Quit(X)
			application.Exit()
		})

	// In order to get ConfigureNotify events, we must listen to the window
	// using the 'StructureNotify' mask.
	win.Listen(xproto.EventMaskStructureNotify)

	win.Map()

	xevent.ConfigureNotifyFun(
		func(X *xgbutil.XUtil, ev xevent.ConfigureNotifyEvent) {
			reshape(int(ev.Width), int(ev.Height))
		}).Connect(X, win.Id)

	// err = mousebind.ButtonReleaseFun(
	// 	func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) {
	// 		newWindow(X)
	// 	}).Connect(X, win.Id, "1", false, false)

	if err != nil {
		log.Fatal(err)
	}
	return win
}
Beispiel #12
0
func splitVertical(target, incoming *xwindow.Window, incomingOnTop bool) error {
	bounds, err := target.DecorGeometry()
	if err != nil {
		log.Printf("splitVertical: error getting bounds of target: %v\n", err)
		return err
	}

	bottom_height := bounds.Height() / 2
	top_height := bounds.Height() - bottom_height

	var top, bottom *xwindow.Window
	if incomingOnTop {
		top = incoming
		bottom = target
	} else {
		top = target
		bottom = incoming
	}

	// target goes on bottom...
	err = MoveResize(bottom, bounds.X(), bounds.Y()+top_height, bounds.Width(), bottom_height)
	if err != nil {
		log.Printf("splitVertical: error configuring bottom: %v\n", err)
		return err
	}

	// and incoming on top
	err = MoveResize(top, bounds.X(), bounds.Y(), bounds.Width(), top_height)
	if err != nil {
		log.Printf("splitVertical: error configuring top: %v\n", err)
		return err
	}

	// cool
	return nil
}
Beispiel #13
0
// Sometimes window managers are really slow about
// re-implemented here because under Fluxbox, win.WMMove() results in the window
// growing vertically by the height of the titlebar!
// So we snapshot the size of the window before we move it,
// move it, compare the sizes, then resize it vertically to be in line with our intentions
//
// this is synchronous: it waits for the window to finish moving before it releases control
// because it would be impossible to selectivley poll for just the move.
func Move(win *xwindow.Window, x, y int) error {
	// snapshot both sorts of window geometries
	decor_geom, geom, err := Geometries(win)
	if err != nil {
		return err
	}
	log.Printf("Move: detected geometry to be %v\n", geom)

	// move the window, then wait for it to finish moving
	err = win.WMMove(x, y)
	if err != nil {
		return err
	}

	// this waits 30MS under non-Fluxbox window manager
	// WHAT DO
	err = PollFor(win, GeometryDiffers(geom))
	if err != nil {
		// if we had a timeout, that means that the geometry didn't derp during
		// moving, and everything is A-OK!
		// skip the rest of the function
		if _, wasTimeout := err.(*TimeoutError); wasTimeout {
			return nil
		}
		return err
	}

	// compare window widths before/after move
	_, post_move_base, err := Geometries(win)
	if err != nil {
		return err
	}

	delta_w := post_move_base.Width() - geom.Width()
	delta_h := post_move_base.Height() - geom.Height()

	if delta_h != 0 || delta_w != 0 {
		// fluxbox has done it again. We issued a move, and we got a taller window, too!
		log.Printf("Move: resetting dimensions to %v due to w/h delta: %v/%v\n", geom, delta_w, delta_h)
		err = win.WMResize(geom.Width(), geom.Height())
		if err != nil {
			return err
		}

		// wait for that to succeed
		err = PollFor(win, GeometryDiffers(post_move_base))
		if err != nil {
			return err
		}
	}
	// make sure window did actually move
	err = PollFor(win, DecorDiffers(decor_geom))
	if err != nil {
		// if we had a timeout, that means that the window didn't move
		// we want to send an error mentioning that fact specifically
		// instead of a generic "lol timeout happan in polling :DDD"
		if te, wasTimeout := err.(*TimeoutError); wasTimeout {
			return &TimeoutError{"Move: window didn't move", te.Timeout}
		}
		// return whatever other error stymied the polling
		return err
	}
	return nil
}
Beispiel #14
0
// resize a window by a certain number of pixels in a given direction.
// This function tries to prevent the window from moving
func ResizeDirection(X *xgbutil.XUtil, win *xwindow.Window, dir wm.Direction, px int) error {
	// we resize around the decor_geometry of the window

	if px == 0 {
		// no need to resize
		return nil
	}

	geom, err := win.Geometry()
	if err != nil {
		return fmt.Errorf("Resize: coudn't get normal geometry: %v", err)
	}
	w, h := geom.Width(), geom.Height()
	log.Printf("ResizeDirection: pre_geom == %v\n", geom)

	if dir == wm.Left || dir == wm.Right {
		// horizontal resize
		w += px
	} else {
		h += px
	}

	// two-step resize -> move process, to compensate for WM peculiarities and window sizing hints
	// first save the initial position info
	pre_decor, err := win.DecorGeometry()
	if err != nil {
		return fmt.Errorf("Resize: coudn't get decorated geometry: %v", err)
	}

	// resize the window
	err = win.WMResize(w, h)
	if err != nil {
		return err
	}

	// wait for the geometry to change
	// we use a goroutine to query X a bunch while waiting for the window
	// to finish resizing
	err = wm.PollFor(win, wm.DecorDiffers(pre_decor), wm.GeometryDiffers(geom))
	if err != nil {
		return fmt.Errorf("ResizeDirection: error waiting for window geometries to change: %v", err)
	}

	post_decor, post_geom, err := wm.Geometries(win)
	if err != nil {
		return err
	}
	log.Printf("ResizeDirection: post_decor == %v\n", post_decor)
	log.Printf("ResizeDirection: post_geom == %v\n", post_geom)

	// the opposite edge should stay in the same place
	op := dir.Opposite()
	pre_edge := EdgePos(pre_decor, op)
	post_edge := EdgePos(post_decor, op)
	delta := post_edge - pre_edge

	x, y := post_decor.X(), post_decor.Y()

	// move the window upwards by our height resize so that the bottom edge stays in the same place
	if dir == wm.Top || dir == wm.Bottom {
		y -= delta
	}

	// move the window right  by our resize so that the right stays in the same place
	if dir == wm.Left || dir == wm.Right {
		x -= delta
	}

	// move to lock opposite edge
	return wm.Move(win, x, y)
}