1/*
2 * Functions involving the info/identify window.
3 */
4
5#include "ctwm.h"
6
7#include <stdio.h>
8#include <stdlib.h>
9
10#include <X11/Xatom.h>
11
12#include "ctopts.h"
13#include "drawing.h"
14#include "functions.h"
15#include "functions_internal.h"
16#include "icons.h"
17#include "otp.h"
18#include "r_area.h"
19#include "r_layout.h"
20#include "screen.h"
21#include "version.h"
22#include "vscreen.h"
23
24
25/* We hold it in a big static buffer */
26#define INFO_LINES 30
27#define INFO_SIZE 200
28static char Info[INFO_LINES][INFO_SIZE];
29
30/* The builder */
31static void Identify(TwmWindow *t);
32
33
34/*
35 * The functions that cause this to pop up.
36 *
37 * n.b.: these are referenced in the Developer Manual in doc/devman/; if
38 * you make any changes here be sure to tweak that if necessary.
39 */
40DFHANDLER(identify)
41{
42	Identify(tmp_win);
43}
44
45DFHANDLER(version)
46{
47	Identify(NULL);
48}
49
50
51/*
52 * Building and displaying.
53 */
54
55/*
56 * Backend for f.identify and f.version: Fills in the Info array with the
57 * appropriate bits for ctwm and the window specified (if any), and
58 * sizes/pops up the InfoWindow.
59 *
60 * Notably, the bits of Info aren't written into the window during this
61 * process; that happens later as a result of the expose event.
62 */
63static void
64Identify(TwmWindow *t)
65{
66	int i, n, twidth, width, height;
67	int x, y;
68	unsigned int wwidth, wheight, bw, depth;
69	Window junk;
70	int px, py, dummy;
71	unsigned udummy;
72	unsigned char *prop;
73	unsigned long nitems, bytesafter;
74	Atom actual_type;
75	int actual_format;
76	XRectangle inc_rect;
77	XRectangle logical_rect;
78	char *ctopts;
79
80	/*
81	 * Include some checking we don't blow out _LINES.  We use snprintf()
82	 * exclusively to avoid blowing out _SIZE.
83	 *
84	 * In an ideal world, we'd probably fix this to be more dynamically
85	 * allocated, but this will do for now.
86	 */
87	n = 0;
88#define CHKN do { \
89                if(n > (INFO_LINES - 3)) { \
90                        fprintf(stderr, "Overflowing Info[] on line %d\n", n); \
91                        sprintf(Info[n++], "(overflow)"); \
92                        goto info_dismiss; \
93                } \
94        } while(0)
95
96	snprintf(Info[n++], INFO_SIZE, "Twm version:  %s", TwmVersion);
97	CHKN;
98	if(VCSRevision) {
99		snprintf(Info[n++], INFO_SIZE, "VCS Revision:  %s", VCSRevision);
100		CHKN;
101	}
102
103	ctopts = ctopts_string(", ");
104	snprintf(Info[n++], INFO_SIZE, "Compile time options : %s", ctopts);
105	free(ctopts);
106	CHKN;
107
108	Info[n++][0] = '\0';
109	CHKN;
110
111	if(t) {
112		// The border would be on the frame, not t->w, so assume our
113		// internal tracking is right for it
114		XGetGeometry(dpy, t->w, &JunkRoot, &JunkX, &JunkY,
115		             &wwidth, &wheight, &JunkBW, &depth);
116		bw = t->frame_bw;
117		XTranslateCoordinates(dpy, t->w, Scr->Root, 0, 0,
118		                      &x, &y, &junk);
119		snprintf(Info[n++], INFO_SIZE, "Name               = \"%s\"",
120		         t->name);
121		CHKN;
122		snprintf(Info[n++], INFO_SIZE, "Class.res_name     = \"%s\"",
123		         t->class.res_name);
124		CHKN;
125		snprintf(Info[n++], INFO_SIZE, "Class.res_class    = \"%s\"",
126		         t->class.res_class);
127		CHKN;
128		Info[n++][0] = '\0';
129		CHKN;
130		snprintf(Info[n++], INFO_SIZE,
131		         "Geometry/root (UL) = %dx%d+%d+%d (Inner: %dx%d+%d+%d)",
132		         wwidth + 2 * (bw + t->frame_bw3D),
133		         wheight + 2 * (bw + t->frame_bw3D) + t->title_height,
134		         x - (bw + t->frame_bw3D),
135		         y - (bw + t->frame_bw3D + t->title_height),
136		         wwidth, wheight, x, y);
137		CHKN;
138		snprintf(Info[n++], INFO_SIZE,
139		         "Geometry/root (LR) = %dx%d-%d-%d (Inner: %dx%d-%d-%d)",
140		         wwidth + 2 * (bw + t->frame_bw3D),
141		         wheight + 2 * (bw + t->frame_bw3D) + t->title_height,
142		         Scr->rootw - (x + wwidth + bw + t->frame_bw3D),
143		         Scr->rooth - (y + wheight + bw + t->frame_bw3D),
144		         wwidth, wheight,
145		         Scr->rootw - (x + wwidth), Scr->rooth - (y + wheight));
146		CHKN;
147		snprintf(Info[n++], INFO_SIZE, "Border width       = %d", bw);
148		CHKN;
149		snprintf(Info[n++], INFO_SIZE, "3D border width    = %d", t->frame_bw3D);
150		CHKN;
151		snprintf(Info[n++], INFO_SIZE, "Depth              = %d", depth);
152		CHKN;
153		if(t->vs &&
154		                t->vs->wsw &&
155		                t->vs->wsw->currentwspc) {
156			snprintf(Info[n++], INFO_SIZE, "Virtual Workspace  = %s",
157			         t->vs->wsw->currentwspc->name);
158			CHKN;
159		}
160		snprintf(Info[n++], INFO_SIZE, "OnTopPriority      = %d",
161		         OtpEffectiveDisplayPriority(t));
162		CHKN;
163
164		if(t->icon != NULL) {
165			int iwx, iwy;
166
167			XGetGeometry(dpy, t->icon->w, &JunkRoot, &iwx, &iwy,
168			             &wwidth, &wheight, &bw, &depth);
169			Info[n++][0] = '\0';
170			CHKN;
171			snprintf(Info[n++], INFO_SIZE, "IconGeom/root     = %dx%d+%d+%d",
172			         wwidth, wheight, iwx, iwy);
173			CHKN;
174			snprintf(Info[n++], INFO_SIZE, "IconGeom/intern   = %dx%d+%d+%d",
175			         t->icon->w_width, t->icon->w_height,
176			         t->icon->w_x, t->icon->w_y);
177			CHKN;
178			snprintf(Info[n++], INFO_SIZE, "IconBorder width  = %d", bw);
179			CHKN;
180			snprintf(Info[n++], INFO_SIZE, "IconDepth         = %d", depth);
181			CHKN;
182		}
183
184		if(XGetWindowProperty(dpy, t->w, XA_WM_CLIENT_MACHINE, 0L, 64, False,
185		                      XA_STRING, &actual_type, &actual_format, &nitems,
186		                      &bytesafter, &prop) == Success) {
187			if(nitems && prop) {
188				snprintf(Info[n++], INFO_SIZE, "Client machine     = %s",
189				         (char *)prop);
190				XFree(prop);
191				CHKN;
192			}
193		}
194		Info[n++][0] = '\0';
195		CHKN;
196	}
197
198#undef CHKN
199info_dismiss:
200	snprintf(Info[n++], INFO_SIZE, "Click to dismiss....");
201
202
203	/*
204	 * OK, it's all built now.
205	 */
206
207	/* figure out the width and height of the info window */
208	height = n * (Scr->DefaultFont.height + 2);
209	width = 1;
210	for(i = 0; i < n; i++) {
211		XmbTextExtents(Scr->DefaultFont.font_set, Info[i],
212		               strlen(Info[i]), &inc_rect, &logical_rect);
213
214		twidth = logical_rect.width;
215		if(twidth > width) {
216			width = twidth;
217		}
218	}
219
220	/* Unmap if it's currently up, while we muck with it */
221	if(Scr->InfoWindow.mapped) {
222		XUnmapWindow(dpy, Scr->InfoWindow.win);
223		/* Don't really need to bother since we're about to reset, but...  */
224		Scr->InfoWindow.mapped = false;
225	}
226
227	/* Stash the new number of lines */
228	Scr->InfoWindow.lines = n;
229
230	width += 10;                /* some padding */
231	height += 10;               /* some padding */
232	if(XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild,
233	                 &dummy, &dummy, &px, &py, &udummy)) {
234		px -= width / 2;
235		py -= height / 3;
236	}
237	else {
238		px = py = 0;
239	}
240
241	{
242		RArea area = RAreaNew(px, py, width, height);
243		int min_x, min_y, max_bottom, max_right;
244
245		RLayoutFindLeftRightEdges(Scr->Layout, &area, &min_x, &max_right);
246		if(px < min_x) {
247			px = min_x;
248		}
249		else if(px + width - 1 > max_right) {
250			px = max_right - width + 1;
251		}
252
253		RLayoutFindTopBottomEdges(Scr->Layout, &area, &min_y, &max_bottom);
254		if(py < min_y) {
255			py = min_y;
256		}
257		else if(py + height - 1 > max_bottom) {
258			py = max_bottom - height + 1;
259		}
260	}
261	XMoveResizeWindow(dpy, Scr->InfoWindow.win, px, py, width, height);
262	XMapRaised(dpy, Scr->InfoWindow.win);
263	Scr->InfoWindow.mapped = true;
264	Scr->InfoWindow.width  = width;
265	Scr->InfoWindow.height = height;
266}
267
268
269/*
270 * And the routine to actually write the text into the InfoWindow.  This
271 * gets called from events.c as a result of Expose events on the window.
272 */
273void
274draw_info_window(void)
275{
276	int i;
277	const int height = Scr->DefaultFont.height + 2;
278
279	Draw3DBorder(Scr->InfoWindow.win, 0, 0,
280	             Scr->InfoWindow.width, Scr->InfoWindow.height,
281	             2, Scr->DefaultC, off, true, false);
282
283	FB(Scr->DefaultC.fore, Scr->DefaultC.back);
284
285	for(i = 0; i < Scr->InfoWindow.lines ; i++) {
286		XmbDrawString(dpy, Scr->InfoWindow.win, Scr->DefaultFont.font_set,
287		              Scr->NormalGC, 5,
288		              (i * height) + Scr->DefaultFont.y + 5,
289		              Info[i], strlen(Info[i]));
290	}
291}
292