Пример #1
0
// WMGracefulClose will do all the necessary setup to implement the
// WM_DELETE_WINDOW protocol. This will prevent well-behaving window managers
// from killing your client whenever one of your windows is closed. (Killing
// a client is bad because it will destroy your X connection and any other
// clients you have open.)
// You must provide a callback function that is called when the window manager
// asks you to close your window. (You may provide some means of confirmation
// to the user, i.e., "Do you really want to quit?", but you should probably
// just wrap things up and call DestroyWindow.)
func (w *Window) WMGracefulClose(cb func(w *Window)) {
	// Get the current protocols so we don't overwrite anything.
	prots, _ := icccm.WmProtocolsGet(w.X, w.Id)

	// If WM_DELETE_WINDOW isn't here, add it. Otherwise, move on.
	wmdelete := false
	for _, prot := range prots {
		if prot == "WM_DELETE_WINDOW" {
			wmdelete = true
			break
		}
	}
	if !wmdelete {
		icccm.WmProtocolsSet(w.X, w.Id, append(prots, "WM_DELETE_WINDOW"))
	}

	// Attach a ClientMessage event handler. It will determine whether the
	// ClientMessage is a 'close' request, and if so, run the callback 'cb'
	// provided.
	xevent.ClientMessageFun(
		func(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) {
			if icccm.IsDeleteProtocol(X, ev) {
				cb(w)
			}
		}).Connect(w.X, w.Id)
}
Пример #2
0
func (c *Client) cbClientMessage() xevent.ClientMessageFun {
	f := func(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) {
		name, err := xprop.AtomName(wm.X, ev.Type)
		if err != nil {
			logger.Warning.Printf("Could not get property atom name for "+
				"ClientMessage event on '%s': %s.", c, err)
			return
		}

		logger.Lots.Printf(
			"Handling ClientMessage '%s' on client '%s'.", name, c)
		c.handleClientMessage(name, ev.Data.Data32)
	}
	return xevent.ClientMessageFun(f)
}
Пример #3
0
func main() {
	X, err := xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	// Start generating other source events.
	otherChan := otherSource()

	// Start generating X events (by sending client messages to root window).
	go xSource(X)

	// Listen to those X events.
	xwindow.New(X, X.RootWin()).Listen(xproto.EventMaskSubstructureNotify)

	// Respond to those X events.
	xevent.ClientMessageFun(
		func(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) {
			atmName, err := xprop.AtomName(X, ev.Type)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("ClientMessage: %d. %s\n", ev.Data.Data32[0], atmName)
		}).Connect(X, X.RootWin())

	// Instead of using the usual xevent.Main, we use xevent.MainPing.
	// It runs the main event loop inside a goroutine and returns ping
	// channels, which are sent benign values right before an event is
	// dequeued and right after that event has finished running all callbacks
	// associated with it, respectively.
	pingBefore, pingAfter, pingQuit := xevent.MainPing(X)
	for {
		select {
		case <-pingBefore:
			// Wait for the event to finish processing.
			<-pingAfter
		case otherVal := <-otherChan:
			fmt.Printf("Processing other event: %d\n", otherVal)
		case <-pingQuit:
			fmt.Printf("xevent loop has quit")
			return
		}
	}
}
Пример #4
0
// WMTakeFocus will do all the necessary setup to support the WM_TAKE_FOCUS
// protocol using the "LocallyActive" input model described in Section 4.1.7
// of the ICCCM. Namely, listening to ClientMessage events and running the
// callback function provided when a WM_TAKE_FOCUS ClientMessage has been
// received.
//
// Typically, the callback function should include a call to SetInputFocus
// with the "Parent" InputFocus type, the sub-window id of the window that
// should have focus, and the 'tstamp' timestamp.
func (w *Window) WMTakeFocus(cb func(w *Window, tstamp xproto.Timestamp)) {
	// Make sure the Input flag is set to true in WM_HINTS. We first
	// must retrieve the current WM_HINTS, so we don't overwrite the flags.
	curFlags := 0
	if hints, err := icccm.WmHintsGet(w.X, w.Id); err == nil {
		curFlags = hints.Flags
	}
	icccm.WmHintsSet(w.X, w.Id, &icccm.Hints{
		Flags: curFlags | icccm.HintInput,
		Input: 1,
	})

	// Get the current protocols so we don't overwrite anything.
	prots, _ := icccm.WmProtocolsGet(w.X, w.Id)

	// If WM_TAKE_FOCUS isn't here, add it. Otherwise, move on.
	wmfocus := false
	for _, prot := range prots {
		if prot == "WM_TAKE_FOCUS" {
			wmfocus = true
			break
		}
	}
	if !wmfocus {
		icccm.WmProtocolsSet(w.X, w.Id, append(prots, "WM_TAKE_FOCUS"))
	}

	// Attach a ClientMessage event handler. It will determine whether the
	// ClientMessage is a 'focus' request, and if so, run the callback 'cb'
	// provided.
	xevent.ClientMessageFun(
		func(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) {
			if icccm.IsFocusProtocol(X, ev) {
				cb(w, xproto.Timestamp(ev.Data.Data32[1]))
			}
		}).Connect(w.X, w.Id)
}
Пример #5
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)
		}
	}
}
Пример #6
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)
	}
}
Пример #7
0
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
}
Пример #8
0
func main() {
	var err error

	X, err = xgbutil.NewConn()
	if err != nil {
		logger.Error.Println(err)
		logger.Error.Println("Error connecting to X, quitting...")
		return
	}
	defer X.Conn().Close()

	keybind.Initialize(X)
	mousebind.Initialize(X)
	focus.Initialize(X)
	stack.Initialize(X)

	wingo = newWingoState()

	// Create a root window abstraction and load its geometry
	wingo.root = xwindow.New(X, X.RootWin())
	_, err = wingo.root.Geometry()
	if err != nil {
		logger.Error.Println("Could not get ROOT window geometry because: %v",
			err)
		logger.Error.Println("Cannot continue. Quitting...")
		return
	}

	// Load configuration
	wingo.conf, err = loadConfig()
	if err != nil {
		logger.Error.Println(err)
		logger.Error.Println("No configuration found. Quitting...")
		return
	}

	// Load theme
	wingo.theme, err = loadTheme(X)
	if err != nil {
		logger.Error.Println(err)
		logger.Error.Println("No theme configuration found. Quitting...")
		return
	}

	// Initialize prompts
	wingo.prompts = newPrompts()

	wingo.initializeHeads()

	// Attach all global key bindings
	attachAllKeys()

	// Attach all root mouse bindings
	rootMouseConfig()

	// Setup some cursors we use
	cursors.Setup(X)

	// Listen to Root. It is all-important.
	wingo.root.Listen(xproto.EventMaskPropertyChange |
		xproto.EventMaskStructureNotify |
		xproto.EventMaskSubstructureNotify |
		xproto.EventMaskSubstructureRedirect)

	// Update state when the root window changes size
	// xevent.ConfigureNotifyFun(rootGeometryChange).Connect(X, wingo.root.Id)

	// Oblige map request events
	xevent.MapRequestFun(
		func(X *xgbutil.XUtil, ev xevent.MapRequestEvent) {
			newClient(X, ev.Window)
		}).Connect(X, wingo.root.Id)

	// Oblige configure requests from windows we don't manage.
	xevent.ConfigureRequestFun(
		func(X *xgbutil.XUtil, ev xevent.ConfigureRequestEvent) {
			flags := int(ev.ValueMask) &
				^int(xproto.ConfigWindowSibling) &
				^int(xproto.ConfigWindowStackMode)
			xwindow.New(X, ev.Window).Configure(flags,
				int(ev.X), int(ev.Y), int(ev.Width), int(ev.Height),
				ev.Sibling, ev.StackMode)
		}).Connect(X, wingo.root.Id)

	// Listen to Root client message events.
	// We satisfy EWMH with these AND it also provides a mechanism
	// to issue commands using wingo-cmd.
	xevent.ClientMessageFun(commandHandler).Connect(X, wingo.root.Id)

	xevent.Main(X)
}