Beispiel #1
0
func (c *Client) handleClientMessage(name string, data []uint32) {
	switch name {
	case "WM_CHANGE_STATE":
		if data[0] == icccm.StateIconic && !c.iconified {
			c.IconifyToggle()
		}
	case "_NET_ACTIVE_WINDOW":
		c.Focus()
		c.Raise()
	case "_NET_CLOSE_WINDOW":
		c.Close()
	case "_NET_MOVERESIZE_WINDOW":
		// The data[0] element contains bit-packed information. See
		// EWMH _NET_MOVERESIZE_WINDOW for the deets.
		gravity := int(data[0] & 0xff)
		xflags := int((data[0] >> 8) & 0xf)
		x, y, w, h := frame.ClientToFrame(c.frame, gravity,
			int(data[1]), int(data[2]), int(data[3]), int(data[4]))
		c.LayoutMROpt(xflags, x, y, w, h)
	case "_NET_RESTACK_WINDOW":
		// We basically treat this as a request to stack the window.
		// We ignore the sibling. Maybe someday we can support that, but eh...
		c.Raise()
	case "_NET_WM_DESKTOP":
		if data[0] == 0xFFFFFFFF {
			c.stick()
			return
		}
		if wrk := wm.Heads.Workspaces.Get(int(data[0])); wrk != nil {
			wrk.Add(c)
		} else {
			logger.Warning.Printf(
				"_NET_WM_DESKTOP ClientMessage: No workspace indexed at '%d' "+
					"exists.", data[0])
		}
	case "_NET_WM_STATE":
		prop1, _ := xprop.AtomName(wm.X, xproto.Atom(data[1]))
		prop2, _ := xprop.AtomName(wm.X, xproto.Atom(data[2]))
		switch data[0] {
		case 0:
			c.updateStates("remove", prop1, prop2)
		case 1:
			c.updateStates("add", prop1, prop2)
		case 2:
			c.updateStates("toggle", prop1, prop2)
		default:
			logger.Warning.Printf(
				"_NET_WM_STATE: Unknown action '%d'.", data[0])
		}
	default:
		logger.Warning.Printf("Unknown ClientMessage for '%s': %s.", c, name)
	}
}
Beispiel #2
0
func handleClientMessages(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) {
	name, err := xprop.AtomName(X, ev.Type)
	if err != nil {
		logger.Warning.Printf("Could not get atom name for '%s': %s", ev, err)
		return
	}
	switch name {
	case "_NET_NUMBER_OF_DESKTOPS":
		logger.Warning.Printf("Wingo does not support adding/removing " +
			"desktops using the _NET_NUMBER_OF_DESKTOPS property. Please use " +
			"the Wingo commands 'AddWorkspace' and 'RemoveWorkspace' to add " +
			"or remove workspaces.")
	case "_NET_DESKTOP_GEOMETRY":
		logger.Warning.Printf("Wingo does not support the " +
			"_NET_DESKTOP_GEOMETRY property. Namely, more than one workspace " +
			"can be visible at a time, so different workspaces can have " +
			"different geometries.")
	case "_NET_DESKTOP_VIEWPORT":
		logger.Warning.Printf("Wingo does not use viewports, and therefore " +
			"does not support the _NET_DESKTOP_VIEWPORT property.")
	case "_NET_CURRENT_DESKTOP":
		index := int(ev.Data.Data32[0])
		if wrk := wm.Heads.Workspaces.Get(index); wrk != nil {
			wm.SetWorkspace(wrk, false)
			wm.FocusFallback()
		} else {
			logger.Warning.Printf("Desktop index %d is not in the range "+
				"[0, %d).", index, len(wm.Heads.Workspaces.Wrks))
		}
	default:
		logger.Warning.Printf("Unknown root client message: %s", name)
	}
}
Beispiel #3
0
// commandHandler responds to client message events that issue commands.
func commandHandler(X *xgbutil.XUtil, cm xevent.ClientMessageEvent) {
	typeName, err := xprop.AtomName(X, cm.Type)
	if err != nil {
		logger.Warning.Printf(
			"Could not get type of ClientMessage event: %s", cm)
		return
	}

	if typeName == "_WINGO_CMD" {
		cmd, err := cmdusage.CmdGet(X)
		if err != nil {
			logger.Warning.Printf("Could not get _WINGO_CMD value: %s", err)
			return
		}

		// Blank out the command
		cmdusage.CmdSet(X, "")

		// Parse the command
		cmdName, args := commandParse(cmd)
		cmdFun := commandFun("", cmdName, args...)
		if cmdFun != nil {
			cmdFun()
			cmdusage.StatusSet(X, true)
		} else {
			cmdusage.StatusSet(X, false)
		}
	}
}
// IsFocusProtocol checks whether a ClientMessage event satisfies the
// WM_TAKE_FOCUS protocol.
func IsFocusProtocol(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) bool {
	// Make sure the Format is 32. (Meaning that each data item is
	// 32 bits.)
	if ev.Format != 32 {
		return false
	}

	// Check to make sure the Type atom is WM_PROTOCOLS.
	typeName, err := xprop.AtomName(X, ev.Type)
	if err != nil || typeName != "WM_PROTOCOLS" { // not what we want
		return false
	}

	// Check to make sure the first data item is WM_TAKE_FOCUS.
	protocolType, err := xprop.AtomName(X,
		xproto.Atom(ev.Data.Data32[0]))
	if err != nil || protocolType != "WM_TAKE_FOCUS" {
		return false
	}

	return true
}
Beispiel #5
0
func (c *Client) cbClientMessage() xevent.ClientMessageFun {
	f := func(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) {
		name, err := xprop.AtomName(wm.X, ev.Type)
		if err != nil {
			logger.Warning.Printf("Could not get property atom name for "+
				"ClientMessage event on '%s': %s.", c, err)
			return
		}

		logger.Lots.Printf(
			"Handling ClientMessage '%s' on client '%s'.", name, c)
		c.handleClientMessage(name, ev.Data.Data32)
	}
	return xevent.ClientMessageFun(f)
}
Beispiel #6
0
func (c *Client) cbPropertyNotify() xevent.PropertyNotifyFun {
	f := func(X *xgbutil.XUtil, ev xevent.PropertyNotifyEvent) {
		name, err := xprop.AtomName(wm.X, ev.Atom)
		if err != nil {
			logger.Warning.Printf("Could not get property atom name for '%s' "+
				"because: %s.", ev, err)
			return
		}

		logger.Lots.Printf("Updating property %s with state %v on window %s",
			name, ev.State, c)
		c.handleProperty(name)
	}
	return xevent.PropertyNotifyFun(f)
}
Beispiel #7
0
func (c *client) cbPropertyNotify() xevent.PropertyNotifyFun {
	// helper function to log property vals
	showVals := func(o, n interface{}) {
		logger.Lots.Printf("\tOld value: '%s', new value: '%s'", o, n)
	}
	f := func(X *xgbutil.XUtil, ev xevent.PropertyNotifyEvent) {
		name, err := xprop.AtomName(c.X, ev.Atom)
		if err != nil {
			logger.Warning.Printf("Could not get property atom name for '%s' "+
				"because: %s.", ev, err)
		}

		logger.Lots.Printf("Updating property %s with state %v on window %s",
			name, ev.State, c)
		switch name {
		case "_NET_WM_VISIBLE_NAME":
			fallthrough
		case "_NET_WM_NAME":
			fallthrough
		case "WM_NAME":
			c.refreshName()
		case "_NET_WM_ICON":
		case "WM_HINTS":
			if hints, err := icccm.WmHintsGet(X, c.Id()); err == nil {
				c.hints = hints
			}
		case "WM_NORMAL_HINTS":
			if nhints, err := icccm.WmNormalHintsGet(X, c.Id()); err == nil {
				c.nhints = nhints
			}
		case "WM_TRANSIENT_FOR":
			if trans, err := icccm.WmTransientForGet(X, c.Id()); err == nil {
				if transCli := wingo.findManagedClient(trans); transCli != nil {
					c.transientFor = transCli
				}
			}
		case "_NET_WM_USER_TIME":
			if newTime, err := ewmh.WmUserTimeGet(X, c.Id()); err == nil {
				showVals(c.time, newTime)
				c.time = xproto.Timestamp(newTime)
			}
		case "_NET_WM_STRUT_PARTIAL":
		}

	}
	return xevent.PropertyNotifyFun(f)
}
Beispiel #8
0
func main() {
	X, err := xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	// Start generating other source events.
	otherChan := otherSource()

	// Start generating X events (by sending client messages to root window).
	go xSource(X)

	// Listen to those X events.
	xwindow.New(X, X.RootWin()).Listen(xproto.EventMaskSubstructureNotify)

	// Respond to those X events.
	xevent.ClientMessageFun(
		func(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) {
			atmName, err := xprop.AtomName(X, ev.Type)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("ClientMessage: %d. %s\n", ev.Data.Data32[0], atmName)
		}).Connect(X, X.RootWin())

	// Instead of using the usual xevent.Main, we use xevent.MainPing.
	// It runs the main event loop inside a goroutine and returns ping
	// channels, which are sent benign values right before an event is
	// dequeued and right after that event has finished running all callbacks
	// associated with it, respectively.
	pingBefore, pingAfter, pingQuit := xevent.MainPing(X)
	for {
		select {
		case <-pingBefore:
			// Wait for the event to finish processing.
			<-pingAfter
		case otherVal := <-otherChan:
			fmt.Printf("Processing other event: %d\n", otherVal)
		case <-pingQuit:
			fmt.Printf("xevent loop has quit")
			return
		}
	}
}
Beispiel #9
0
func (w *Window) handleEvents() {
	var noX int32 = 1<<31 - 1
	noX++
	var lastX, lastY int32 = noX, 0
	var button wde.Button

	downKeys := map[string]bool{}

	for {
		e, err := w.conn.WaitForEvent()

		// if err != nil {
		// 	fmt.Println("err:", err)
		// }
		if err == io.EOF {
			break
		}

		xgbutil.BeSafe(&err)

		switch e := e.(type) {

		case xgb.ButtonPressEvent:
			button = button | buttonForDetail(e.Detail)
			var bpe wde.MouseDownEvent
			bpe.Which = buttonForDetail(e.Detail)
			bpe.Where.X = int(e.EventX)
			bpe.Where.Y = int(e.EventY)
			lastX = int32(e.EventX)
			lastY = int32(e.EventY)
			w.events <- bpe

		case xgb.ButtonReleaseEvent:
			button = button & ^buttonForDetail(e.Detail)
			var bue wde.MouseUpEvent
			bue.Which = buttonForDetail(e.Detail)
			bue.Where.X = int(e.EventX)
			bue.Where.Y = int(e.EventY)
			lastX = int32(e.EventX)
			lastY = int32(e.EventY)
			w.events <- bue

		case xgb.LeaveNotifyEvent:
			var wee wde.MouseExitedEvent
			wee.Where.X = int(e.EventX)
			wee.Where.Y = int(e.EventY)
			if lastX != noX {
				wee.From.X = int(lastX)
				wee.From.Y = int(lastY)
			} else {
				wee.From.X = wee.Where.X
				wee.From.Y = wee.Where.Y
			}
			lastX = int32(e.EventX)
			lastY = int32(e.EventY)
			w.events <- wee
		case xgb.EnterNotifyEvent:
			var wee wde.MouseEnteredEvent
			wee.Where.X = int(e.EventX)
			wee.Where.Y = int(e.EventY)
			if lastX != noX {
				wee.From.X = int(lastX)
				wee.From.Y = int(lastY)
			} else {
				wee.From.X = wee.Where.X
				wee.From.Y = wee.Where.Y
			}
			lastX = int32(e.EventX)
			lastY = int32(e.EventY)
			w.events <- wee

		case xgb.MotionNotifyEvent:
			var mme wde.MouseMovedEvent
			mme.Where.X = int(e.EventX)
			mme.Where.Y = int(e.EventY)
			if lastX != noX {
				mme.From.X = int(lastX)
				mme.From.Y = int(lastY)
			} else {
				mme.From.X = mme.Where.X
				mme.From.Y = mme.Where.Y
			}
			lastX = int32(e.EventX)
			lastY = int32(e.EventY)
			if button == 0 {
				w.events <- mme
			} else {
				var mde wde.MouseDraggedEvent
				mde.MouseMovedEvent = mme
				mde.Which = button
				w.events <- mde
			}

		case xgb.KeyPressEvent:
			var ke wde.KeyEvent
			code := keybind.LookupString(w.xu, e.State, e.Detail)
			ke.Key = keyForCode(code)
			w.events <- wde.KeyDownEvent(ke)
			downKeys[ke.Key] = true
			kpe := wde.KeyTypedEvent{
				KeyEvent: ke,
				Glyph:    letterForCode(code),
				Chord:    wde.ConstructChord(downKeys),
			}
			w.events <- kpe

		case xgb.KeyReleaseEvent:
			var ke wde.KeyUpEvent
			ke.Key = keyForCode(keybind.LookupString(w.xu, e.State, e.Detail))
			delete(downKeys, ke.Key)
			w.events <- ke

		case xgb.ConfigureNotifyEvent:
			var re wde.ResizeEvent
			re.Width = int(e.Width)
			re.Height = int(e.Height)
			if re.Width != w.width || re.Height != w.height {
				w.width, w.height = re.Width, re.Height
				w.buffer = image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{re.Width, re.Height}})
				w.events <- re
			}

		case xgb.ClientMessageEvent:
			if e.Format != 32 {
				break
			}
			name, err := xprop.AtomName(w.xu, e.Type)
			if err != nil {
				// TODO: log
				break
			}
			if name != "WM_PROTOCOLS" {
				break
			}
			name2, err := xprop.AtomName(w.xu, xgb.Id(e.Data.Data32[0]))
			if err != nil {
				// TODO: log
				break
			}
			if name2 == "WM_DELETE_WINDOW" {
				w.events <- wde.CloseEvent{}
			}

		case xgb.DestroyNotifyEvent:
		case xgb.ReparentNotifyEvent:
		case xgb.MapNotifyEvent:
		case xgb.UnmapNotifyEvent:
		case xgb.PropertyNotifyEvent:

		default:
			fmt.Printf("unhandled event: type %T\n%+v\n", e, e)
		}

	}

	close(w.events)
}
Beispiel #10
0
func main() {
	X, err := xgbutil.Dial("")
	if err != nil {
		logger.Error.Println(err)
		logger.Error.Println("Error connecting to X, quitting...")
		return
	}
	defer X.Conn().Close()

	// Get command from arguments
	flag.Parse()
	if flag.NArg() < 1 {
		fmt.Fprintln(os.Stderr, "Usage: wingo-cmd CommandName [CommandArgs]")
		return
	}

	commandPieces := flag.Args()
	cmdName := commandPieces[0]
	cmdFull := strings.Join(commandPieces, " ")

	// make sure we start with failure
	cmdusage.StatusSet(X, false)
	success := false

	// Set the command before sending request to run command.
	err = cmdusage.CmdSet(X, cmdFull)
	if err != nil {
		logger.Error.Printf("Could not set command: %s", err)
		return
	}

	// Issue the command!
	ewmh.ClientEvent(X, X.RootWin(), "_WINGO_CMD")

	// Now let's set up a handler to detect when the status changes
	xevent.PropertyNotifyFun(
		func(X *xgbutil.XUtil, ev xevent.PropertyNotifyEvent) {
			name, err := xprop.AtomName(X, ev.Atom)
			if err != nil {
				logger.Warning.Println(
					"Could not get property atom name for", ev.Atom)
				return
			}

			if name == "_WINGO_CMD_STATUS" {
				success = cmdusage.StatusGet(X)
				if success {
					os.Exit(0)
				} else {
					logger.Warning.Printf("Error running '%s'", cmdFull)
					cmdusage.ShowUsage(cmdName)
					os.Exit(1)
				}
			}
		}).Connect(X, X.RootWin())

	// Listen to Root property change events
	xwindow.Listen(X, X.RootWin(), xproto.EventMaskPropertyChange)

	go xevent.Main(X)

	time.Sleep(time.Second * 5)

	logger.Error.Println(
		"Timed out while trying to issue command to Wingo. " +
			"Are you sure Wingo is running?")
	os.Exit(1)
}