1b18c2d1eSnia/* 2b18c2d1eSnia * Shutdown (/restart) bits 3b18c2d1eSnia */ 4b18c2d1eSnia 5b18c2d1eSnia#include "ctwm.h" 6b18c2d1eSnia 7b18c2d1eSnia#include <stdlib.h> 8b18c2d1eSnia#include <stdio.h> 9b18c2d1eSnia#include <unistd.h> 10b18c2d1eSnia 11b18c2d1eSnia#include "animate.h" 12b18c2d1eSnia#ifdef CAPTIVE 13b18c2d1eSnia#include "captive.h" 14b18c2d1eSnia#endif 15b18c2d1eSnia#include "colormaps.h" 16b18c2d1eSnia#include "ctwm_atoms.h" 17b18c2d1eSnia#include "ctwm_shutdown.h" 18b18c2d1eSnia#include "screen.h" 19b18c2d1eSnia#ifdef SESSION 20b18c2d1eSnia#include "session.h" 21b18c2d1eSnia#endif 22b18c2d1eSnia#ifdef SOUNDS 23b18c2d1eSnia# include "sound.h" 24b18c2d1eSnia#endif 25b18c2d1eSnia#include "otp.h" 26b18c2d1eSnia#include "win_ops.h" 27b18c2d1eSnia#include "win_utils.h" 28b18c2d1eSnia 29b18c2d1eSnia 30b18c2d1eSniastatic void RestoreForShutdown(Time mytime); 31b18c2d1eSnia 32b18c2d1eSnia 33b18c2d1eSnia/** 34b18c2d1eSnia * Put a window back where it should be if we don't (any longer) control 35b18c2d1eSnia * it and reparent it back up to the root. This leaves it where it was 36b18c2d1eSnia * before we started (well, adjusted by any moves we've made to it 37b18c2d1eSnia * since), and placed so that if we restart and take it back over, it'll 38b18c2d1eSnia * wind up right where it is now, so restarting doesn't shift windows all 39b18c2d1eSnia * over the place. 40b18c2d1eSnia */ 41b18c2d1eSniavoid 42b18c2d1eSniaRestoreWinConfig(TwmWindow *tmp) 43b18c2d1eSnia{ 44b18c2d1eSnia int gravx, gravy; 45b18c2d1eSnia int newx, newy; 46b18c2d1eSnia 47b18c2d1eSnia // Things adjusting by the border have to move our border size, but 48b18c2d1eSnia // subtract away from that the old border we're restoring. 49b18c2d1eSnia const int borders = tmp->frame_bw + tmp->frame_bw3D - tmp->old_bw; 50b18c2d1eSnia 51b18c2d1eSnia // If this window is "unmapped" by moving it way offscreen, and is in 52b18c2d1eSnia // that state, move it back onto the window. 53b18c2d1eSnia if(tmp->UnmapByMovingFarAway && !visible(tmp)) { 54b18c2d1eSnia XMoveWindow(dpy, tmp->frame, tmp->frame_x, tmp->frame_y); 55b18c2d1eSnia } 56b18c2d1eSnia 57b18c2d1eSnia // If it's squeezed, unsqueeze it. 58b18c2d1eSnia if(tmp->squeezed) { 59b18c2d1eSnia Squeeze(tmp); 60b18c2d1eSnia } 61b18c2d1eSnia 62b18c2d1eSnia // This is apparently our standard "is this window still around?" 63b18c2d1eSnia // idiom. 64b18c2d1eSnia if(XGetGeometry(dpy, tmp->w, &JunkRoot, &JunkX, &JunkY, 65b18c2d1eSnia &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0) { 66b18c2d1eSnia // Well, give up... 67b18c2d1eSnia return; 68b18c2d1eSnia } 69b18c2d1eSnia 70b18c2d1eSnia // Get gravity bits to know how to move stuff around when we take 71b18c2d1eSnia // away the decorations. 72b18c2d1eSnia GetGravityOffsets(tmp, &gravx, &gravy); 73b18c2d1eSnia 74b18c2d1eSnia // We want to move the window to where it should be "outside" of our 75b18c2d1eSnia // frame. This varies depending on the window gravity detail, and we 76b18c2d1eSnia // have to account for that, since on re-startup we'll be doing it to 77b18c2d1eSnia // resposition it after we re-wrap it. 78b18c2d1eSnia // 79b18c2d1eSnia // e.g., in simple "NorthWest" gravity, we just made the frame start 80b18c2d1eSnia // where the window did, and shifted the app window right (by the 81b18c2d1eSnia // border width) and down (by the border width + titlebar). However, 82b18c2d1eSnia // "SouthEast" gravity means the bottom right of the frame is where 83b18c2d1eSnia // the windows' was, and the window itself shifted left/up by the 84b18c2d1eSnia // border. Compare e.g. an xterm with -geometry "+0+0" with one 85b18c2d1eSnia // using "-0-0" as an easy trick to make windows with different 86b18c2d1eSnia // geoms. 87b18c2d1eSnia newx = tmp->frame_x; 88b18c2d1eSnia newy = tmp->frame_y; 89b18c2d1eSnia 90b18c2d1eSnia 91b18c2d1eSnia // So, first consider the north/south gravity. If gravity is North, 92b18c2d1eSnia // we put the top of the frame where the window was and shifted 93b18c2d1eSnia // everything inside down, so the top of the frame now is where the 94b18c2d1eSnia // window should be put. With South-y gravity, the window should 95b18c2d1eSnia // wind up at the bottom of the frame, which means we need to shift 96b18c2d1eSnia // it down by the titlebar height, plus twice the border width. But 97b18c2d1eSnia // if the vertical gravity is neutral, then the window needs to wind 98b18c2d1eSnia // up staying right where it is, because we built the frame around it 99b18c2d1eSnia // without moving it. 100b18c2d1eSnia // 101b18c2d1eSnia // Previous code here (and code elsewhere) expressed this calculation 102b18c2d1eSnia // by the rather confusing idiom ((gravy + 1) * border_width), which 103b18c2d1eSnia // gives the right answer, but is confusing as hell... 104b18c2d1eSnia if(gravy < 0) { 105b18c2d1eSnia // North; where the frame starts (already set) 106b18c2d1eSnia } 107b18c2d1eSnia else if(gravy > 0) { 108b18c2d1eSnia // South; shift down title + 2*border 109b18c2d1eSnia newy += tmp->title_height + 2 * borders; 110b18c2d1eSnia } 111b18c2d1eSnia else { 112b18c2d1eSnia // Neutral; down by the titlebar + border. 113b18c2d1eSnia newy += tmp->title_height + borders; 114b18c2d1eSnia } 115b18c2d1eSnia 116b18c2d1eSnia 117b18c2d1eSnia // Now east/west. West means align with the frame start, east means 118b18c2d1eSnia // align with the frame right edge, neutral means where it already 119b18c2d1eSnia // is. 120b18c2d1eSnia if(gravx < 0) { 121b18c2d1eSnia // West; it's already correct 122b18c2d1eSnia } 123b18c2d1eSnia else if(gravx > 0) { 124b18c2d1eSnia // East; shift over by 2*border 125b18c2d1eSnia newx += 2 * borders; 126b18c2d1eSnia } 127b18c2d1eSnia else { 128b18c2d1eSnia // Neutral; over by the left border 129b18c2d1eSnia newx += borders; 130b18c2d1eSnia } 131b18c2d1eSnia 132b18c2d1eSnia 133b18c2d1eSnia#ifdef WINBOX 134b18c2d1eSnia // If it's in a WindowBox, reparent the frame back up to our real root 135b18c2d1eSnia if(tmp->winbox && tmp->winbox->twmwin && tmp->frame) { 136b18c2d1eSnia int xbox, ybox; 137b18c2d1eSnia unsigned int j_bw; 138b18c2d1eSnia 139b18c2d1eSnia // XXX This isn't right, right? This will give coords relative 140b18c2d1eSnia // to the window box, but we're using them relative to the real 141b18c2d1eSnia // screen root? 142b18c2d1eSnia if(XGetGeometry(dpy, tmp->frame, &JunkRoot, &xbox, &ybox, 143b18c2d1eSnia &JunkWidth, &JunkHeight, &j_bw, &JunkDepth)) { 144b18c2d1eSnia ReparentWindow(dpy, tmp, WinWin, Scr->Root, xbox, ybox); 145b18c2d1eSnia } 146b18c2d1eSnia } 147b18c2d1eSnia#endif 148b18c2d1eSnia 149b18c2d1eSnia 150b18c2d1eSnia // Restore the original window border if there were one 151b18c2d1eSnia if(tmp->old_bw) { 152b18c2d1eSnia XSetWindowBorderWidth(dpy, tmp->w, tmp->old_bw); 153b18c2d1eSnia } 154b18c2d1eSnia 155b18c2d1eSnia // Reparent and move back to where it otter be 156b18c2d1eSnia XReparentWindow(dpy, tmp->w, Scr->Root, newx, newy); 157b18c2d1eSnia 158b18c2d1eSnia // If it came with a pre-made icon window, hide it 159b18c2d1eSnia if(tmp->wmhints->flags & IconWindowHint) { 160b18c2d1eSnia XUnmapWindow(dpy, tmp->wmhints->icon_window); 161b18c2d1eSnia } 162b18c2d1eSnia 163b18c2d1eSnia // Done 164b18c2d1eSnia return; 165b18c2d1eSnia} 166b18c2d1eSnia 167b18c2d1eSnia 168b18c2d1eSnia/** 169b18c2d1eSnia * Restore some window positions/etc in preparation for going away. 170b18c2d1eSnia */ 171b18c2d1eSniastatic void 172b18c2d1eSniaRestoreForShutdown(Time mytime) 173b18c2d1eSnia{ 174b18c2d1eSnia ScreenInfo *savedScr = Scr; // We need Scr flipped around... 175b18c2d1eSnia 176b18c2d1eSnia XGrabServer(dpy); 177b18c2d1eSnia 178b18c2d1eSnia for(int scrnum = 0; scrnum < NumScreens; scrnum++) { 179b18c2d1eSnia if((Scr = ScreenList[scrnum]) == NULL) { 180b18c2d1eSnia continue; 181b18c2d1eSnia } 182b18c2d1eSnia 183b18c2d1eSnia // Force reinstalling any colormaps 184b18c2d1eSnia InstallColormaps(0, &Scr->RootColormaps); 185b18c2d1eSnia 186b18c2d1eSnia // Pull all the windows out of their frames and reposition them 187b18c2d1eSnia // where the frame was, with approprate adjustments for gravity. 188b18c2d1eSnia // This will preserve their positions when we restart, or put 189b18c2d1eSnia // them back where they were before we started. Do it from the 190b18c2d1eSnia // bottom up of our stacking order to preserve the stacking. 191b18c2d1eSnia for(TwmWindow *tw = OtpBottomWin() ; tw != NULL 192b18c2d1eSnia ; tw = OtpNextWinUp(tw)) { 193b18c2d1eSnia if(tw->isiconmgr || tw->iswspmgr || tw->isoccupy) { 194b18c2d1eSnia // Don't bother with internals... 195b18c2d1eSnia continue; 196b18c2d1eSnia } 197b18c2d1eSnia RestoreWinConfig(tw); 198b18c2d1eSnia } 199b18c2d1eSnia } 200b18c2d1eSnia 201b18c2d1eSnia XUngrabServer(dpy); 202b18c2d1eSnia 203b18c2d1eSnia // Restore 204b18c2d1eSnia Scr = savedScr; 205b18c2d1eSnia 206b18c2d1eSnia // Focus away from any windows 207b18c2d1eSnia SetFocus(NULL, mytime); 208b18c2d1eSnia} 209b18c2d1eSnia 210b18c2d1eSnia 211b18c2d1eSnia/** 212b18c2d1eSnia * Cleanup and exit ctwm 213b18c2d1eSnia */ 214b18c2d1eSniavoid 215b18c2d1eSniaDoShutdown(void) 216b18c2d1eSnia{ 217b18c2d1eSnia 218b18c2d1eSnia#ifdef SOUNDS 219b18c2d1eSnia // Denounce ourselves 220b18c2d1eSnia play_exit_sound(); 221b18c2d1eSnia#endif 222b18c2d1eSnia 223b18c2d1eSnia // Restore windows/colormaps for our absence. 224b18c2d1eSnia RestoreForShutdown(CurrentTime); 225b18c2d1eSnia 226b18c2d1eSnia#ifdef EWMH 227b18c2d1eSnia // Clean up EWMH properties 228b18c2d1eSnia EwmhTerminate(); 229b18c2d1eSnia#endif 230b18c2d1eSnia 231b18c2d1eSnia // Clean up our list of workspaces 232b18c2d1eSnia XDeleteProperty(dpy, Scr->Root, XA_WM_WORKSPACESLIST); 233b18c2d1eSnia 234b18c2d1eSnia#ifdef CAPTIVE 235b18c2d1eSnia // Shut down captive stuff 236b18c2d1eSnia if(CLarg.is_captive) { 237b18c2d1eSnia RemoveFromCaptiveList(Scr->captivename); 238b18c2d1eSnia } 239b18c2d1eSnia#endif 240b18c2d1eSnia 241b18c2d1eSnia // Close up shop 242b18c2d1eSnia XCloseDisplay(dpy); 243b18c2d1eSnia exit(0); 244b18c2d1eSnia} 245b18c2d1eSnia 246b18c2d1eSnia 247b18c2d1eSnia/** 248b18c2d1eSnia * exec() ourself to restart. 249b18c2d1eSnia */ 250b18c2d1eSniavoid 251b18c2d1eSniaDoRestart(Time t) 252b18c2d1eSnia{ 253b18c2d1eSnia // Don't try to do any further animating 254b18c2d1eSnia StopAnimation(); 255b18c2d1eSnia XSync(dpy, 0); 256b18c2d1eSnia 257b18c2d1eSnia // Replace all the windows/colormaps as if we were going away. 'cuz 258b18c2d1eSnia // we are. 259b18c2d1eSnia RestoreForShutdown(t); 260b18c2d1eSnia XSync(dpy, 0); 261b18c2d1eSnia 262b18c2d1eSnia#ifdef SESSION 263b18c2d1eSnia // Shut down session management connection cleanly. 264b18c2d1eSnia shutdown_session(); 265b18c2d1eSnia#endif 266b18c2d1eSnia 267b18c2d1eSnia // Re-run ourself 268b18c2d1eSnia fprintf(stderr, "%s: restarting: %s\n", ProgramName, *Argv); 269b18c2d1eSnia execvp(*Argv, Argv); 270b18c2d1eSnia 271b18c2d1eSnia // Uh oh, we shouldn't get here... 272b18c2d1eSnia fprintf(stderr, "%s: unable to restart: %s\n", ProgramName, *Argv); 273b18c2d1eSnia 274b18c2d1eSnia // We should probably un-RestoreForShutdown() etc. If that exec 275b18c2d1eSnia // fails, we're in a really weird state... 276b18c2d1eSnia XBell(dpy, 0); 277b18c2d1eSnia} 278