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