Example #1
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)
	}
}
Example #2
0
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)
	}
}
Example #3
0
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
	})
}
Example #4
0
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
	})
}
Example #5
0
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
	})
}
Example #6
0
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
	})
}
Example #7
0
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
	})
}
Example #8
0
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
	})
}
Example #9
0
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()
		}
	}
}
Example #10
0
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,
	})
}
Example #11
0
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)
		}
	}
}
Example #12
0
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)
	}
}
Example #13
0
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)
		}
	}
}