xmonad / xmonad

The core of xmonad, a small but functional ICCCM-compliant tiling window manager
https://xmonad.org
BSD 3-Clause "New" or "Revised" License
3.36k stars 290 forks source link

bespoke function to shut down xmonad, possibly with a shutdownHook #77

Open geekosaur opened 7 years ago

geekosaur commented 7 years ago

In the old bug tracker we had a request for a shutdownHook; this was not at the time possible, because there is no way to intercept exitWith or exitSuccess which are currently used by defaultConfig and by configs which make their own "exit" binding (which sadly includes all configs based on the smash-the-world config, but they're going to lose eventually no matter what, unless we stop all xmonad development).

We should use and export a bespoke shutdown function, which initially would just do io exitSuccess; future directions would include support for a shutdownHook and/or possibly arranging for the xmonad entry point to return control to main instead of "hard" stopping the process. (An advantage of the hook is we could pass it an indication of whether we intend to restart or not; doing that by returning control would require xmonad :: XConfig -> IO Bool which might break configs and which, judging by past experience in #haskell, will confuse a lot of beginners.)

thblt commented 7 years ago

This is only partially related, but @pjones suggested on Reddit that the solution I use to cleanly terminate XMonad may be of some use in this issue. In the absence of an internal way to handle termination, I've bound Mod-S-q to spawn a small script, ~/.xmonad/scripts/stop. stop uses wmctrl to ask each open window to close, and killall xmonad iff there are no more open windows. This last step is obviously a really poor approach ("works for me"-level), and could be seriously improved by passing the script the id of the process to kill, or at the very least its name. Another possible improvement would be to wait until the actual process controlling the window has properly terminated (by using xprop to get the pid)

My dotfiles don't have a license because I'm lazy, but if this script can be of any interest and someone would like to build something better based on it, I can release it on any suitable license, or transfer ownership if needed.

liskin commented 3 years ago

Do we still want this? Are there usecases that can't comfortably be solved via other means?

geekosaur commented 3 years ago

It occurs to me that some corner cases with killAllStatusBars might be avoided with a proper shutdownHook.

yongweiy commented 3 years ago

As for me, when switching gpu mode with optimus-manager, I have to shut down xmonad/log out and manually run sudo prime-switch. A shutdownHook would handle such case.

liskin commented 3 years ago

@VictorYYW But why do you need the hook in particular? What would it do that a simple keybinding to spawn "sudo prime-switch" >> io (exitWith ExitSuccess) can't do? Do you actually need to run code after the X connection is closed? Note that the Xorg server would still be running! if you need to run something only after the Xorg server shuts down, then that's way more complicated and a shutdownHook isn't going to help at all.

(Off-topic, but somewhat relevant: Why do you log off in the first place? There are many ways to launch apps/games on the nvidia dGPU that don't require you to shut down your primary X session. With recent enough nvidia architectures, the driver supports PRIME offloading and does power management, so everything just works in a single session. With older GPUs, power management doesn't work well and needs hacks such as bumblebee, primusrun, etc. These have some overhead, which is probably why some people prefer to run the Xorg server directly on nvidia, but you don't need to shutdown your existing session to do that! Just switch to another virtual console and run it there. I've been doing this myself for years.)

yongweiy commented 3 years ago

Excuse me for being incorrect on wording and the confusion. I think what I am trying to do is to run sudo prime-switch after exiting the X session and before initiating a new one. Not sure it is equivalent to "after X connection is closed". For example, if I am going to do it manually, I would exit XMonad via "M-S-q" and that would bring me back to the virtual console since I am not using any display manager.

A bit of context why am I doing this: I have been trying to set up a more smooth way to work with optimus-manager, a GPU switching solution on laptops with dual Nvidia/Intel. According to their documentation, if I don't use one of their supported display manager or don't use a DM at all, I need to make sure to run sudo prime-switch after X server is stopped.

geekosaur commented 3 years ago

A shutdownHook in xmonad won't help with that, since the X server will still be running when the hook is run.

yongweiy commented 3 years ago

Good to know that. Thanks for your reply!

liskin commented 3 years ago

Can some native English speaker perhaps help me understand how my reply was not clear about the X server still running?

(Anyway, I'll add one other unsolicited bit of advice: I also don't use any display manager, and the decision whether to use nvidia or intel is based upon which VT I'm logging into. So when I log into vt9, it's nvidia, and vts 7, 8 and 10 are intel. Other vts are just plain old text console. In other words, the decision to use nvidia/intel isn't made during log off, but during log in. Perhaps you'd like something like that as well? Obviously, my recommendation to not shut down the intel X when you need nvidia for a short while still stands.)

yongweiy commented 3 years ago

Can some native English speaker perhaps help me understand how my reply was not clear about the X server still running?

It was me who lack understanding of the basic mechanism of X Org. Nothing wrong about your explanation.

(Anyway, I'll add one other unsolicited bit of advice: I also don't use any display manager, and the decision whether to use nvidia or intel is based upon which VT I'm logging into. So when I log into vt9, it's nvidia, and vts 7, 8 and 10 are intel. Other vts are just plain old text console. In other words, the decision to use nvidia/intel isn't made during log off, but during log in. Perhaps you'd like something like that as well? Obviously, my recommendation to not shut down the intel X when you need nvidia for a short while still stands.)

That sounds like a solution as well. I will keep that in mind. Thanks!

TheMC47 commented 3 years ago

Just chiming in to say that for purposes of cleaning up between restarts, we can emulate a shutdownHook with a persistent state and a startupHook (in fact, this might be a good contrib module) . For other purposes, I'd have to agree with @liskin that binding a key to doStuff >> existSuccess should be enough

PRESFIL commented 2 years ago

Is there anyone here who thinks it would be better to create a separate shutdownHook instead of the persistent state and a startupHook as it is currently implemented?

I believe that XMonad should reap all of its child processes on exit. Specifically in my case, docks and bars. And if it could be done using X.H.StatusBar.statusBarGeneric, transparently, it would be great. Do I understand correctly that now all the additional processes of bars are left on their own and they are killed by display-manager or by init or die from disabling X-display connection?

geekosaur commented 2 years ago

There is no magical way to just nuke all child processes. Plus there are the processes that aren't direct children (consider a status bar launched from a shell for debugging, or some other application). The way most apps die now is that xinit is waiting on the user session (meaning xmonad) to exit, and when it does it shuts down the X server which closes all server sockets. Desktop managers with session managers often change this, but that requires that all applications register themselves with the session manager and you will still see the default behavior from apps like xterm that don't.

geekosaur commented 2 years ago

Moreover, if you go check the history of X.H.StatusBar, you will find there are problems with the whole idea of tracking applications: conceivably an application exits, xmonad doesn't know it, then some other application gets the same process ID and gets closed inappropriately on mod-q. There is no reliable fix for this, as simply using SIGCHLD only catches direct children as above, and while there are ways on Linux to get SIGCHLD on any child in a session instead of direct children, this will break for terminals (which create their own sessions) and is not portable to the *BSDs (where xmonad originated and is still used).

PRESFIL commented 2 years ago

It is regrettable. I saw that SIGCHLD handling is not implemented.

At least I could try to emit this behavior by adding killAllStatusBars at the bottom of main?

geekosaur commented 2 years ago

It was attempted at one point but broke xmonad for sjanssen (the original xmonad developer) because he was on OpenBSD. I don't think you can find it in repo history because I had to move it from the original darcs repo on code.haskell.org rather quickly and a github token wasn't helping with moving a converted repo.

The bottom of main is never reached; see https://github.com/xmonad/xmonad/blob/master/src/XMonad/Config.hs#L221 which is the main reason why I wanted this in the first place.

PRESFIL commented 2 years ago

Somehow it would be possible to modify the "main loop" to keep track of the Exstensible State, in which it should be set the special exit flag. But it can't be done without xmonad-contrib and I wouldn't do that...

Another option is to return a non-empty X(..) with a state inside, but will break something.

... then we reach bottom of main.

geekosaur commented 2 years ago

My ideal for the bespoke shutdown function would be to set a new flag in the XState (which is not generally accessed directly by users, only piecemeal via gets) and check it just before nextEvent. Ideally then returning a Bool indicating whether we're restarting or not (or perhaps avoiding boolean blindness and using a new type that was explicit about which). This would indeed also avoid the shutdownHook change by allowing users to do it in main. But has the problem that some things might need to access ExtensibleState, like the one you brought up (the statusbar shutdown function, currently invoked in the restarted xmonad's startupHook instead).