// Replace existing window manager func usurpWM(X *xgb.Conn, screen *xproto.ScreenInfo) { 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 { log.Fatal(err) } }
func (m *TrayManager) tryOwner() bool { // Make a check, the tray application MUST be 1. reply, err := m.getSelectionOwner() if err != nil { logger.Error(err) return false } if reply.Owner != 0 { logger.Warning("Another System tray application is running") return false } timeStamp, _ := ewmh.WmUserTimeGet(TrayXU, m.owner) err = xproto.SetSelectionOwnerChecked( TrayXU.Conn(), m.owner, _NET_SYSTEM_TRAY_S0, xproto.Timestamp(timeStamp), ).Check() if err != nil { logger.Warning("Set Selection Owner failed: ", err) return false } //owner the _NET_SYSTEM_TRAY_Sn logger.Info("Required _NET_SYSTEM_TRAY_S0 successful") m.RequireManageTrayIcons() xprop.ChangeProp32( TrayXU, m.owner, "_NET_SYSTEM_TRAY_VISUAL", "VISUALID", uint(TRAYMANAGER.visual), ) xprop.ChangeProp32( TrayXU, m.owner, "_NET_SYSTEM_TRAY_ORIENTAION", "CARDINAL", 0, ) reply, err = m.getSelectionOwner() if err != nil { logger.Warning(err) return false } return reply.Owner != 0 }
func initXSettings() { a0 := internAtom("_XSETTINGS_S0") if err := xp.SetSelectionOwnerChecked(xConn, desktopXWin, a0, xp.TimeCurrentTime).Check(); err != nil { log.Printf("could not set xsettings: %v", err) return } a1 := internAtom("_XSETTINGS_SETTINGS") encoded := makeEncodedXSettings() if err := xp.ChangePropertyChecked(xConn, xp.PropModeReplace, desktopXWin, a1, a1, 8, uint32(len(encoded)), encoded).Check(); err != nil { log.Printf("could not set xsettings: %v", err) return } }
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 }
// 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 }
func New(X *xgbutil.XUtil) (*SystemTray, error) { tray := &SystemTray{ X: X, } var err error if sysTrayAtom == 0 { sysTrayAtom, err = xprop.Atom(X, "_NET_SYSTEM_TRAY_S0", false) if err != nil { return nil, err } } if sysTrayMsgAtom == 0 { sysTrayMsgAtom, err = xprop.Atom(X, "_NET_SYSTEM_TRAY_OPCODE", false) if err != nil { return nil, err } } if managerEventAtom == 0 { managerEventAtom, err = xprop.Atom(X, "MANAGER", false) if err != nil { return nil, err } } tray.wid, err = xwindow.Create(X, X.RootWin()) if err != nil { return nil, err } ts, err := currentTime(X) if err != nil { return nil, err } X.TimeSet(ts) // tray.wid.Listen(xproto.EventMaskNoEvent | xproto.EventMaskPropertyChange) err = xproto.SetSelectionOwnerChecked(tray.X.Conn(), tray.wid.Id, sysTrayAtom, tray.X.TimeGet()).Check() if err != nil { return nil, err } reply, err := xproto.GetSelectionOwner(X.Conn(), sysTrayAtom).Reply() if err != nil { return nil, err } if reply.Owner != tray.wid.Id { return nil, fmt.Errorf("Could not get ownership of the thingy-thing.") } evt, err := xevent.NewClientMessage(32, X.RootWin(), managerEventAtom, int(X.TimeGet()), int(sysTrayAtom), int(tray.wid.Id)) if err != nil { return nil, err } if err = xevent.SendRootEvent(X, evt, xproto.EventMaskStructureNotify); err != nil { return nil, err } xevent.ClientMessageFun(func(_ *xgbutil.XUtil, ev xevent.ClientMessageEvent) { tray.event(ev) }).Connect(tray.X, tray.wid.Id) return tray, nil }
func (tray *SystemTray) Teardown() error { // TODO: Tray Icon Tracking? currentTime(tray.X) return xproto.SetSelectionOwnerChecked(tray.X.Conn(), xproto.WindowNone, sysTrayAtom, tray.X.TimeGet()).Check() }
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 }