func (c *Client) handleProperty(name string) { switch name { case "_NET_WM_VISIBLE_NAME": fallthrough case "_NET_WM_NAME": fallthrough case "WM_NAME": c.refreshName() case "_NET_WM_ICON": c.refreshIcon() case "WM_HINTS": if hints, err := icccm.WmHintsGet(wm.X, c.Id()); err == nil { c.hints = hints c.refreshIcon() } case "WM_NORMAL_HINTS": if nhints, err := icccm.WmNormalHintsGet(wm.X, c.Id()); err == nil { c.nhints = nhints } case "WM_TRANSIENT_FOR": if trans, err := icccm.WmTransientForGet(wm.X, c.Id()); err == nil { if transCli := wm.FindManagedClient(trans); transCli != nil { c.transientFor = transCli.(*Client) } } case "_NET_WM_USER_TIME": if newTime, err := ewmh.WmUserTimeGet(wm.X, c.Id()); err == nil { c.time = xproto.Timestamp(newTime) } case "_NET_WM_STRUT_PARTIAL": c.maybeApplyStruts() case "_MOTIF_WM_HINTS": // This is a bit messed up. If a client is floating, we don't // really care what the decorations are, so we oblige blindly. // However, if we're tiling, then we don't want to mess with // the frames---but we also want to make sure that any states // the client might revert to have the proper frames. decor := c.shouldDecor() if _, ok := c.Layout().(layout.Floater); ok { if decor { c.FrameFull() } else { c.FrameNada() } } else { for k := range c.states { s := c.states[k] if decor { s.frame = c.frames.full } else { s.frame = c.frames.nada } c.states[k] = s } } } }
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") }
func main() { if flagWriteConfig { writeConfigFiles() os.Exit(0) } X, err := xgbutil.NewConn() if err != nil { logger.Error.Println(err) logger.Error.Fatalln("Error connecting to X, quitting...") } defer X.Conn().Close() // Do this first! Attempt to retrieve window manager ownership. // This includes waiting for any existing window manager to die. // 'own' also sets up handlers for quitting when a window manager tries // to replace *us*. if err := own(X, flagReplace); err != nil { logger.Error.Fatalf( "Could not establish window manager ownership: %s", err) } if len(flagConfigDir) > 0 { misc.ConfigPaths.Override = flagConfigDir } if len(flagDataDir) > 0 { misc.DataPaths.Override = flagDataDir } misc.ReadData() keybind.Initialize(X) mousebind.Initialize(X) focus.Initialize(X) stack.Initialize(X) cursors.Initialize(X) wm.Initialize(X, commands.Env, newHacks()) hook.Initialize(commands.Env, misc.ConfigFile("hooks.wini")) // Listen to Root. It is all-important. err = xwindow.New(X, X.RootWin()).Listen( xproto.EventMaskPropertyChange | xproto.EventMaskFocusChange | xproto.EventMaskButtonPress | xproto.EventMaskButtonRelease | xproto.EventMaskStructureNotify | xproto.EventMaskSubstructureNotify | xproto.EventMaskSubstructureRedirect) if err != nil { logger.Error.Fatalf("Could not listen to Root window events: %s", err) } // Update state when the root window changes size wm.RootGeomChangeFun().Connect(X, wm.Root.Id) // Oblige map request events xevent.MapRequestFun( func(X *xgbutil.XUtil, ev xevent.MapRequestEvent) { xclient.New(ev.Window) }).Connect(X, wm.Root.Id) // Oblige configure requests from windows we don't manage. xevent.ConfigureRequestFun( func(X *xgbutil.XUtil, ev xevent.ConfigureRequestEvent) { // Make sure we aren't managing this client. if wm.FindManagedClient(ev.Window) != nil { return } xwindow.New(X, ev.Window).Configure(int(ev.ValueMask), int(ev.X), int(ev.Y), int(ev.Width), int(ev.Height), ev.Sibling, ev.StackMode) }).Connect(X, wm.Root.Id) xevent.FocusInFun( func(X *xgbutil.XUtil, ev xevent.FocusInEvent) { if ignoreRootFocus(ev.Mode, ev.Detail) { return } if len(wm.Workspace().Clients) == 0 { return } wm.FocusFallback() }).Connect(X, wm.Root.Id) // Listen to Root client message events. This is how we handle all // of the EWMH bullshit. xevent.ClientMessageFun(handleClientMessages).Connect(X, wm.Root.Id) // Tell everyone what we support. setSupported() // Start up the IPC command listener. go ipc() // Just before starting the main event loop, check to see if there are // any clients that already exist that we should manage. manageExistingClients() // Now make sure that clients are in the appropriate visible state. for _, wrk := range wm.Heads.Workspaces.Wrks { if wrk.IsVisible() { wrk.Show() } else { wrk.Hide() } } wm.Heads.ApplyStruts(wm.Clients) wm.FocusFallback() wm.Startup = false pingBefore, pingAfter, pingQuit := xevent.MainPing(X) if len(flagCpuProfile) > 0 { f, err := os.Create(flagCpuProfile) if err != nil { logger.Error.Fatalf("%s\n", err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } if flagWingoRestarted { hook.Fire(hook.Restarted, hook.Args{}) } else { hook.Fire(hook.Startup, hook.Args{}) } EVENTLOOP: for { select { case <-pingBefore: // Wait for the event to finish processing. <-pingAfter case f := <-commands.SafeExec: commands.SafeReturn <- f() case <-pingQuit: break EVENTLOOP } } if wm.Restart { // We need to tell the next invocation of Wingo that it is being // *restarted*. (So that we don't refire the startup hook.) // Thus, search os.Args for "--wingo-restarted". If it doesn't exist, // add it. found := false for _, arg := range os.Args { if strings.ToLower(strings.TrimSpace(arg)) == "--wingo-restarted" { found = true } } if !found { os.Args = append(os.Args, "--wingo-restarted") } logger.Message.Println("The user has told us to restart...\n\n\n") if err := syscall.Exec(os.Args[0], os.Args, os.Environ()); err != nil { logger.Error.Fatalf("Could not exec '%s': %s", strings.Join(os.Args, " "), err) } } }
func rootInit(X *xgbutil.XUtil) { var err error // Listen to Root. It is all-important. evMasks := xproto.EventMaskPropertyChange | xproto.EventMaskFocusChange | xproto.EventMaskButtonPress | xproto.EventMaskButtonRelease | xproto.EventMaskStructureNotify | xproto.EventMaskSubstructureNotify | xproto.EventMaskSubstructureRedirect if wm.Config.FfmHead { evMasks |= xproto.EventMaskPointerMotion } err = xwindow.New(X, X.RootWin()).Listen(evMasks) if err != nil { logger.Error.Fatalf("Could not listen to Root window events: %s", err) } // Update state when the root window changes size wm.RootGeomChangeFun().Connect(X, wm.Root.Id) // Oblige map request events xevent.MapRequestFun( func(X *xgbutil.XUtil, ev xevent.MapRequestEvent) { xclient.New(ev.Window) }).Connect(X, wm.Root.Id) // Oblige configure requests from windows we don't manage. xevent.ConfigureRequestFun( func(X *xgbutil.XUtil, ev xevent.ConfigureRequestEvent) { // Make sure we aren't managing this client. if wm.FindManagedClient(ev.Window) != nil { return } xwindow.New(X, ev.Window).Configure(int(ev.ValueMask), int(ev.X), int(ev.Y), int(ev.Width), int(ev.Height), ev.Sibling, ev.StackMode) }).Connect(X, wm.Root.Id) xevent.FocusInFun( func(X *xgbutil.XUtil, ev xevent.FocusInEvent) { if ignoreRootFocus(ev.Mode, ev.Detail) { return } if len(wm.Workspace().Clients) == 0 { return } wm.FocusFallback() }).Connect(X, wm.Root.Id) // Listen to Root client message events. This is how we handle all // of the EWMH bullshit. xevent.ClientMessageFun(handleClientMessages).Connect(X, wm.Root.Id) // Check where the pointer is on motion events. If it's crossed a monitor // boundary, switch the focus of the head. if wm.Config.FfmHead { xevent.MotionNotifyFun(handleMotionNotify).Connect(X, wm.Root.Id) } }
func New(id xproto.Window) *Client { wm.X.Grab() defer wm.X.Ungrab() // If this is an override redirect, skip... attrs, err := xproto.GetWindowAttributes(wm.X.Conn(), id).Reply() if err != nil { logger.Warning.Printf("Could not get window attributes for '%d': %s", id, err) } else { if attrs.OverrideRedirect { logger.Message.Printf( "Not managing override redirect window %d", id) return nil } } if client := wm.FindManagedClient(id); client != nil { logger.Message.Printf("Already managing client: %s", client) return nil } win := xwindow.New(wm.X, id) if _, err := win.Geometry(); err != nil { logger.Warning.Printf("Could not manage client %d because: %s", id, err) return nil } c := &Client{ win: win, name: "N/A", state: frame.Inactive, layer: stack.LayerDefault, maximized: false, iconified: false, unmapIgnore: 0, floating: false, fullscreen: false, skipTaskbar: false, skipPager: false, demanding: false, attnQuit: make(chan struct{}, 0), } c.manage() // We don't fire the managed hook on startup since it can lead to // unintuitive state changes. // If someone really wants it, we can add a new "startup_managed" hook // or something. if !wm.Startup { event.Notify(event.ManagedClient{c.Id()}) c.FireHook(hook.Managed) } if !c.iconified { c.Map() if !wm.Startup && c.PrimaryType() == TypeNormal { if !wm.Config.Ffm || wm.Config.FfmStartupFocus { c.Focus() } } } return c }
func (c *Client) fetchXProperties() { var err error c.hints, err = icccm.WmHintsGet(wm.X, c.Id()) if err != nil { logger.Warning.Println(err) logger.Message.Printf("Using reasonable defaults for WM_HINTS for %X", c.Id()) c.hints = &icccm.Hints{ Flags: icccm.HintInput | icccm.HintState, Input: 1, InitialState: icccm.StateNormal, } } c.nhints, err = icccm.WmNormalHintsGet(wm.X, c.Id()) if err != nil { logger.Warning.Println(err) logger.Message.Printf("Using reasonable defaults for WM_NORMAL_HINTS "+ "for %X", c.Id()) c.nhints = &icccm.NormalHints{} } c.protocols, err = icccm.WmProtocolsGet(wm.X, c.Id()) if err != nil { logger.Warning.Printf( "Window %X does not have WM_PROTOCOLS set.", c.Id()) } c.winTypes, err = ewmh.WmWindowTypeGet(wm.X, c.Id()) if err != nil { logger.Warning.Printf("Could not find window type for window %X, "+ "using 'normal'.", c.Id()) c.winTypes = []string{"_NET_WM_WINDOW_TYPE_NORMAL"} } c.winStates, err = ewmh.WmStateGet(wm.X, c.Id()) if err != nil { c.winStates = []string{} ewmh.WmStateSet(wm.X, c.Id(), c.winStates) } c.class, err = icccm.WmClassGet(wm.X, c.Id()) if err != nil { logger.Warning.Printf("Could not find window class for window %X: %s", c.Id(), err) c.class = &icccm.WmClass{ Instance: "", Class: "", } } trans, _ := icccm.WmTransientForGet(wm.X, c.Id()) if trans == 0 { for _, c2_ := range wm.Clients { c2 := c2_.(*Client) if c2.transient(c) { c.transientFor = c2 break } } } else if transCli := wm.FindManagedClient(trans); transCli != nil { c.transientFor = transCli.(*Client) } tmp, err := xprop.PropValNum( xprop.GetProperty(wm.X, c.Id(), "_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED")) if err == nil { c.gtkMaximizeNada = (tmp == 1) } else { c.gtkMaximizeNada = false } c.setShaped() }