Example #1
0
func NewXHandle(display string) (*xhandle, error) {
	c, err := xgb.NewConnDisplay(display)
	if err != nil {
		return nil, err
	}

	s := xproto.Setup(c)
	screen := s.DefaultScreen(c)

	kb, err := NewKeyboard(s, c)
	if err != nil {
		return nil, err
	}

	h := &xhandle{
		conn:     c,
		root:     screen.Root,
		Events:   make([]Event, 0, 1000),
		EvtsLck:  &sync.RWMutex{},
		keyboard: kb,
		Keys:     make(map[int]map[xproto.Window]map[Input][]Key),
		KeysLck:  &sync.RWMutex{},
	}

	return h, nil
}
Example #2
0
func newHeads(X *xgb.Conn) heads {
	var primaryHead *head
	var primaryOutput randr.Output

	root := xproto.Setup(X).DefaultScreen(X).Root
	resources, err := randr.GetScreenResourcesCurrent(X, root).Reply()
	if err != nil {
		log.Fatalf("Could not get screen resources: %s.", err)
	}

	primaryOutputReply, _ := randr.GetOutputPrimary(X, root).Reply()
	if primaryOutputReply != nil {
		primaryOutput = primaryOutputReply.Output
	}

	hds := make([]head, 0, len(resources.Outputs))
	off := make([]string, 0)
	disconnected := make([]string, 0)
	for i, output := range resources.Outputs {
		oinfo, err := randr.GetOutputInfo(X, output, 0).Reply()
		if err != nil {
			log.Fatalf("Could not get output info for screen %d: %s.", i, err)
		}
		outputName := string(oinfo.Name)

		if oinfo.Connection != randr.ConnectionConnected {
			disconnected = append(disconnected, outputName)
			continue
		}
		if oinfo.Crtc == 0 {
			off = append(off, outputName)
			continue
		}

		crtcinfo, err := randr.GetCrtcInfo(X, oinfo.Crtc, 0).Reply()
		if err != nil {
			log.Fatalf("Could not get crtc info for screen (%d, %s): %s.",
				i, outputName, err)
		}

		head := newHead(output, outputName, crtcinfo)
		if output == primaryOutput {
			primaryHead = &head
		}
		hds = append(hds, head)
	}
	if primaryHead == nil && len(hds) > 0 {
		tmp := hds[0]
		primaryHead = &tmp
	}

	hdsPrim := heads{
		primary:      primaryHead,
		heads:        hds,
		off:          off,
		disconnected: disconnected,
	}
	sort.Sort(hdsPrim)
	return hdsPrim
}
Example #3
0
// Lock grabs the keyboard and pointer locking the X11 display
func Lock(X *xgb.Conn) error {
	screen := xproto.Setup(X).DefaultScreen(X)

	passEventsToOwner := false
	kbCookie := xproto.GrabKeyboard(X, passEventsToOwner, screen.Root, xproto.TimeCurrentTime, xproto.GrabModeAsync, xproto.GrabModeAsync)
	kbReply, err := kbCookie.Reply()
	if err != nil {
		return err
	}
	if kbReply.Status != xproto.GrabStatusSuccess {
		return fmt.Errorf("GrabKeyboard status %v", grabStatus(kbReply.Status))
	}

	ptrCookie := xproto.GrabPointer(X, passEventsToOwner, screen.Root, 0, xproto.GrabModeAsync, xproto.GrabModeAsync, xproto.AtomNone, xproto.AtomNone, xproto.TimeCurrentTime)

	ptrReply, err := ptrCookie.Reply()
	if err != nil {
		xproto.UngrabKeyboard(X, xproto.TimeCurrentTime)
		return err
	}
	if ptrReply.Status != xproto.GrabStatusSuccess {
		return fmt.Errorf("GrabPointer status %v", grabStatus(kbReply.Status))
	}

	return nil
}
Example #4
0
func getPrimaryScreenBestResolution() (w uint16, h uint16) {
	// if connect to x failed, just return 1024x768
	w, h = 1024, 768

	XU, err := xgbutil.NewConn()
	if err != nil {
		return
	}
	err = randr.Init(XU.Conn())
	if err != nil {
		return
	}
	_, err = randr.QueryVersion(XU.Conn(), 1, 4).Reply()
	if err != nil {
		return
	}
	Root := xproto.Setup(XU.Conn()).DefaultScreen(XU.Conn()).Root
	resources, err := randr.GetScreenResources(XU.Conn(), Root).Reply()
	if err != nil {
		return
	}

	bestModes := make([]uint32, 0)
	for _, output := range resources.Outputs {
		reply, err := randr.GetOutputInfo(XU.Conn(), output, 0).Reply()
		if err == nil && reply.NumModes > 1 {
			bestModes = append(bestModes, uint32(reply.Modes[0]))
		}
	}

	w, h = 0, 0
	for _, m := range resources.Modes {
		for _, id := range bestModes {
			if id == m.Id {
				bw, bh := m.Width, m.Height
				if w == 0 || h == 0 {
					w, h = bw, bh
				} else if uint32(bw)*uint32(bh) < uint32(w)*uint32(h) {
					w, h = bw, bh
				}
			}
		}
	}

	if w == 0 || h == 0 {
		// get resource failed, use root window's geometry
		rootRect := xwindow.RootGeometry(XU)
		w, h = uint16(rootRect.Width()), uint16(rootRect.Height())
	}

	if w == 0 || h == 0 {
		w, h = 1024, 768 // default value
	}

	logger.Debugf("primary screen's best resolution is %dx%d", w, h)
	return
}
Example #5
0
func Load(c *xgb.Conn) error {
	ctxs = make(map[string]*gcontext)
	scr := xproto.Setup(c).DefaultScreen(c)
	err := loadGCS(c, scr)
	if err != nil {
		return err
	}
	return nil
}
Example #6
0
func (m *Mouse) Move(args *MoveArgs, reply *int) error {
	//fmt.Printf("%#v\n", args)
	screen := xproto.Setup(m.x11).DefaultScreen(m.x11)
	cookie := xtest.FakeInputChecked(m.x11, xproto.MotionNotify, 0, 0, screen.Root, int16(args.X), int16(args.Y), 0)
	if cookie.Check() != nil {
		fmt.Println("FakeInput failed")
	}

	return nil
}
func ScreenRect() (image.Rectangle, error) {
	c, err := xgb.NewConn()
	if err != nil {
		return image.Rectangle{}, err
	}
	defer c.Close()

	screen := xproto.Setup(c).DefaultScreen(c)
	x := screen.WidthInPixels
	y := screen.HeightInPixels

	return image.Rect(0, 0, int(x), int(y)), nil
}
Example #8
0
func main() {
	X, err := xgb.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	// Get the window id of the root window.
	setup := xproto.Setup(X)
	root := setup.DefaultScreen(X).Root

	// Get the atom id (i.e., intern an atom) of "_NET_ACTIVE_WINDOW".
	aname := "_NET_ACTIVE_WINDOW"
	activeAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
		aname).Reply()
	if err != nil {
		log.Fatal(err)
	}

	// Get the atom id (i.e., intern an atom) of "_NET_WM_NAME".
	aname = "_NET_WM_NAME"
	nameAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
		aname).Reply()
	if err != nil {
		log.Fatal(err)
	}

	// Get the actual value of _NET_ACTIVE_WINDOW.
	// Note that 'reply.Value' is just a slice of bytes, so we use an
	// XGB helper function, 'Get32', to pull an unsigned 32-bit integer out
	// of the byte slice. We then convert it to an X resource id so it can
	// be used to get the name of the window in the next GetProperty request.
	reply, err := xproto.GetProperty(X, false, root, activeAtom.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
	if err != nil {
		log.Fatal(err)
	}
	windowId := xproto.Window(xgb.Get32(reply.Value))
	fmt.Printf("Active window id: %X\n", windowId)

	// Now get the value of _NET_WM_NAME for the active window.
	// Note that this time, we simply convert the resulting byte slice,
	// reply.Value, to a string.
	reply, err = xproto.GetProperty(X, false, windowId, nameAtom.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Active window name: %s\n", string(reply.Value))
}
Example #9
0
func NewXHandle(display string, ewhm []string) (*xhandle, error) {
	c, err := xgb.NewConnDisplay(display)
	if err != nil {
		return nil, err
	}

	setup := xproto.Setup(c)
	screen := setup.DefaultScreen(c)
	meta, err := mkMeta(screen, c)
	if err != nil {
		return nil, err
	}
	motion, err := mkMotion(screen, c)
	if err != nil {
		return nil, err
	}

	h := &xhandle{
		conn:    c,
		setup:   setup,
		screen:  screen,
		root:    screen.Root,
		meta:    meta,
		motion:  motion,
		Events:  make([]Event, 0, 1000),
		EvtsLck: &sync.RWMutex{},
	}

	mr := NewMotionRecorder(h.conn, h.root, h.motion)
	h.pointer = NewPointer(mr)

	h.Windower = NewWindower(h.conn, h.root)

	h.InputFocus = NewInputFocus(h.conn, h.root)

	h.Atomic = atomic.New(h.conn)

	EWMH := ewmh.New(h.conn, h.root, h.Atomic)
	err = EWMH.SupportedSet(ewhm)
	if err != nil {
		return nil, err
	}

	h.Ewmh = NewEwmh(EWMH)
	//h.Ewmh.Set("string name", h.root, h.meta)

	return h, nil
}
Example #10
0
func (b *brightness) adjust(Xu *xgbutil.XUtil, increase bool) error {
	X := Xu.Conn()
	root := xproto.Setup(X).DefaultScreen(X).Root
	screens, err := randr.GetScreenResources(X, root).Reply()
	if err != nil {
		return fmt.Errorf("getting screen: %v", err)
	}

	for _, output := range screens.Outputs {
		query, err := randr.QueryOutputProperty(X, output, b.prop).Reply()
		if err != nil {
			if _, ok := err.(xproto.NameError); ok {
				// this output has no backlight
				continue
			}
			return fmt.Errorf("query backlight: %v", err)
		}
		if !query.Range {
			return errors.New("backlight brightness range not specified")
		}
		if len(query.ValidValues) != 2 {
			return fmt.Errorf("expected min and max, got: %v", query.ValidValues)
		}
		min, max := query.ValidValues[0], query.ValidValues[1]
		// log.Printf("backlight range: %d .. %d", min, max)

		get, err := randr.GetOutputProperty(X, output, b.prop, xproto.AtomNone, 0, 4, false, false).Reply()
		if err != nil {
			return fmt.Errorf("get backlight property: %v", err)
		}
		if get.Type != xproto.AtomInteger ||
			get.NumItems != 1 ||
			get.Format != 32 {
			return fmt.Errorf("backlight property value looks wrong")
		}
		old := *(*int32)(unsafe.Pointer(&get.Data[0]))
		// log.Printf("backlight data: %d", old)

		bri := delta5(old, min, max, increase)

		data := (*[4]byte)(unsafe.Pointer(&bri))[:]
		if err := randr.ChangeOutputPropertyChecked(X, output, b.prop, xproto.AtomInteger, 32, xproto.PropModeReplace, 1, data).Check(); err != nil {
			return err
		}
	}
	return nil
}
Example #11
0
func main() {

	//	os.Setenv("DISPLAY", "localhost:6080")

	c, err := xgb.NewConn()
	if err != nil {
		fmt.Println(err)
		return
	}
	screen := xproto.Setup(c).DefaultScreen(c)

	rect := image.Rect(0, 0, int(screen.WidthInPixels), int(screen.HeightInPixels))

	x, y := rect.Dx(), rect.Dy()

	xImg, err := xproto.GetImage(c,
		xproto.ImageFormatZPixmap,
		xproto.Drawable(screen.Root),
		int16(rect.Min.X),
		int16(rect.Min.Y),
		uint16(x),
		uint16(y),
		0xffffffff).Reply()

	if err != nil {
		fmt.Println("Error: %s\r\n", err)
	}

	data := xImg.Data
	for i := 0; i < len(data); i += 4 {
		data[i], data[i+2], data[i+3] = data[i+2], data[i], 255
	}

	img := &image.RGBA{
		data, 4 * x, image.Rect(0, 0, x, y)}

	z.FcheckParents("screen")
	f := z.FileW("screen")
	defer f.Close()

	png := jpeg.Encode(f, img, &jpeg.Options{90})

	fmt.Printf("End with png: %v", png)
}
Example #12
0
func (logger *WindowLogger) getCurWindowTitle() (name string, err error) {
	// Get the window id of the root window.
	setup := xproto.Setup(logger.X11Connection)
	root := setup.DefaultScreen(logger.X11Connection).Root

	// Get the atom id (i.e., intern an atom) of "_NET_ACTIVE_WINDOW".
	aname := "_NET_ACTIVE_WINDOW"
	activeAtom, err := xproto.InternAtom(logger.X11Connection, true, uint16(len(aname)),
		aname).Reply()
	if err != nil {
		return "", err
	}

	// Get the atom id (i.e., intern an atom) of "_NET_WM_NAME".
	aname = "_NET_WM_NAME"
	nameAtom, err := xproto.InternAtom(logger.X11Connection, true, uint16(len(aname)),
		aname).Reply()
	if err != nil {
		return "", err
	}

	// Get the actual value of _NET_ACTIVE_WINDOW.
	// Note that 'reply.Value' is just a slice of bytes, so we use an
	// XGB helper function, 'Get32', to pull an unsigned 32-bit integer out
	// of the byte slice. We then convert it to an X resource id so it can
	// be used to get the name of the window in the next GetProperty request.
	reply, err := xproto.GetProperty(logger.X11Connection, false, root, activeAtom.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
	if err != nil {
		return "", err
	}
	windowId := xproto.Window(xgb.Get32(reply.Value))

	// Now get the value of _NET_WM_NAME for the active window.
	// Note that this time, we simply convert the resulting byte slice,
	// reply.Value, to a string.
	reply, err = xproto.GetProperty(logger.X11Connection, false, windowId, nameAtom.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
	if err != nil {
		return "", err
	}
	return string(reply.Value), nil
}
Example #13
0
func newScreenImpl(xc *xgb.Conn) (*screenImpl, error) {
	s := &screenImpl{
		xc:      xc,
		xsi:     xproto.Setup(xc).DefaultScreen(xc),
		buffers: map[shm.Seg]*bufferImpl{},
		uploads: map[uint16]completion{},
		windows: map[xproto.Window]*windowImpl{},
	}
	if err := s.initAtoms(); err != nil {
		return nil, err
	}
	if err := s.initPictformats(); err != nil {
		return nil, err
	}
	if err := s.initWindow32(); err != nil {
		return nil, err
	}
	go s.run()
	return s, nil
}
Example #14
0
func main() {

	var serve *bool = flag.Bool("serve", false, "Listen for connections")

	flag.Usage = usage
	flag.Parse()

	if remote := os.Getenv("SSH_CONNECTION"); remote == "" && len(flag.Args()) != 0 {
		invoked_as := os.Args[0]
		actual_binary, err := os.Readlink("/proc/self/exe")
		if err != nil {
			log.Panic("/proc/self/exe doesn't exist!")
		}
		log.Print("Invoked as: '", invoked_as, "' (actual=", actual_binary, ")")
		log.Panic("Not yet implemented: Would have run locally")
		return
	}

	c, err := xgb.NewConn()
	if err != nil {
		log.Panic("cannot connect: %v\n", err)
	}
	s := xproto.Setup(c).DefaultScreen(c)

	rl_execute_reply, err := xproto.InternAtom(c, false, uint16(len(atomname)), atomname).Reply()
	if err != nil {
		panic(err)
	}
	rl_execute_atom := rl_execute_reply.Atom

	if *serve {
		//log.Printf("c = %v, s = %v, a = %v", c, s, rl_execute_atom)
		start_server(c, s, rl_execute_atom)
	} else {
		if len(flag.Args()) == 0 {
			usage()
		}
		connect(c, s, rl_execute_atom, fixup_args(flag.Args()))
	}
}
Example #15
0
func subscribeXEvents(ch chan<- Event, done <-chan struct{}) {
	X, err := xgb.NewConn()
	if err != nil {
		ch <- Event{Error: err}
		return
	}

	defer X.Close()
	if err = randr.Init(X); err != nil {
		ch <- Event{Error: err}
		return
	}

	root := xproto.Setup(X).DefaultScreen(X).Root

	eventMask := randr.NotifyMaskScreenChange |
		randr.NotifyMaskCrtcChange |
		randr.NotifyMaskOutputChange |
		randr.NotifyMaskOutputProperty

	err = randr.SelectInputChecked(X, root, uint16(eventMask)).Check()
	if err != nil {
		ch <- Event{Error: err}
		return
	}

	for {
		ev, err := X.WaitForEvent()
		select {
		case ch <- Event{Event: ev, Error: err}:
		case <-time.After(eventSendTimeout):
			continue
		case <-done:
			return
		}
		if err != nil {
			log.Fatal(err)
		}
	}
}
func CaptureRect(rect image.Rectangle) (*image.RGBA, error) {
	c, err := xgb.NewConn()
	if err != nil {
		return nil, err
	}
	defer c.Close()

	screen := xproto.Setup(c).DefaultScreen(c)
	x, y := rect.Dx(), rect.Dy()
	xImg, err := xproto.GetImage(c, xproto.ImageFormatZPixmap, xproto.Drawable(screen.Root), int16(rect.Min.X), int16(rect.Min.Y), uint16(x), uint16(y), 0xffffffff).Reply()
	if err != nil {
		return nil, err
	}

	data := xImg.Data
	for i := 0; i < len(data); i += 4 {
		data[i], data[i+2], data[i+3] = data[i+2], data[i], 255
	}

	img := &image.RGBA{data, 4 * x, image.Rect(0, 0, x, y)}
	return img, nil
}
Example #17
0
func Open(c *xgb.Conn, ctx, title, text string) (*Window, error) {
	gc, ok := ctxs[ctx]
	if !ok {
		return nil, BadContextError(ctx)
	}

	scr := xproto.Setup(c).DefaultScreen(c)
	wdwid, err := xproto.NewWindowId(c)
	if err != nil {
		return nil, err
	}

	lines := cutLines(c, gc.width-2*gc.border, gc.font, text)
	height := uint32(len(lines)) * gc.fontHeight

	var mask uint32 = xproto.CwBackPixel | xproto.CwOverrideRedirect | xproto.CwEventMask
	values := make([]uint32, 3)
	values[0] = scr.WhitePixel
	values[1] = 1
	values[2] = xproto.EventMaskExposure
	err = xproto.CreateWindowChecked(c, xproto.WindowClassCopyFromParent, wdwid, scr.Root,
		0, 0, uint16(gc.width), uint16(height+2*gc.border), 1,
		xproto.WindowClassInputOutput, scr.RootVisual,
		mask, values).Check()
	if err != nil {
		return nil, err
	}
	xproto.ChangeProperty(c, xproto.PropModeReplace, wdwid,
		xproto.AtomWmName, xproto.AtomString,
		8, uint32(len(title)), []byte(title))

	var wdw Window
	wdw.id = wdwid
	wdw.conn = c
	wdw.lines = lines
	wdw.gc = gc
	wdw.geom = types.Geometry{0, 0, int32(gc.width), int32(height + 2*gc.border)}
	return &wdw, nil
}
Example #18
0
func default1() {

	X, err := xgb.NewConn()
	if err != nil {
		fmt.Println(err)
		return
	}

	wid, _ := xproto.NewWindowId(X)
	screen := xproto.Setup(X).DefaultScreen(X)
	xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root,
		0, 0, 500, 500, 0,
		xproto.WindowClassInputOutput, screen.RootVisual,
		xproto.CwBackPixel|xproto.CwEventMask,
		[]uint32{
			0xffffffff,
			xproto.EventMaskStructureNotify |
				xproto.EventMaskKeyPress |
				xproto.EventMaskKeyRelease})

	xproto.MapWindow(X, wid)
	for {
		ev, xerr := X.WaitForEvent()
		if ev == nil && xerr == nil {
			fmt.Println("Both event and error are nil. Exiting...")
			return
		}

		if ev != nil {
			fmt.Println("Event: %s\r\n", ev)
		}
		if xerr != nil {
			fmt.Println("Error: %s\r\n", xerr)
		}

	}

}
Example #19
0
func Focused(c *xgb.Conn) uint32 {
	incookie := xproto.GetInputFocus(c)
	rep, err := incookie.Reply()
	if err != nil {
		return 0
	}
	win := rep.Focus

	trcookie := xproto.TranslateCoordinates(c, win,
		xproto.Setup(c).DefaultScreen(c).Root,
		0, 0)
	att, err := trcookie.Reply()
	if err != nil {
		return 0
	}
	x, y := int32(att.DstX), int32(att.DstY)

	for i, size := range sizes {
		if size.X <= x && size.X+size.W >= x && size.Y <= y && size.Y+size.H >= y {
			return uint32(i)
		}
	}
	return 0
}
Example #20
0
func setStatus(status []byte, X *xgb.Conn) {
	screen := xproto.Setup(X).DefaultScreen(X)
	fmt.Println(string(status))
	setWindowTitle(status, X, screen.Root)
}
Example #21
0
func main() {
	X, _ := xgb.NewConn()

	// Every extension must be initialized before it can be used.
	err := randr.Init(X)
	if err != nil {
		log.Fatal(err)
	}

	// Get the root window on the default screen.
	root := xproto.Setup(X).DefaultScreen(X).Root

	// Gets the current screen resources. Screen resources contains a list
	// of names, crtcs, outputs and modes, among other things.
	resources, err := randr.GetScreenResources(X, root).Reply()
	if err != nil {
		log.Fatal(err)
	}

	// Iterate through all of the outputs and show some of their info.
	for _, output := range resources.Outputs {
		info, err := randr.GetOutputInfo(X, output, 0).Reply()
		if err != nil {
			log.Fatal(err)
		}

		bestMode := info.Modes[0]
		for _, mode := range resources.Modes {
			if mode.Id == uint32(bestMode) {
				fmt.Printf("Width: %d, Height: %d\n", mode.Width, mode.Height)
			}
		}
	}

	fmt.Println("\n")

	// Iterate through all of the crtcs and show some of their info.
	for _, crtc := range resources.Crtcs {
		info, err := randr.GetCrtcInfo(X, crtc, 0).Reply()
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("X: %d, Y: %d, Width: %d, Height: %d\n",
			info.X, info.Y, info.Width, info.Height)
	}

	// Tell RandR to send us events. (I think these are all of them, as of 1.3.)
	err = randr.SelectInputChecked(X, root,
		randr.NotifyMaskScreenChange|
			randr.NotifyMaskCrtcChange|
			randr.NotifyMaskOutputChange|
			randr.NotifyMaskOutputProperty).Check()
	if err != nil {
		log.Fatal(err)
	}

	// Listen to events and just dump them to standard out.
	// A more involved approach will have to read the 'U' field of
	// RandrNotifyEvent, which is a union (really a struct) of type
	// RanrNotifyDataUnion.
	for {
		ev, err := X.WaitForEvent()
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(ev)
	}
}
Example #22
0
func main() {
	X, err := xgb.NewConn()
	if err != nil {
		fmt.Println(err)
		return
	}

	wid, _ := xproto.NewWindowId(X)
	screen := xproto.Setup(X).DefaultScreen(X)
	xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root,
		0, 0, 240, 240, 0,
		xproto.WindowClassInputOutput,
		screen.RootVisual,
		xproto.CwBackPixel|xproto.CwEventMask,
		[]uint32{ // values must be in the order defined by the protocol
			0xffffffff,
			xproto.EventMaskStructureNotify |
				xproto.EventMaskKeyPress |
				xproto.EventMaskKeyRelease})

	xproto.MapWindow(X, wid)
	fmt.Printf("%d %d\n", screen.AllowedDepths[0].Visuals[0].VisualId, screen.RootVisual)

	var (
		ux, uy float64 = 1, 1

		fe                   cairo.FontExtents
		te                   cairo.TextExtents
		text                 = "joy"
		x, y, px, dashlength float64
	)

	surface := cairo.NewSurfaceFromXCB(xproto.Drawable(wid), screen.AllowedDepths[0].Visuals[0], 240, 240)
	surface.Scale(240, 240)
	surface.SetFontSize(0.5)

	/* Drawing code goes here */
	surface.SetSourceRGB(0.0, 0.0, 0.0)
	surface.SelectFontFace("Georgia", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
	surface.FontExtents(&fe)

	ux, uy = surface.DeviceToUserDistance(ux, uy)
	if ux > uy {
		px = ux
	} else {
		px = uy
	}

	surface.FontExtents(&fe)
	surface.TextExtents(text, &te)
	x = 0.5 - te.X_bearing - te.Width/2
	y = 0.5 - fe.Descent + fe.Height/2

	/* baseline, descent, ascent, height */
	surface.SetLineWidth(4 * px)
	dashlength = 9 * px
	surface.SetDash([]float64{dashlength}, 0)
	surface.SetSourceRGBA(0, 0.6, 0, 0.5)
	surface.MoveTo(x+te.X_bearing, y)
	surface.RelLineTo(te.Width, 0)
	surface.MoveTo(x+te.X_bearing, y+fe.Descent)
	surface.RelLineTo(te.Width, 0)
	surface.MoveTo(x+te.X_bearing, y-fe.Ascent)
	surface.RelLineTo(te.Width, 0)
	surface.MoveTo(x+te.X_bearing, y-fe.Height)
	surface.RelLineTo(te.Width, 0)
	surface.Stroke()

	/* extents: width & height */
	surface.SetSourceRGBA(0, 0, 0.75, 0.5)
	surface.SetLineWidth(px)
	dashlength = 3 * px
	surface.SetDash([]float64{dashlength}, 0)
	surface.Rectangle(x+te.X_bearing, y+te.Y_bearing, te.Width, te.Height)
	surface.Stroke()

	/* text */
	surface.MoveTo(x, y)
	surface.SetSourceRGB(0, 0, 0)
	surface.ShowText(text)

	/* bearing */
	surface.SetDash(nil, 0)
	surface.SetLineWidth(2 * px)
	surface.SetSourceRGBA(0, 0, 0.75, 0.5)
	surface.MoveTo(x, y)
	surface.RelLineTo(te.X_bearing, te.Y_bearing)
	surface.Stroke()

	/* text's advance */
	surface.SetSourceRGBA(0, 0, 0.75, 0.5)
	surface.Arc(x+te.X_advance, y+te.Y_advance, 5*px, 0, 2*math.Pi)
	surface.Fill()

	/* reference point */
	surface.Arc(x, y, 5*px, 0, 2*math.Pi)
	surface.SetSourceRGBA(0.75, 0, 0, 0.5)
	surface.Fill()

	surface.Finish()
	surface.Destroy()

	for {
		ev, xerr := X.WaitForEvent()
		if ev == nil && xerr == nil {
			// fmt.Println("Both event and error are nil. Exiting...")
			return
		}

		if ev != nil {
			// fmt.Printf("Event: %s\n", ev)
		}
		if xerr != nil {
			// fmt.Printf("Error: %s\n", xerr)
		}
	}
}
Example #23
0
func main() {
	// Open the connection to the X server
	X, err := xgb.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	// geometric objects
	points := []xproto.Point{
		{10, 10},
		{10, 20},
		{20, 10},
		{20, 20}}

	polyline := []xproto.Point{
		{50, 10},
		{5, 20}, // rest of points are relative
		{25, -20},
		{10, 10}}

	segments := []xproto.Segment{
		{100, 10, 140, 30},
		{110, 25, 130, 60}}

	rectangles := []xproto.Rectangle{
		{10, 50, 40, 20},
		{80, 50, 10, 40}}

	arcs := []xproto.Arc{
		{10, 100, 60, 40, 0, 90 << 6},
		{90, 100, 55, 40, 0, 270 << 6}}

	setup := xproto.Setup(X)
	// Get the first screen
	screen := setup.DefaultScreen(X)

	// Create black (foreground) graphic context
	foreground, _ := xproto.NewGcontextId(X)
	mask := uint32(xproto.GcForeground | xproto.GcGraphicsExposures)
	values := []uint32{screen.BlackPixel, 0}
	xproto.CreateGC(X, foreground, xproto.Drawable(screen.Root), mask, values)

	// Ask for our window's Id
	win, _ := xproto.NewWindowId(X)
	winDrawable := xproto.Drawable(win)

	// Create the window
	mask = uint32(xproto.CwBackPixel | xproto.CwEventMask)
	values = []uint32{screen.WhitePixel, xproto.EventMaskExposure}
	xproto.CreateWindow(X, // Connection
		screen.RootDepth, // Depth
		win,              // Window Id
		screen.Root,      // Parent Window
		0, 0,             // x, y
		150, 150, // width, height
		10, // border_width
		xproto.WindowClassInputOutput, // class
		screen.RootVisual,             // visual
		mask, values)                  // masks

	// Map the window on the screen
	xproto.MapWindow(X, win)

	for {
		evt, err := X.WaitForEvent()
		switch evt.(type) {
		case xproto.ExposeEvent:
			/* We draw the points */
			xproto.PolyPoint(X, xproto.CoordModeOrigin, winDrawable, foreground, points)

			/* We draw the polygonal line */
			xproto.PolyLine(X, xproto.CoordModePrevious, winDrawable, foreground, polyline)

			/* We draw the segments */
			xproto.PolySegment(X, winDrawable, foreground, segments)

			/* We draw the rectangles */
			xproto.PolyRectangle(X, winDrawable, foreground, rectangles)

			/* We draw the arcs */
			xproto.PolyArc(X, winDrawable, foreground, arcs)

		default:
			/* Unknown event type, ignore it */
		}

		if err != nil {
			log.Fatal(err)
		}
	}
	return
}
Example #24
0
func main() {
	// Open the connection to the X server
	X, err := xgb.NewConn()
	if err != nil {
		log.Fatal(err)
	}
	defer X.Close()

	// geometric objects
	points := []xproto.Point{
		{10, 10},
		{10, 20},
		{20, 10},
		{20, 20}}

	polyline := []xproto.Point{
		{50, 10},
		{5, 20}, // rest of points are relative
		{25, -20},
		{10, 10}}

	segments := []xproto.Segment{
		{100, 10, 140, 30},
		{110, 25, 130, 60}}

	rectangles := []xproto.Rectangle{
		{10, 50, 40, 20},
		{80, 50, 10, 40}}

	arcs := []xproto.Arc{
		{10, 100, 60, 40, 0, 90 << 6},
		{90, 100, 55, 40, 0, 270 << 6}}

	setup := xproto.Setup(X)
	// Get the first screen
	screen := setup.DefaultScreen(X)

	// Create black (foreground) graphic context
	foreground, _ := xproto.NewGcontextId(X)
	mask := uint32(xproto.GcForeground | xproto.GcGraphicsExposures)
	values := []uint32{screen.BlackPixel, 0}
	xproto.CreateGC(X, foreground, xproto.Drawable(screen.Root), mask, values)

	// Ask for our window's Id
	win, _ := xproto.NewWindowId(X)
	winDrawable := xproto.Drawable(win)

	// Create the window
	mask = uint32(xproto.CwBackPixel | xproto.CwEventMask)
	values = []uint32{screen.WhitePixel, xproto.EventMaskExposure}
	xproto.CreateWindow(X, // Connection
		screen.RootDepth, // Depth
		win,              // Window Id
		screen.Root,      // Parent Window
		0, 0,             // x, y
		150, 150, // width, height
		10, // border_width
		xproto.WindowClassInputOutput, // class
		screen.RootVisual,             // visual
		mask, values)                  // masks

	// Map the window on the screen
	xproto.MapWindow(X, win)

	// Obey the window-delete protocol
	tp := "WM_PROTOCOLS"
	prp := "WM_DELETE_WINDOW"
	typeAtom, _ := xproto.InternAtom(X, true, uint16(len(tp)), tp).Reply()
	propertyAtom, _ := xproto.InternAtom(X, true, uint16(len(prp)), prp).Reply()

	// It turns out that we need the window ID as a byte-stream... WTF!!
	// xprop.ChangeProp(xu, win, 8, "WM_NAME", "STRING", ([]byte)(name))
	// ChangeProp(xu *xgbutil.XUtil, win xproto.Window, format byte, prop string, typ string, data []byte)
	data := make([]byte, 4)
	xgb.Put32(data, uint32(propertyAtom.Atom))
	xproto.ChangeProperty(X, xproto.PropModeReplace, win, typeAtom.Atom, xproto.AtomAtom, 32, 1, data)

	for {
		evt, err := X.WaitForEvent()
		fmt.Printf("An event of type %T occured.\n", evt)

		if evt == nil && err == nil {
			fmt.Println("Exiting....")
			return
		} else if err != nil {
			log.Fatal(err)
		}

		switch event := evt.(type) {
		case xproto.ExposeEvent:
			/* We draw the points */
			xproto.PolyPoint(X, xproto.CoordModeOrigin, winDrawable, foreground, points)

			/* We draw the polygonal line */
			xproto.PolyLine(X, xproto.CoordModePrevious, winDrawable, foreground, polyline)

			/* We draw the segments */
			xproto.PolySegment(X, winDrawable, foreground, segments)

			/* We draw the rectangles */
			xproto.PolyRectangle(X, winDrawable, foreground, rectangles)

			/* We draw the arcs */
			xproto.PolyArc(X, winDrawable, foreground, arcs)
		case xproto.ClientMessageEvent:
			if len(event.Data.Data32) > 0 {
				data := xproto.Atom(event.Data.Data32[0])
				if data == propertyAtom.Atom {
					return
				} else {
					atomName, _ := xproto.GetAtomName(X, data).Reply()
					fmt.Println(atomName.Name)
				}
			} else {
				atomName, _ := xproto.GetAtomName(X, event.Type).Reply()
				fmt.Println(atomName.Name)
			}
		default:
			/* Unknown event type, ignore it */
		}
	}
	return
}
Example #25
0
func main() {
	// Open the connection to the X server
	X, err := xgb.NewConn()
	if err != nil {
		log.Fatal(err)
	}
	defer X.Close()

	setup := xproto.Setup(X)
	// Get the first screen
	screen := setup.DefaultScreen(X)

	// Replace existing window manager
	wmName := fmt.Sprintf("WM_S%d", X.DefaultScreen)
	managerAtom, err := xproto.InternAtom(X, true, uint16(len(wmName)), wmName).Reply()
	if err != nil {
		log.Fatal(err)
	}

	fakeWindow, _ := xproto.NewWindowId(X)
	xproto.CreateWindow(X, // Connection
		screen.RootDepth, // Depth
		fakeWindow,       // Window Id
		screen.Root,      // Parent Window
		-1000, -1000,     // x, y
		1, 1, // width, height
		0, // border_width
		xproto.WindowClassInputOutput, // class
		screen.RootVisual,             // visual
		xproto.CwEventMask|xproto.CwOverrideRedirect,
		[]uint32{1, xproto.EventMaskPropertyChange}) // masks
	xproto.MapWindow(X, fakeWindow)
	err = xproto.SetSelectionOwnerChecked(X, fakeWindow, managerAtom.Atom, xproto.TimeCurrentTime).Check()
	if err != nil {
		fmt.Println("foo")
		log.Fatal(err)
	}

	arcs := []xproto.Arc{
		{10, 100, 60, 40, 0, 90 << 6},
		{90, 100, 55, 40, 0, 270 << 6}}

	// Create black (foreground) graphic context
	foreground, _ := xproto.NewGcontextId(X)
	mask := uint32(xproto.GcForeground | xproto.GcGraphicsExposures)
	values := []uint32{screen.BlackPixel, 0}
	xproto.CreateGC(X, foreground, xproto.Drawable(screen.Root), mask, values)

	// Ask for our window's Id
	win, _ := xproto.NewWindowId(X)
	winDrawable := xproto.Drawable(win)

	// Create the window
	mask = uint32(xproto.CwBackPixel | xproto.CwEventMask)
	values = []uint32{screen.WhitePixel, xproto.EventMaskExposure}
	xproto.CreateWindow(X, // Connection
		screen.RootDepth, // Depth
		win,              // Window Id
		screen.Root,      // Parent Window
		0, 0,             // x, y
		150, 150, // width, height
		10, // border_width
		xproto.WindowClassInputOutput, // class
		screen.RootVisual,             // visual
		mask, values)                  // masks

	// Map the window on the screen
	xproto.MapWindow(X, win)

	// Obey the window-delete protocol
	tp := "WM_PROTOCOLS"
	prp := "WM_DELETE_WINDOW"
	typeAtom, _ := xproto.InternAtom(X, true, uint16(len(tp)), tp).Reply()
	propertyAtom, _ := xproto.InternAtom(X, true, uint16(len(prp)), prp).Reply()

	data := make([]byte, 4)
	xgb.Put32(data, uint32(propertyAtom.Atom))
	xproto.ChangeProperty(X, xproto.PropModeReplace, win, typeAtom.Atom, xproto.AtomAtom, 32, 1, data)

	// Main loop
	for {
		evt, err := X.WaitForEvent()
		fmt.Printf("An event of type %T occured.\n", evt)

		if evt == nil && err == nil {
			fmt.Println("Exiting....")
			return
		} else if err != nil {
			log.Fatal(err)
		}

		switch event := evt.(type) {
		case xproto.ExposeEvent:
			/* We draw the arcs */
			xproto.PolyArc(X, winDrawable, foreground, arcs)
		case xproto.ClientMessageEvent:
			if len(event.Data.Data32) > 0 {
				data := xproto.Atom(event.Data.Data32[0])
				if data == propertyAtom.Atom {
					return
				} else {
					atomName, _ := xproto.GetAtomName(X, data).Reply()
					fmt.Println(atomName.Name)
				}
			} else {
				atomName, _ := xproto.GetAtomName(X, event.Type).Reply()
				fmt.Println(atomName.Name)
			}
		default:
			/* Unknown event type, ignore it */
		}
	}
	return
}
Example #26
0
// NewConnXgb use the specific xgb.Conn to create a new XUtil.
//
//  NewConn, NewConnDisplay are wrapper of this function.
func NewConnXgb(c *xgb.Conn) (*XUtil, error) {
	setup := xproto.Setup(c)
	screen := setup.DefaultScreen(c)

	// Initialize our central struct that stores everything.
	xu := &XUtil{
		conn:             c,
		Quit:             false,
		Evqueue:          make([]EventOrError, 0, 1000),
		EvqueueLck:       &sync.RWMutex{},
		setup:            setup,
		screen:           screen,
		root:             screen.Root,
		eventTime:        xproto.Timestamp(0), // last event time
		Atoms:            make(map[string]xproto.Atom, 50),
		AtomsLck:         &sync.RWMutex{},
		AtomNames:        make(map[xproto.Atom]string, 50),
		AtomNamesLck:     &sync.RWMutex{},
		Callbacks:        make(map[int]map[xproto.Window][]Callback, 33),
		CallbacksLck:     &sync.RWMutex{},
		Hooks:            make([]CallbackHook, 0),
		HooksLck:         &sync.RWMutex{},
		Keymap:           nil, // we don't have anything yet
		Modmap:           nil,
		KeyRedirect:      0,
		Keybinds:         make(map[KeyKey][]CallbackKey, 10),
		KeybindsLck:      &sync.RWMutex{},
		Keygrabs:         make(map[KeyKey]int, 10),
		Keystrings:       make([]KeyString, 0, 10),
		Mousebinds:       make(map[MouseKey][]CallbackMouse, 10),
		MousebindsLck:    &sync.RWMutex{},
		Mousegrabs:       make(map[MouseKey]int, 10),
		InMouseDrag:      false,
		MouseDragStepFun: nil,
		MouseDragEndFun:  nil,
		ErrorHandler:     func(err xgb.Error) { Logger.Println(err) },
	}

	var err error = nil
	// Create a general purpose graphics context
	xu.gc, err = xproto.NewGcontextId(xu.conn)
	if err != nil {
		return nil, err
	}
	xproto.CreateGC(xu.conn, xu.gc, xproto.Drawable(xu.root),
		xproto.GcForeground, []uint32{xu.screen.WhitePixel})

	// Create a dummy window
	xu.dummy, err = xproto.NewWindowId(xu.conn)
	if err != nil {
		return nil, err
	}
	xproto.CreateWindow(xu.conn, xu.Screen().RootDepth, xu.dummy, xu.RootWin(),
		-1000, -1000, 1, 1, 0,
		xproto.WindowClassInputOutput, xu.Screen().RootVisual,
		xproto.CwEventMask|xproto.CwOverrideRedirect,
		[]uint32{1, xproto.EventMaskPropertyChange})
	xproto.MapWindow(xu.conn, xu.dummy)

	// Register the Xinerama extension... because it doesn't cost much.
	err = xinerama.Init(xu.conn)

	// If we can't register Xinerama, that's okay. Output something
	// and move on.
	if err != nil {
		Logger.Printf("WARNING: %s\n", err)
		Logger.Printf("MESSAGE: The 'xinerama' package cannot be used " +
			"because the XINERAMA extension could not be loaded.")
	}

	return xu, nil
}
Example #27
0
func main() {
	var err error
	xConn, err = xgb.NewConn()
	if err != nil {
		log.Fatal(err)
	}
	if err = xinerama.Init(xConn); err != nil {
		log.Fatal(err)
	}
	xSetup := xp.Setup(xConn)
	if len(xSetup.Roots) != 1 {
		log.Fatalf("X setup has unsupported number of roots: %d", len(xSetup.Roots))
	}
	rootXWin = xSetup.Roots[0].Root

	becomeTheWM()
	initAtoms()
	initDesktop(&xSetup.Roots[0])
	initKeyboardMapping()
	initScreens()

	// Manage any existing windows.
	if tree, err := xp.QueryTree(xConn, rootXWin).Reply(); err != nil {
		log.Fatal(err)
	} else if tree != nil {
		for _, c := range tree.Children {
			if c == desktopXWin {
				continue
			}
			attrs, err := xp.GetWindowAttributes(xConn, c).Reply()
			if attrs == nil || err != nil {
				continue
			}
			if attrs.OverrideRedirect || attrs.MapState == xp.MapStateUnmapped {
				continue
			}
			manage(c, false)
		}
	}

	// Process X events.
	eeChan := make(chan xEventOrError)
	go func() {
		for {
			e, err := xConn.WaitForEvent()
			eeChan <- xEventOrError{e, err}
		}
	}()
	for {
		for i, c := range checkers {
			if err := c.Check(); err != nil {
				log.Println(err)
			}
			checkers[i] = nil
		}
		checkers = checkers[:0]

		select {
		case f := <-proactiveChan:
			f()
		case ee := <-eeChan:
			if ee.error != nil {
				log.Println(ee.error)
				continue
			}
			switch e := ee.event.(type) {
			case xp.ButtonPressEvent:
				eventTime = e.Time
				handleButtonPress(e)
			case xp.ButtonReleaseEvent:
				eventTime = e.Time
			case xp.ClientMessageEvent:
				// No-op.
			case xp.ConfigureNotifyEvent:
				// No-op.
			case xp.ConfigureRequestEvent:
				handleConfigureRequest(e)
			case xp.DestroyNotifyEvent:
				// No-op.
			case xp.EnterNotifyEvent:
				eventTime = e.Time
				handleEnterNotify(e)
			case xp.ExposeEvent:
				handleExpose(e)
			case xp.KeyPressEvent:
				eventTime, keyRootX, keyRootY, keyState = e.Time, e.RootX, e.RootY, e.State
				handleKeyPress(e)
			case xp.KeyReleaseEvent:
				eventTime, keyRootX, keyRootY, keyState = e.Time, 0, 0, 0
			case xp.MapNotifyEvent:
				// No-op.
			case xp.MappingNotifyEvent:
				// No-op.
			case xp.MapRequestEvent:
				manage(e.Window, true)
			case xp.MotionNotifyEvent:
				eventTime = e.Time
				handleMotionNotify(e)
			case xp.UnmapNotifyEvent:
				unmanage(e.Window)
			default:
				log.Printf("unhandled event: %v", ee.event)
			}
		}
	}
}
Example #28
0
func main() {
	X, err := xgb.NewConn()
	if err != nil {
		fmt.Println(err)
		return
	}

	// xproto.Setup retrieves the Setup information from the setup bytes
	// gathered during connection.
	setup := xproto.Setup(X)

	// This is the default screen with all its associated info.
	screen := setup.DefaultScreen(X)

	// Any time a new resource (i.e., a window, pixmap, graphics context, etc.)
	// is created, we need to generate a resource identifier.
	// If the resource is a window, then use xproto.NewWindowId. If it's for
	// a pixmap, then use xproto.NewPixmapId. And so on...
	wid, _ := xproto.NewWindowId(X)

	// CreateWindow takes a boatload of parameters.
	xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root,
		0, 0, 500, 500, 0,
		xproto.WindowClassInputOutput, screen.RootVisual, 0, []uint32{})

	// This call to ChangeWindowAttributes could be factored out and
	// included with the above CreateWindow call, but it is left here for
	// instructive purposes. It tells X to send us events when the 'structure'
	// of the window is changed (i.e., when it is resized, mapped, unmapped,
	// etc.) and when a key press or a key release has been made when the
	// window has focus.
	// We also set the 'BackPixel' to white so that the window isn't butt ugly.
	xproto.ChangeWindowAttributes(X, wid,
		xproto.CwBackPixel|xproto.CwEventMask,
		[]uint32{ // values must be in the order defined by the protocol
			0xffffffff,
			xproto.EventMaskStructureNotify |
				xproto.EventMaskKeyPress |
				xproto.EventMaskKeyRelease})

	// MapWindow makes the window we've created appear on the screen.
	// We demonstrated the use of a 'checked' request here.
	// A checked request is a fancy way of saying, "do error handling
	// synchronously." Namely, if there is a problem with the MapWindow request,
	// we'll get the error *here*. If we were to do a normal unchecked
	// request (like the above CreateWindow and ChangeWindowAttributes
	// requests), then we would only see the error arrive in the main event
	// loop.
	//
	// Typically, checked requests are useful when you need to make sure they
	// succeed. Since they are synchronous, they incur a round trip cost before
	// the program can continue, but this is only going to be noticeable if
	// you're issuing tons of requests in succession.
	//
	// Note that requests without replies are by default unchecked while
	// requests *with* replies are checked by default.
	err = xproto.MapWindowChecked(X, wid).Check()
	if err != nil {
		fmt.Printf("Checked Error for mapping window %d: %s\n", wid, err)
	} else {
		fmt.Printf("Map window %d successful!\n", wid)
	}

	// This is an example of an invalid MapWindow request and what an error
	// looks like.
	err = xproto.MapWindowChecked(X, 0).Check()
	if err != nil {
		fmt.Printf("Checked Error for mapping window 0x1: %s\n", err)
	} else { // neva
		fmt.Printf("Map window 0x1 successful!\n")
	}

	// Start the main event loop.
	for {
		// WaitForEvent either returns an event or an error and never both.
		// If both are nil, then something went wrong and the loop should be
		// halted.
		//
		// An error can only be seen here as a response to an unchecked
		// request.
		ev, xerr := X.WaitForEvent()
		if ev == nil && xerr == nil {
			fmt.Println("Both event and error are nil. Exiting...")
			return
		}

		if ev != nil {
			fmt.Printf("Event: %s\n", ev)
		}
		if xerr != nil {
			fmt.Printf("Error: %s\n", xerr)
		}
	}
}
Example #29
0
// Listen ...
func Listen(fn func(e Event)) error {
	X, err := xgb.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	setup := xproto.Setup(X)
	root := setup.DefaultScreen(X).Root

	var prevWindowID xproto.Window
	var prevClasses []string

	for {
		<-time.After(msRefresh * time.Millisecond)

		// From one of the xgb examples.
		aname := "_NET_ACTIVE_WINDOW"
		activeAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
			aname).Reply()
		if err != nil {
			log.Println(err)
			continue
		}

		reply, err := xproto.GetProperty(X, false, root, activeAtom.Atom,
			xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
		if err != nil {
			log.Println(err)
			continue
		}

		windowID := xproto.Window(xgb.Get32(reply.Value))

		aname = "WM_CLASS"
		nameAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
			aname).Reply()
		if err != nil {
			log.Println(err)
			continue
		}

		reply, err = xproto.GetProperty(X, false, windowID, nameAtom.Atom,
			xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
		if err != nil {
			log.Println(err)
			continue
		}

		classes := stringSlice(reply.Value)

		// Check if active window has changed since last time.
		if windowID != prevWindowID {
			fn(Lost{
				WMClass: prevClasses,
			})
			fn(Gained{
				WMClass: classes,
			})
			prevWindowID = windowID
			prevClasses = classes
		}
	}
}