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