// NotifyEventNew constructs a NotifyEvent value that implements xgb.Event from a byte slice. func NotifyEventNew(buf []byte) xgb.Event { v := NotifyEvent{} b := 1 // don't read event number v.State = buf[b] b += 1 v.Sequence = xgb.Get16(buf[b:]) b += 2 v.Time = xproto.Timestamp(xgb.Get32(buf[b:])) b += 4 v.Root = xproto.Window(xgb.Get32(buf[b:])) b += 4 v.Window = xproto.Window(xgb.Get32(buf[b:])) b += 4 v.Kind = buf[b] b += 1 if buf[b] == 1 { v.Forced = true } else { v.Forced = false } b += 1 b += 14 // padding return v }
func (self *SessionModule) toX11WindowId(window_id string) (xproto.Window, error) { if id_number, err := strconv.Atoi(window_id); err == nil { return xproto.Window(uint32(id_number)), nil } else { return xproto.Window(0), err } }
// WM_HINTS get func WmHintsGet(xu *xgbutil.XUtil, win xproto.Window) (hints *Hints, err error) { lenExpect := 9 raw, err := xprop.PropValNums(xprop.GetProperty(xu, win, "WM_HINTS")) if err != nil { return nil, err } if len(raw) != lenExpect { return nil, fmt.Errorf("WmHints: There are %d fields in "+ "WM_HINTS, but xgbutil expects %d.", len(raw), lenExpect) } hints = &Hints{} hints.Flags = raw[0] hints.Input = raw[1] hints.InitialState = raw[2] hints.IconPixmap = xproto.Pixmap(raw[3]) hints.IconWindow = xproto.Window(raw[4]) hints.IconX = raw[5] hints.IconY = raw[6] hints.IconMask = xproto.Pixmap(raw[7]) hints.WindowGroup = xproto.Window(raw[8]) return hints, nil }
// queryInfoReply reads a byte slice into a QueryInfoReply value. func queryInfoReply(buf []byte) *QueryInfoReply { v := new(QueryInfoReply) b := 1 // skip reply determinant v.State = buf[b] b += 1 v.Sequence = xgb.Get16(buf[b:]) b += 2 v.Length = xgb.Get32(buf[b:]) // 4-byte units b += 4 v.SaverWindow = xproto.Window(xgb.Get32(buf[b:])) b += 4 v.MsUntilServer = xgb.Get32(buf[b:]) b += 4 v.MsSinceUserInput = xgb.Get32(buf[b:]) b += 4 v.EventMask = xgb.Get32(buf[b:]) b += 4 v.Kind = buf[b] b += 1 b += 7 // padding return v }
// commandArgsClient scans an argument list for a window id. // A window id has the form 'win:WINDOW-ID_NUMBER'. // Both 'win:0x0001' and 'win:1' are valid thanks to Go's ParseInt. // Finally, if the window id corresponds to managed client, return that // client. Otherwise, return nil and emit an error if we have an invalid ID. // We also return a bool as a second argument which should be interpreted // as whether or not to continue the current operation. // i.e., not finding anything that looks like a window id is safe to ignore, // but if we find something like an ID and error out, we should stop the // command entirely. func commandArgsClient(args []string) (*client, bool) { for _, arg := range args { if len(arg) < 5 || arg[0:4] != "win:" { continue } maybeId64, err := strconv.ParseInt(arg[4:], 0, 0) if err != nil { logger.Warning.Printf("'%s' is not a valid window id.", arg[4:]) return nil, false } goodId := xproto.Window(maybeId64) for _, c := range wingo.clients { if c.Id() == goodId { return c, true } } logger.Warning.Printf( "'%s' is a valid window ID, but does not match any managed "+ "window ID by Wingo.", arg[4:]) return nil, false } return nil, true }
func (m *TrayManager) addTrayIcon(xid xproto.Window) { m.checkValid() for _, id := range m.TrayIcons { if xproto.Window(id) == xid { return } } if d, err := damage.NewDamageId(TrayXU.Conn()); err != nil { return } else { m.dmageInfo[xid] = d if err := damage.CreateChecked(TrayXU.Conn(), d, xproto.Drawable(xid), damage.ReportLevelRawRectangles).Check(); err != nil { logger.Debug("DamageCreate Failed:", err) return } } composite.RedirectWindow(TrayXU.Conn(), xid, composite.RedirectAutomatic) m.TrayIcons = append(m.TrayIcons, uint32(xid)) icon := xwindow.New(TrayXU, xid) icon.Listen(xproto.EventMaskVisibilityChange | damage.Notify | xproto.EventMaskStructureNotify) icon.Change(xproto.CwBackPixel, 0) name, err := ewmh.WmNameGet(TrayXU, xid) if err != nil { logger.Debug("WmNameGet failed:", err, xid) } m.nameInfo[xid] = name m.notifyInfo[xid] = true dbus.Emit(m, "Added", uint32(xid)) logger.Infof("Added try icon: \"%s\"(%d)", name, uint32(xid)) }
func (tray *SystemTray) event(ev xevent.ClientMessageEvent) { if ev.Format != 32 { return } if ev.Type != sysTrayMsgAtom { return } opCode := ev.Data.Data32[1] if opCode == 0 { // SYSTEM_TRAY_REQUEST_DOCK icon := tray.newIcon(xproto.Window(ev.Data.Data32[2])) if icon != nil { tray.Handler.NewIcon(icon) } } else if opCode == 1 { // Not Implemented. } else if opCode == 2 { // Not Implemented. } else { // Do Nothing for now. fmt.Printf("[SystemTray] Got unknown opcode: %d", opCode) } }
// getScreenSizeReply reads a byte slice into a GetScreenSizeReply value. func getScreenSizeReply(buf []byte) *GetScreenSizeReply { v := new(GetScreenSizeReply) b := 1 // skip reply determinant b += 1 // padding v.Sequence = xgb.Get16(buf[b:]) b += 2 v.Length = xgb.Get32(buf[b:]) // 4-byte units b += 4 v.Width = xgb.Get32(buf[b:]) b += 4 v.Height = xgb.Get32(buf[b:]) b += 4 v.Window = xproto.Window(xgb.Get32(buf[b:])) b += 4 v.Screen = xgb.Get32(buf[b:]) b += 4 return v }
// maybe move to apps-builder func (m *ClientManager) CloseWindow(xid uint32) bool { err := ewmh.CloseWindow(XU, xproto.Window(xid)) if err != nil { logger.Warning("Close window failed:", err) return false } return true }
func main() { // winw, winh := 128, 128 time.Sleep(time.Nanosecond) X, err := xgbutil.NewConn() if err != nil { log.Fatal(err) } libre := xproto.Window(0x4a0001d) xclock := xproto.Window(0x480000a) showIcon(X, libre, "libre") showIcon(X, xclock, "xclock") xevent.Main(X) }
// maybe move to apps-builder func (m *ClientManager) ActiveWindow(xid uint32) bool { err := activateWindow(xproto.Window(xid)) if err != nil { logger.Warning("Actice window failed:", err) return false } return true }
func (a *atomic) PropValWindow(reply *xproto.GetPropertyReply, err error) (xproto.Window, error) { if err != nil { return 0, err } if reply.Format != 32 { return 0, fmt.Errorf("PropValId: Expected format 32 but got %d", reply.Format) } return xproto.Window(xgb.Get32(reply.Value)), nil }
func getWindowPid(X *xgbutil.XUtil, xid uint32) (uint32, error) { pid, err := ewmh.WmPidGet(X, xproto.Window(xid)) if err != nil { logger.Warning("Get window pid failed:", err) return 0, err } return uint32(pid), nil }
func getWindowName(X *xgbutil.XUtil, xid uint32) (string, error) { name, err := ewmh.WmNameGet(X, xproto.Window(xid)) if err != nil { logger.Warning("Get window name failed:", err) return "", err } return name, nil }
func getWindowState(X *xgbutil.XUtil, xid uint32) ([]string, error) { list, err := ewmh.WmStateGet(X, xproto.Window(xid)) if err != nil { logger.Warning("Get window state failed:", err) return []string{}, err } return list, nil }
func (m *TrayManager) checkValid() { for _, id := range m.TrayIcons { xid := xproto.Window(id) if m.isValidWindow(xid) { continue } m.removeTrayIcon(xid) } }
func main() { X, _ := xgbutil.NewConn() active, err := ewmh.ActiveWindowGet(X) wmName, err := icccm.WmNameGet(X, active) showTest("WM_NAME get", wmName, err) err = icccm.WmNameSet(X, active, "hooblah") wmName, _ = icccm.WmNameGet(X, active) showTest("WM_NAME set", wmName, err) wmNormHints, err := icccm.WmNormalHintsGet(X, active) showTest("WM_NORMAL_HINTS get", wmNormHints, err) wmNormHints.Width += 5 err = icccm.WmNormalHintsSet(X, active, wmNormHints) showTest("WM_NORMAL_HINTS set", wmNormHints, err) wmHints, err := icccm.WmHintsGet(X, active) showTest("WM_HINTS get", wmHints, err) wmHints.InitialState = icccm.StateNormal err = icccm.WmHintsSet(X, active, wmHints) showTest("WM_NORMAL_HINTS set", wmHints, err) wmClass, err := icccm.WmClassGet(X, active) showTest("WM_CLASS get", wmClass, err) wmClass.Instance = "hoopdy hoop" err = icccm.WmClassSet(X, active, wmClass) showTest("WM_CLASS set", wmClass, err) wmTrans, err := icccm.WmTransientForGet(X, active) showTest("WM_TRANSIENT_FOR get", wmTrans, err) wmProts, err := icccm.WmProtocolsGet(X, active) showTest("WM_PROTOCOLS get", wmProts, err) wmClient, err := icccm.WmClientMachineGet(X, active) showTest("WM_CLIENT_MACHINE get", wmClient, err) err = icccm.WmClientMachineSet(X, active, "Leopard") wmClient, _ = icccm.WmClientMachineGet(X, active) showTest("WM_CLIENT_MACHINE set", wmClient, err) wmState, err := icccm.WmStateGet(X, active) showTest("WM_STATE get", wmState, err) wmState.Icon = xproto.Window(8365538) wmState.State = icccm.StateNormal err = icccm.WmStateSet(X, active, wmState) wmState, _ = icccm.WmStateGet(X, active) showTest("WM_STATE set", wmState, err) }
func (m *TrayManager) startListener() { // to avoid creating too much listener when SelectionNotifyEvent occurs. if isListened { return } isListened = true for { if e, err := TrayXU.Conn().WaitForEvent(); err == nil { switch ev := e.(type) { case xproto.ClientMessageEvent: // logger.Info("ClientMessageEvent") if ev.Type == _NET_SYSTEM_TRAY_OPCODE { // timeStamp = ev.Data.Data32[0] opCode := ev.Data.Data32[1] // logger.Info("TRAY_OPCODE") switch opCode { case OpCodeSystemTrayRequestDock: xid := xproto.Window(ev.Data.Data32[2]) m.addTrayIcon(xid) case OpCodeSystemTrayBeginMessage: case OpCodeSystemTrayCancelMessage: } } case damage.NotifyEvent: m.handleTrayDamage(xproto.Window(ev.Drawable)) case xproto.DestroyNotifyEvent: m.removeTrayIcon(ev.Window) case xproto.SelectionClearEvent: m.Unmanage() case xfixes.SelectionNotifyEvent: m.Manage() } } } }
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)) }
// WM_STATE get func WmStateGet(xu *xgbutil.XUtil, win xproto.Window) (*WmState, error) { raw, err := xprop.PropValNums(xprop.GetProperty(xu, win, "WM_STATE")) if err != nil { return nil, err } if len(raw) != 2 { return nil, fmt.Errorf("WmState: Expected two integers in WM_STATE property "+ "but xgbutil found %d in '%v'.", len(raw), raw) } return &WmState{ State: raw[0], Icon: xproto.Window(raw[1]), }, nil }
func (a *atomic) PropValWindows(reply *xproto.GetPropertyReply, err error) ([]xproto.Window, error) { if err != nil { return nil, err } if reply.Format != 32 { return nil, fmt.Errorf("PropValIds: Expected format 32 but got %d", reply.Format) } ids := make([]xproto.Window, reply.ValueLen) vals := reply.Value for i := 0; len(vals) >= 4; i++ { ids[i] = xproto.Window(xgb.Get32(vals)) vals = vals[4:] } return ids, nil }
// getStateReply reads a byte slice into a GetStateReply value. func getStateReply(buf []byte) *GetStateReply { v := new(GetStateReply) b := 1 // skip reply determinant v.State = buf[b] b += 1 v.Sequence = xgb.Get16(buf[b:]) b += 2 v.Length = xgb.Get32(buf[b:]) // 4-byte units b += 4 v.Window = xproto.Window(xgb.Get32(buf[b:])) b += 4 return v }
func withClient(cArg gribble.Any, f func(c *xclient.Client)) gribble.Any { switch c := cArg.(type) { case int: if c == 0 { return ":void:" } for _, client_ := range wm.Clients { client := client_.(*xclient.Client) if int(client.Id()) == c { f(client) return int(client.Id()) } } return ":void:" case string: switch c { case ":void:": return ":void:" case ":mouse:": wid := xproto.Window(wm.MouseClientClicked) if client := wm.FindManagedClient(wid); client != nil { c := client.(*xclient.Client) f(c) return int(c.Id()) } else { f(nil) return ":void:" } default: for _, client_ := range wm.Clients { client := client_.(*xclient.Client) name := strings.ToLower(client.Name()) if strings.Contains(name, strings.ToLower(c)) { f(client) return int(client.Id()) } } return ":void:" } default: panic(fmt.Sprintf("BUG: Unknown Gribble return type: %T", c)) } panic("unreachable") }
// getOverlayWindowReply reads a byte slice into a GetOverlayWindowReply value. func getOverlayWindowReply(buf []byte) *GetOverlayWindowReply { v := new(GetOverlayWindowReply) b := 1 // skip reply determinant b += 1 // padding v.Sequence = xgb.Get16(buf[b:]) b += 2 v.Length = xgb.Get32(buf[b:]) // 4-byte units b += 4 v.OverlayWin = xproto.Window(xgb.Get32(buf[b:])) b += 4 b += 20 // padding return v }
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 }
// NotifyEventNew constructs a NotifyEvent value that implements xgb.Event from a byte slice. func NotifyEventNew(buf []byte) xgb.Event { v := NotifyEvent{} b := 1 // don't read event number v.ShapeKind = Kind(buf[b]) b += 1 v.Sequence = xgb.Get16(buf[b:]) b += 2 v.AffectedWindow = xproto.Window(xgb.Get32(buf[b:])) b += 4 v.ExtentsX = int16(xgb.Get16(buf[b:])) b += 2 v.ExtentsY = int16(xgb.Get16(buf[b:])) b += 2 v.ExtentsWidth = xgb.Get16(buf[b:]) b += 2 v.ExtentsHeight = xgb.Get16(buf[b:]) b += 2 v.ServerTime = xproto.Timestamp(xgb.Get32(buf[b:])) b += 4 if buf[b] == 1 { v.Shaped = true } else { v.Shaped = false } b += 1 b += 11 // padding return v }
func (m *TrayManager) Unmanage() bool { reply, err := m.getSelectionOwner() if err != nil { logger.Warning("get selection owner failed:", err) return false } if reply.Owner != m.owner { logger.Warning("not selection owner") return false } m.destroyOwnerWindow() trayicons := m.TrayIcons for _, icon := range trayicons { m.removeTrayIcon(xproto.Window(icon)) } timeStamp, _ := ewmh.WmUserTimeGet(TrayXU, m.owner) return xproto.SetSelectionOwnerChecked( TrayXU.Conn(), 0, _NET_SYSTEM_TRAY_S0, xproto.Timestamp(timeStamp), ).Check() == nil }
// processEventQueue processes every item in the event/error queue. func processEventQueue(xu *xgbutil.XUtil, pingBefore, pingAfter chan struct{}) { for !Empty(xu) { if Quitting(xu) { return } // We send the ping *before* the next event is dequeued. // This is so the queue doesn't present a misrepresentation of which // events haven't been processed yet. if pingBefore != nil && pingAfter != nil { pingBefore <- struct{}{} } ev, err := Dequeue(xu) // If we gobbled up an error, send it to the error event handler // and move on the next event/error. if err != nil { ErrorHandlerGet(xu)(err) if pingBefore != nil && pingAfter != nil { pingAfter <- struct{}{} } continue } // We know there isn't an error. If there isn't an event either, // then there's a bug somewhere. if ev == nil { xgbutil.Logger.Fatal("BUG: Expected an event but got nil.") } hooks := getHooks(xu) for _, hook := range hooks { if !hook.Run(xu, ev) { goto END } } switch event := ev.(type) { case xproto.KeyPressEvent: e := KeyPressEvent{&event} // If we're redirecting key events, this is the place to do it! if wid := RedirectKeyGet(xu); wid > 0 { e.Event = wid } xu.TimeSet(e.Time) runCallbacks(xu, e, KeyPress, e.Event) case xproto.KeyReleaseEvent: e := KeyReleaseEvent{&event} // If we're redirecting key events, this is the place to do it! if wid := RedirectKeyGet(xu); wid > 0 { e.Event = wid } xu.TimeSet(e.Time) runCallbacks(xu, e, KeyRelease, e.Event) case xproto.ButtonPressEvent: e := ButtonPressEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, ButtonPress, e.Event) case xproto.ButtonReleaseEvent: e := ButtonReleaseEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, ButtonRelease, e.Event) case xproto.MotionNotifyEvent: e := MotionNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, MotionNotify, e.Event) case xproto.EnterNotifyEvent: e := EnterNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, EnterNotify, e.Event) case xproto.LeaveNotifyEvent: e := LeaveNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, LeaveNotify, e.Event) case xproto.FocusInEvent: e := FocusInEvent{&event} runCallbacks(xu, e, FocusIn, e.Event) case xproto.FocusOutEvent: e := FocusOutEvent{&event} runCallbacks(xu, e, FocusOut, e.Event) case xproto.KeymapNotifyEvent: e := KeymapNotifyEvent{&event} runCallbacks(xu, e, KeymapNotify, NoWindow) case xproto.ExposeEvent: e := ExposeEvent{&event} runCallbacks(xu, e, Expose, e.Window) case xproto.GraphicsExposureEvent: e := GraphicsExposureEvent{&event} runCallbacks(xu, e, GraphicsExposure, xproto.Window(e.Drawable)) case xproto.NoExposureEvent: e := NoExposureEvent{&event} runCallbacks(xu, e, NoExposure, xproto.Window(e.Drawable)) case xproto.VisibilityNotifyEvent: e := VisibilityNotifyEvent{&event} runCallbacks(xu, e, VisibilityNotify, e.Window) case xproto.CreateNotifyEvent: e := CreateNotifyEvent{&event} runCallbacks(xu, e, CreateNotify, e.Parent) case xproto.DestroyNotifyEvent: e := DestroyNotifyEvent{&event} runCallbacks(xu, e, DestroyNotify, e.Window) case xproto.UnmapNotifyEvent: e := UnmapNotifyEvent{&event} runCallbacks(xu, e, UnmapNotify, e.Window) case xproto.MapNotifyEvent: e := MapNotifyEvent{&event} runCallbacks(xu, e, MapNotify, e.Event) case xproto.MapRequestEvent: e := MapRequestEvent{&event} runCallbacks(xu, e, MapRequest, e.Window) runCallbacks(xu, e, MapRequest, e.Parent) case xproto.ReparentNotifyEvent: e := ReparentNotifyEvent{&event} runCallbacks(xu, e, ReparentNotify, e.Window) case xproto.ConfigureNotifyEvent: e := ConfigureNotifyEvent{&event} runCallbacks(xu, e, ConfigureNotify, e.Window) case xproto.ConfigureRequestEvent: e := ConfigureRequestEvent{&event} runCallbacks(xu, e, ConfigureRequest, e.Window) runCallbacks(xu, e, ConfigureRequest, e.Parent) case xproto.GravityNotifyEvent: e := GravityNotifyEvent{&event} runCallbacks(xu, e, GravityNotify, e.Window) case xproto.ResizeRequestEvent: e := ResizeRequestEvent{&event} runCallbacks(xu, e, ResizeRequest, e.Window) case xproto.CirculateNotifyEvent: e := CirculateNotifyEvent{&event} runCallbacks(xu, e, CirculateNotify, e.Window) case xproto.CirculateRequestEvent: e := CirculateRequestEvent{&event} runCallbacks(xu, e, CirculateRequest, e.Window) case xproto.PropertyNotifyEvent: e := PropertyNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, PropertyNotify, e.Window) case xproto.SelectionClearEvent: e := SelectionClearEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, SelectionClear, e.Owner) case xproto.SelectionRequestEvent: e := SelectionRequestEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, SelectionRequest, e.Requestor) case xproto.SelectionNotifyEvent: e := SelectionNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, SelectionNotify, e.Requestor) case xproto.ColormapNotifyEvent: e := ColormapNotifyEvent{&event} runCallbacks(xu, e, ColormapNotify, e.Window) case xproto.ClientMessageEvent: e := ClientMessageEvent{&event} runCallbacks(xu, e, ClientMessage, e.Window) case xproto.MappingNotifyEvent: e := MappingNotifyEvent{&event} runCallbacks(xu, e, MappingNotify, NoWindow) case shape.NotifyEvent: e := ShapeNotifyEvent{&event} runCallbacks(xu, e, ShapeNotify, e.AffectedWindow) default: if event != nil { xgbutil.Logger.Printf("ERROR: UNSUPPORTED EVENT TYPE: %T", event) } } END: if pingBefore != nil && pingAfter != nil { pingAfter <- struct{}{} } } }
// own requests ownership over the role of window manager in the current // X environment. It can fail if it does not successfully get ownership. // // When 'replace' is true, Wingo will attempt to replace an window manager // that is currently running. Otherwise, Wingo will quit if a window manager // is running. func own(X *xgbutil.XUtil, replace bool) error { otherWmRunning := false otherWmName := "" otherWmOwner := xproto.Window(xproto.WindowNone) xTime, err := currentTime(X) if err != nil { return err } selAtom, err := managerAtom(X) if err != nil { return err } // Check to see if we need to replace. If so, determine whether to // continue based on `replace`. reply, err := xproto.GetSelectionOwner(X.Conn(), selAtom).Reply() if err != nil { return err } if reply.Owner != xproto.WindowNone { otherWmRunning = true otherWmOwner = reply.Owner // Look for the window manager's name for a nicer error message. otherWmName, err = ewmh.GetEwmhWM(X) if err != nil || len(otherWmName) == 0 { otherWmName = "Unknown" } // We need to listen for DestroyNotify events on the selection // owner in case we need to replace the WM. owner := xwindow.New(X, reply.Owner) if err = owner.Listen(xproto.EventMaskStructureNotify); err != nil { return err } } if otherWmRunning { if !replace { return fmt.Errorf( "Another window manager (%s) is already running. Please use "+ "the '--replace' option to replace the current window "+ "manager with Wingo.", otherWmName) } else { logger.Message.Printf( "Waiting for %s to shutdown and transfer ownership to us.", otherWmName) } } logger.Message.Printf("Setting selection owner...") err = xproto.SetSelectionOwnerChecked( X.Conn(), X.Dummy(), selAtom, xTime).Check() if err != nil { return err } // Now we've got to make sure that we *actually* got ownership. logger.Message.Printf("Getting selection owner...") reply, err = xproto.GetSelectionOwner(X.Conn(), selAtom).Reply() if err != nil { return err } if reply.Owner != X.Dummy() { return fmt.Errorf( "Could not acquire ownership with SetSelectionOwner. "+ "GetSelectionOwner claims that '%d' is the owner, but '%d' "+ "needs to be.", reply.Owner, X.Dummy()) } // While X now acknowledges us as the selection owner, it's possible // that the window manager is misbehaving. ICCCM 2.8 calls for the previous // manager to destroy the selection owner when it's OK for us to take // over. Otherwise, listening to SubstructureRedirect on the root window // might fail if we move too quickly. timeout := time.After(3 * time.Second) if otherWmRunning { OTHER_WM_SHUTDOWN: for { select { case <-timeout: return fmt.Errorf( "Wingo failed to replace the currently running window "+ "manager (%s). Namely, Wingo was not able to detect "+ "that the current window manager had shut down.", otherWmName) default: logger.Message.Printf("Polling for event...") ev, err := X.Conn().PollForEvent() if err != nil { continue } logger.Message.Printf("Got event, error: %s -- %s", ev, err) if destNotify, ok := ev.(xproto.DestroyNotifyEvent); ok { if destNotify.Window == otherWmOwner { break OTHER_WM_SHUTDOWN } } time.Sleep(100 * time.Millisecond) } } } logger.Message.Println("Wingo has window manager ownership!") announce(X) // Listen for SelectionClear events. When we get one of these, then we // know a window manager is trying to replace us. xevent.SelectionClearFun(disown).Connect(X, X.Dummy()) return nil }
// 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 } } }