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) } }
func (c *Client) unmanage() { wm.X.Grab() defer wm.X.Ungrab() go func() { c.frames.destroy() c.prompts.destroy() }() logger.Message.Printf("Unmanaging client: %s", c) c.frame.Unmap() c.win.Detach() icccm.WmStateSet(wm.X, c.Id(), &icccm.WmState{State: icccm.StateWithdrawn}) focus.Remove(c) wm.FocusFallback() stack.Remove(c) c.workspace.Remove(c) wm.RemoveClient(c) c.attnStop() xproto.ChangeSaveSetChecked( wm.X.Conn(), xproto.SetModeDelete, c.Id()).Check() if c.hadStruts { wm.Heads.ApplyStruts(wm.Clients) } }
func (cmd WorkspaceToHead) Run() gribble.Value { return syncRun(func() gribble.Value { withWorkspace(cmd.Workspace, func(wrk *workspace.Workspace) { wm.WorkspaceToHead(cmd.Head, wrk) wm.FocusFallback() }) return nil }) }
func (cmd WorkspaceGreedy) Run() gribble.Value { return syncRun(func() gribble.Value { withWorkspace(cmd.Workspace, func(wrk *workspace.Workspace) { wm.SetWorkspace(wrk, true) wm.FocusFallback() }) return nil }) }
func (cmd HeadFocus) Run() gribble.Value { return syncRun(func() gribble.Value { wm.Heads.WithVisibleWorkspace(cmd.Head, func(wrk *workspace.Workspace) { wm.SetWorkspace(wrk, false) }) wm.FocusFallback() return nil }) }
func (cmd HeadCycle) Run() gribble.Value { return syncRun(func() gribble.Value { cur := wm.Heads.VisibleIndex(wm.Workspace()) next := misc.Mod(cur+1, wm.Heads.NumHeads()) wm.Heads.WithVisibleWorkspace(next, func(wrk *workspace.Workspace) { wm.SetWorkspace(wrk, false) }) wm.FocusFallback() return nil }) }
func (cmd WorkspaceGreedyWithClient) Run() gribble.Value { return syncRun(func() gribble.Value { withWorkspace(cmd.Workspace, func(wrk *workspace.Workspace) { withClient(cmd.Client, func(c *xclient.Client) { c.Raise() wrk.Add(c) wm.SetWorkspace(wrk, true) wm.FocusFallback() }) }) return nil }) }
func (cmd RemoveWorkspace) Run() gribble.Value { return syncRun(func() gribble.Value { withWorkspace(cmd.Workspace, func(wrk *workspace.Workspace) { if err := wm.RemoveWorkspace(wrk); err != nil { wm.PopupError("Could not remove workspace '%s': %s", wrk, err) return } wm.FYI("Workspace %s removed.", wrk) wm.FocusFallback() }) return nil }) }
func handleMotionNotify(X *xgbutil.XUtil, ev xevent.MotionNotifyEvent) { qp, err := xproto.QueryPointer(X.Conn(), X.RootWin()).Reply() if err != nil { logger.Warning.Printf("Could not query pointer: %s", err) return } geom := xrect.New(int(qp.RootX), int(qp.RootY), 1, 1) if wrk := wm.Heads.FindMostOverlap(geom); wrk != nil { if wrk != wm.Workspace() { wm.SetWorkspace(wrk, false) wm.FocusFallback() } } }
func (c *Client) unmanage() { wm.X.Grab() defer wm.X.Ungrab() go func() { c.frames.destroy() c.prompts.destroy() }() logger.Message.Printf("Unmanaging client: %s", c) infoWorkspace := c.workspace.String() infoClass := c.Class().Class infoInstance := c.Class().Instance infoName := c.Name() c.frame.Unmap() c.win.Detach() icccm.WmStateSet(wm.X, c.Id(), &icccm.WmState{State: icccm.StateWithdrawn}) focus.Remove(c) wm.FocusFallback() stack.Remove(c) c.workspace.Remove(c) wm.RemoveClient(c) c.attnStop() xproto.ChangeSaveSetChecked( wm.X.Conn(), xproto.SetModeDelete, c.Id()).Check() if c.hadStruts { wm.Heads.ApplyStruts(wm.Clients) } event.Notify(event.UnmanagedClient{ Id: c.Id(), Name: infoName, Workspace: infoWorkspace, Class: infoClass, Instance: infoInstance, }) }
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 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")) // Initialize event handlers on the root window. rootInit(X) // Tell everyone what we support. setSupported() // Start up the IPC command listener. go ipc(X) // 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) } } }