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