1 /* Skippy - Seduces Kids Into Perversion
2  *
3  * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 #include "skippy.h"
21 
22 /* from 'uncover': */
23 static Visual *
find_argb_visual(Display * dpy,int scr)24 find_argb_visual (Display *dpy, int scr)
25 {
26 	XVisualInfo template = {
27 		.screen = scr, .depth = 32, .class = TrueColor,
28 	};
29 	int	nvi = 0;
30 	XVisualInfo *xvi = XGetVisualInfo (dpy,
31 			VisualScreenMask | VisualDepthMask | VisualClassMask,
32 			&template, &nvi);
33 	if (!xvi) return NULL;
34 
35 	Visual *visual = NULL;;
36 	for (int i = 0; i < nvi; ++i) {
37 		XRenderPictFormat *format =
38 			XRenderFindVisualFormat (dpy, xvi[i].visual);
39 		if (format->type == PictTypeDirect && format->direct.alphaMask) {
40 			visual = xvi[i].visual;
41 			break;
42 		}
43 	}
44 
45 	XFree (xvi);
46 	return visual;
47 }
48 
49 MainWin *
mainwin_create(session_t * ps)50 mainwin_create(session_t *ps) {
51 	Display * const dpy = ps->dpy;
52 
53 	const char *tmp;
54 	double tmp_d;
55 	XColor exact_color;
56 	XSetWindowAttributes wattr;
57 	XWindowAttributes rootattr;
58 	XRenderPictureAttributes pa;
59 	XRenderColor clear;
60 
61 	// Get ARGB visual.
62 	// FIXME: Move this to skippy.c?
63 	if (!ps->argb_visual)
64 		ps->argb_visual = find_argb_visual(dpy, ps->screen);
65 
66 	// calloc() makes sure it's filled with zero
67 	MainWin *mw = allocchk(calloc(1, sizeof(MainWin)));
68 
69 	mw->ps = ps;
70 	if (ps->o.lazyTrans) {
71 		mw->depth  = 32;
72 		mw->visual = ps->argb_visual;
73 		if (!mw->visual) {
74 			printfef("(): Couldn't find ARGB visual, lazy transparency can't work.");
75 			goto mainwin_create_err;
76 		}
77 	}
78 	if (!ps->o.lazyTrans) {
79 		mw->depth = DefaultDepth(dpy, ps->screen);
80 		mw->visual = DefaultVisual(dpy, ps->screen);
81 	}
82 	mw->colormap = XCreateColormap(dpy, ps->root, mw->visual, AllocNone);
83 	mw->bg_pixmap = None;
84 	mw->background = None;
85 	mw->format = XRenderFindVisualFormat(dpy, mw->visual);
86 #ifdef CFG_XINERAMA
87 	mw->xin_info = mw->xin_active = 0;
88 	mw->xin_screens = 0;
89 #endif /* CFG_XINERAMA */
90 
91 	mw->pressed = mw->focus = 0;
92 	mw->tooltip = 0;
93 	mw->cod = 0;
94 	mw->key_up = XKeysymToKeycode(dpy, XK_Up);
95 	mw->key_down = XKeysymToKeycode(dpy, XK_Down);
96 	mw->key_left = XKeysymToKeycode(dpy, XK_Left);
97 	mw->key_right = XKeysymToKeycode(dpy, XK_Right);
98 	mw->key_h = XKeysymToKeycode(dpy, XK_h);
99 	mw->key_j = XKeysymToKeycode(dpy, XK_j);
100 	mw->key_k = XKeysymToKeycode(dpy, XK_k);
101 	mw->key_l = XKeysymToKeycode(dpy, XK_l);
102 	mw->key_enter = XKeysymToKeycode(dpy, XK_Return);
103 	mw->key_space = XKeysymToKeycode(dpy, XK_space);
104 	mw->key_escape = XKeysymToKeycode(dpy, XK_Escape);
105 	mw->key_q = XKeysymToKeycode(dpy, XK_q);
106 
107 	XGetWindowAttributes(dpy, ps->root, &rootattr);
108 	mw->x = mw->y = 0;
109 	mw->width = rootattr.width;
110 	mw->height = rootattr.height;
111 
112 	wattr.colormap = mw->colormap;
113 	wattr.background_pixel = wattr.border_pixel = 0;
114 	wattr.override_redirect = True;
115 	// I have no idea why, but seemingly without ButtonPressMask, we can't
116 	// receive ButtonRelease events in some cases
117 	wattr.event_mask = VisibilityChangeMask | ButtonPressMask
118 		| ButtonReleaseMask | KeyPressMask | KeyReleaseMask;
119 
120 	mw->window = XCreateWindow(dpy, ps->root, 0, 0, mw->width, mw->height, 0,
121 			mw->depth, InputOutput, mw->visual,
122 			CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &wattr);
123 	if (!mw->window) {
124 		free(mw);
125 		return 0;
126 	}
127 	wm_wid_set_info(ps, mw->window, "main window", None);
128 
129 #ifdef CFG_XINERAMA
130 	if (ps->xinfo.xinerama_exist && XineramaIsActive(dpy)) {
131 		mw->xin_info = XineramaQueryScreens(ps->dpy, &mw->xin_screens);
132 # ifdef DEBUG_XINERAMA
133 		printfef("(): Xinerama is enabled (%d screens).", mw->xin_screens);
134 # endif /* DEBUG_XINERAMA */
135 	}
136 #endif /* CFG_XINERAMA */
137 
138 	XCompositeRedirectSubwindows (ps->dpy, ps->root, CompositeRedirectAutomatic);
139 
140 	tmp_d = ps->o.updateFreq;
141 	if(tmp_d != 0.0)
142 		mw->poll_time = (1.0 / tmp_d) * 1000.0;
143 	else
144 		mw->poll_time = 0;
145 
146 	if(!XParseColor(ps->dpy, mw->colormap, ps->o.normal_tint, &exact_color)) {
147 		printfef("(): Couldn't look up color '%s', reverting to black.", ps->o.normal_tint);
148 		mw->normalTint.red = mw->normalTint.green = mw->normalTint.blue = 0;
149 	}
150 	else
151 	{
152 		mw->normalTint.red = exact_color.red;
153 		mw->normalTint.green = exact_color.green;
154 		mw->normalTint.blue = exact_color.blue;
155 	}
156 	mw->normalTint.alpha = alphaconv(ps->o.normal_tintOpacity);
157 
158 	tmp = ps->o.highlight_tint;
159 	if(! XParseColor(ps->dpy, mw->colormap, tmp, &exact_color))
160 	{
161 		fprintf(stderr, "Couldn't look up color '%s', reverting to #101020", tmp);
162 		mw->highlightTint.red = mw->highlightTint.green = 0x10;
163 		mw->highlightTint.blue = 0x20;
164 	}
165 	else
166 	{
167 		mw->highlightTint.red = exact_color.red;
168 		mw->highlightTint.green = exact_color.green;
169 		mw->highlightTint.blue = exact_color.blue;
170 	}
171 	mw->highlightTint.alpha = alphaconv(ps->o.highlight_tintOpacity);
172 
173 	pa.repeat = True;
174 	clear.alpha = alphaconv(ps->o.normal_opacity);
175 	mw->normalPixmap = XCreatePixmap(ps->dpy, mw->window, 1, 1, 8);
176 	mw->normalPicture = XRenderCreatePicture(ps->dpy, mw->normalPixmap, XRenderFindStandardFormat(ps->dpy, PictStandardA8), CPRepeat, &pa);
177 	XRenderFillRectangle(ps->dpy, PictOpSrc, mw->normalPicture, &clear, 0, 0, 1, 1);
178 
179 	clear.alpha = alphaconv(ps->o.highlight_opacity);
180 	mw->highlightPixmap = XCreatePixmap(ps->dpy, mw->window, 1, 1, 8);
181 	mw->highlightPicture = XRenderCreatePicture(ps->dpy, mw->highlightPixmap, XRenderFindStandardFormat(ps->dpy, PictStandardA8), CPRepeat, &pa);
182 	XRenderFillRectangle(ps->dpy, PictOpSrc, mw->highlightPicture, &clear, 0, 0, 1, 1);
183 
184 	mw->distance = ps->o.distance;
185 
186 	if (ps->o.tooltip_show)
187 		mw->tooltip = tooltip_create(mw);
188 
189 	return mw;
190 
191 mainwin_create_err:
192 	if (mw)
193 		free(mw);
194 	return NULL;
195 }
196 
197 void
mainwin_update_background(MainWin * mw)198 mainwin_update_background(MainWin *mw) {
199 	session_t *ps = mw->ps;
200 
201 	Pixmap root = wm_get_root_pmap(ps->dpy);
202 	XRenderPictureAttributes pa;
203 
204 	if(mw->bg_pixmap)
205 		XFreePixmap(ps->dpy, mw->bg_pixmap);
206 	if(mw->background)
207 		XRenderFreePicture(ps->dpy, mw->background);
208 
209 	mw->bg_pixmap = XCreatePixmap(ps->dpy, mw->window, mw->width, mw->height, mw->depth);
210 	pa.repeat = True;
211 	mw->background = XRenderCreatePicture(ps->dpy, mw->bg_pixmap, mw->format, CPRepeat, &pa);
212 
213 	if (ps->o.background) {
214 		XRenderComposite(ps->dpy, PictOpSrc, ps->o.background->pict, None, mw->background, mw->x, mw->y, 0, 0, 0, 0, mw->width, mw->height);
215 	}
216 	else if (!root) {
217 		static const XRenderColor black = { 0, 0, 0, 0xffff};
218 		XRenderFillRectangle(ps->dpy, PictOpSrc, mw->background, &black, 0, 0, mw->width, mw->height);
219 	}
220 	else {
221 		Picture from = XRenderCreatePicture(ps->dpy, root, XRenderFindVisualFormat(ps->dpy, DefaultVisual(ps->dpy, ps->screen)), 0, 0);
222 		XRenderComposite(ps->dpy, PictOpSrc, from, None, mw->background, mw->x, mw->y, 0, 0, 0, 0, mw->width, mw->height);
223 		XRenderFreePicture(ps->dpy, from);
224 	}
225 
226 	XSetWindowBackgroundPixmap(ps->dpy, mw->window, mw->bg_pixmap);
227 	XClearWindow(ps->dpy, mw->window);
228 }
229 
230 void
mainwin_update(MainWin * mw)231 mainwin_update(MainWin *mw)
232 {
233 	session_t * const ps = mw->ps;
234 
235 #ifdef CFG_XINERAMA
236 	XineramaScreenInfo *iter;
237 	int i;
238 	Window dummy_w;
239 	int root_x, root_y, dummy_i;
240 	unsigned int dummy_u;
241 
242 	if(! mw->xin_info || ! mw->xin_screens)
243 	{
244 		mainwin_update_background(mw);
245 		return;
246 	}
247 
248 # ifdef DEBUG
249 	fprintf(stderr, "--> querying pointer... ");
250 # endif /* DEBUG */
251 	XQueryPointer(ps->dpy, ps->root, &dummy_w, &dummy_w, &root_x, &root_y, &dummy_i, &dummy_i, &dummy_u);
252 # ifdef DEBUG
253 	fprintf(stderr, "+%i+%i\n", root_x, root_y);
254 
255 	fprintf(stderr, "--> figuring out which screen we're on... ");
256 # endif /* DEBUG */
257 	iter = mw->xin_info;
258 	for(i = 0; i < mw->xin_screens; ++i)
259 	{
260 		if(root_x >= iter->x_org && root_x < iter->x_org + iter->width &&
261 		   root_y >= iter->y_org && root_y < iter->y_org + iter->height)
262 		{
263 # ifdef DEBUG
264 			fprintf(stderr, "screen %i %ix%i+%i+%i\n", iter->screen_number, iter->width, iter->height, iter->x_org, iter->y_org);
265 # endif /* DEBUG */
266 			break;
267 		}
268 		iter++;
269 	}
270 	if(i == mw->xin_screens)
271 	{
272 # ifdef DEBUG
273 		fprintf(stderr, "unknown\n");
274 # endif /* DEBUG */
275 		return;
276 	}
277 	mw->x = iter->x_org;
278 	mw->y = iter->y_org;
279 	mw->width = iter->width;
280 	mw->height = iter->height;
281 	XMoveResizeWindow(ps->dpy, mw->window, iter->x_org, iter->y_org, mw->width, mw->height);
282 	mw->xin_active = iter;
283 #endif /* CFG_XINERAMA */
284 	mainwin_update_background(mw);
285 }
286 
287 void
mainwin_map(MainWin * mw)288 mainwin_map(MainWin *mw) {
289 	session_t *ps = mw->ps;
290 
291 	wm_set_fullscreen(ps, mw->window, mw->x, mw->y, mw->width, mw->height);
292 	mw->pressed = NULL;
293 	mw->pressed_key = mw->pressed_mouse = false;
294 	XMapWindow(ps->dpy, mw->window);
295 	XRaiseWindow(ps->dpy, mw->window);
296 
297 	// Might because of WM reparent, XSetInputFocus() doesn't work here
298 	// Focus is already on mini window?
299 	XSetInputFocus(ps->dpy, mw->window, RevertToParent, CurrentTime);
300 	/* {
301 		int ret = XGrabKeyboard(ps->dpy, mw->window, True, GrabModeAsync,
302 				GrabModeAsync, CurrentTime);
303 		if (Success != ret)
304 			printfef("(): Failed to grab keyboard (%d), troubles ahead.", ret);
305 	} */
306 }
307 
308 void
mainwin_unmap(MainWin * mw)309 mainwin_unmap(MainWin *mw)
310 {
311 	if(mw->tooltip)
312 		tooltip_unmap(mw->tooltip);
313 	if(mw->bg_pixmap)
314 	{
315 		XFreePixmap(mw->ps->dpy, mw->bg_pixmap);
316 		mw->bg_pixmap = None;
317 	}
318 	XUngrabKeyboard(mw->ps->dpy, CurrentTime);
319 	XUnmapWindow(mw->ps->dpy, mw->window);
320 }
321 
322 void
mainwin_destroy(MainWin * mw)323 mainwin_destroy(MainWin *mw) {
324 	session_t *ps = mw->ps;
325 
326 	// Free all clients associated with this main window
327 	dlist_free_with_func(mw->clients, (dlist_free_func) clientwin_destroy);
328 
329 	if(mw->tooltip)
330 		tooltip_destroy(mw->tooltip);
331 
332 	if(mw->background != None)
333 		XRenderFreePicture(ps->dpy, mw->background);
334 
335 	if(mw->bg_pixmap != None)
336 		XFreePixmap(ps->dpy, mw->bg_pixmap);
337 
338 	if(mw->normalPicture != None)
339 		XRenderFreePicture(ps->dpy, mw->normalPicture);
340 
341 	if(mw->highlightPicture != None)
342 		XRenderFreePicture(ps->dpy, mw->highlightPicture);
343 
344 	if(mw->normalPixmap != None)
345 		XFreePixmap(ps->dpy, mw->normalPixmap);
346 
347 	if(mw->highlightPixmap != None)
348 		XFreePixmap(ps->dpy, mw->highlightPixmap);
349 
350 	XDestroyWindow(ps->dpy, mw->window);
351 
352 #ifdef CFG_XINERAMA
353 	if(mw->xin_info)
354 		XFree(mw->xin_info);
355 #endif /* CFG_XINERAMA */
356 
357 	free(mw);
358 }
359 
360 void
mainwin_transform(MainWin * mw,float f)361 mainwin_transform(MainWin *mw, float f)
362 {
363 	mw->transform.matrix[0][0] = XDoubleToFixed(1.0 / f);
364 	mw->transform.matrix[0][1] = 0.0;
365 	mw->transform.matrix[0][2] = 0.0;
366 	mw->transform.matrix[1][0] = 0.0;
367 	mw->transform.matrix[1][1] = XDoubleToFixed(1.0 / f);
368 	mw->transform.matrix[1][2] = 0.0;
369 	mw->transform.matrix[2][0] = 0.0;
370 	mw->transform.matrix[2][1] = 0.0;
371 	mw->transform.matrix[2][2] = XDoubleToFixed(1.0);
372 }
373 
374 int
mainwin_handle(MainWin * mw,XEvent * ev)375 mainwin_handle(MainWin *mw, XEvent *ev) {
376 	session_t *ps = mw->ps;
377 
378 	switch(ev->type) {
379 		case EnterNotify:
380 			XSetInputFocus(ps->dpy, mw->window, RevertToParent, CurrentTime);
381 			break;
382 		case KeyPress:
383 			mw->pressed_key = true;
384 			break;
385 		case KeyRelease:
386 			if (mw->pressed_key)
387 				report_key_unbinded(ev);
388 			else
389 				report_key_ignored(ev);
390 			break;
391 		case ButtonPress:
392 			mw->pressed_mouse = true;
393 			break;
394 		case ButtonRelease:
395 			if (mw->pressed_mouse) {
396 				printfef("(): Detected mouse button release on main window, "
397 						"exiting.");
398 				return 1;
399 			}
400 			else
401 				printfef("(): ButtonRelease %u ignored.", ev->xbutton.button);
402 			break;
403 	}
404 
405 	return 0;
406 }
407