initial release
[skippy-xd] / mainwin.c
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 *
24 find_argb_visual (Display *dpy, int scr)
25 {
26     XVisualInfo         *xvi;
27     XVisualInfo         template;
28     int                 nvi;
29     int                 i;
30     XRenderPictFormat   *format;
31     Visual              *visual;
32     
33     template.screen = scr;
34     template.depth = 32;
35     template.class = TrueColor;
36     xvi = XGetVisualInfo (dpy, 
37                           VisualScreenMask |
38                           VisualDepthMask |
39                           VisualClassMask,
40                           &template,
41                           &nvi);
42     if (!xvi)
43         return 0;
44     visual = 0;
45     for (i = 0; i < nvi; i++)
46     {
47         format = XRenderFindVisualFormat (dpy, xvi[i].visual);
48         if (format->type == PictTypeDirect && format->direct.alphaMask)
49         {
50             visual = xvi[i].visual;
51             break;
52         }
53     }
54
55     XFree (xvi);
56     return visual;
57 }
58
59 MainWin *
60 mainwin_create(Display *dpy, dlist *config)
61 {
62         const char *tmp;
63         double tmp_d;
64         XColor exact_color;
65         XSetWindowAttributes wattr;
66         XWindowAttributes rootattr;
67         XRenderPictureAttributes pa;
68         XRenderColor clear;
69         int error_base;
70 #ifdef XINERAMA
71         int event_base;
72 #endif /* XINERAMA */
73         
74         MainWin *mw = (MainWin *)malloc(sizeof(MainWin));
75         
76         mw->dpy = dpy;
77         mw->screen = DefaultScreen(dpy);
78         mw->root = RootWindow(dpy, mw->screen);
79         mw->lazy_trans = (strcasecmp(config_get(config, "general", "lazyTrans", "false"), "true") == 0) ? True : False;
80         if(mw->lazy_trans)
81         {
82                 mw->depth  = 32;
83                 mw->visual = find_argb_visual(dpy, DefaultScreen(dpy));
84                 if(! mw->visual)
85                 {
86                         fprintf(stderr, "WARNING: Couldn't find argb visual, disabling lazy transparency.\n");
87                         mw->lazy_trans = False;
88                 }
89         }
90         if(! mw->lazy_trans)
91         {
92                 mw->depth = DefaultDepth(dpy, mw->screen);
93                 mw->visual = DefaultVisual(dpy, mw->screen);
94         }
95         mw->colormap = XCreateColormap(dpy, mw->root, mw->visual, AllocNone);
96         mw->bg_pixmap = None;
97         mw->background = None;
98         mw->format = XRenderFindVisualFormat(dpy, mw->visual);
99 #ifdef XINERAMA
100         mw->xin_info = mw->xin_active = 0;
101         mw->xin_screens = 0;
102 #endif /* XINERAMA */
103         
104         mw->pressed = mw->focus = 0;
105         mw->tooltip = 0;
106         mw->cod = 0;
107         mw->key_up = XKeysymToKeycode(dpy, XK_Up);
108         mw->key_down = XKeysymToKeycode(dpy, XK_Down);
109         mw->key_left = XKeysymToKeycode(dpy, XK_Left);
110         mw->key_right = XKeysymToKeycode(dpy, XK_Right);
111         mw->key_enter = XKeysymToKeycode(dpy, XK_Return);
112         mw->key_space = XKeysymToKeycode(dpy, XK_space);
113         mw->key_escape = XKeysymToKeycode(dpy, XK_Escape);
114         mw->key_q = XKeysymToKeycode(dpy, XK_q);
115         mw->key_f6 = XKeysymToKeycode(dpy, XK_F6);
116         mw->key_f7 = XKeysymToKeycode(dpy, XK_F7);
117         mw->key_f8 = XKeysymToKeycode(dpy, XK_F8);
118         mw->key_f5 = XKeysymToKeycode(dpy, XK_F5);
119         
120         XGetWindowAttributes(dpy, mw->root, &rootattr);
121         mw->x = mw->y = 0;
122         mw->width = rootattr.width;
123         mw->height = rootattr.height;
124
125         wattr.colormap = mw->colormap;
126         wattr.background_pixel = wattr.border_pixel = 0;
127         wattr.event_mask = VisibilityChangeMask |
128                            ButtonReleaseMask;
129         
130         mw->window = XCreateWindow(dpy, mw->root, 0, 0, mw->width, mw->height, 0,
131                                    mw->depth, InputOutput, mw->visual,
132                                    CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wattr);
133         if(mw->window == None) {
134                 free(mw);
135                 return 0;
136         }
137         
138 #ifdef XINERAMA
139 # ifdef DEBUG
140         fprintf(stderr, "--> checking for Xinerama extension... ");
141 # endif /* DEBUG */
142         if(XineramaQueryExtension(dpy, &event_base, &error_base))
143         {
144 # ifdef DEBUG
145             fprintf(stderr, "yes\n--> checking if Xinerama is enabled... ");
146 # endif /* DEBUG */
147             if(XineramaIsActive(dpy))
148             {
149 # ifdef DEBUG
150                 fprintf(stderr, "yes\n--> fetching Xinerama info... ");
151 # endif /* DEBUG */
152                 mw->xin_info = XineramaQueryScreens(mw->dpy, &mw->xin_screens);
153 # ifdef DEBUG           
154                 fprintf(stderr, "done (%i screens)\n", mw->xin_screens);
155 # endif /* DEBUG */
156             }
157 # ifdef DEBUG
158             else
159                 fprintf(stderr, "no\n");
160 # endif /* DEBUG */
161         }
162 # ifdef DEBUG
163         else
164             fprintf(stderr, "no\n");
165 # endif /* DEBUG */
166 #endif /* XINERAMA */
167         
168         if(! XDamageQueryExtension (dpy, &mw->damage_event_base, &error_base))
169         {
170                 fprintf(stderr, "FATAL: XDamage extension not found.\n");
171                 exit(1);
172         }
173         
174         if(! XCompositeQueryExtension(dpy, &event_base, &error_base))
175         {
176                 fprintf(stderr, "FATAL: XComposite extension not found.\n");
177                 exit(1);
178         }
179         
180         if(! XRenderQueryExtension(dpy, &event_base, &error_base))
181         {
182                 fprintf(stderr, "FATAL: XRender extension not found.\n");
183                 exit(1);
184         }
185         
186         if(! XFixesQueryExtension(dpy, &event_base, &error_base))
187         {
188                 fprintf(stderr, "FATAL: XFixes extension not found.\n");
189                 exit(1);
190         }
191         
192         XCompositeRedirectSubwindows (mw->dpy, mw->root, CompositeRedirectAutomatic);
193         
194         tmp_d = strtod(config_get(config, "general", "updateFreq", "10.0"), 0);
195         if(tmp_d != 0.0)
196                 mw->poll_time = (1.0 / tmp_d) * 1000.0;
197         else
198                 mw->poll_time = 0;
199         
200         tmp = config_get(config, "normal", "tint", "black");
201         if(! XParseColor(mw->dpy, mw->colormap, tmp, &exact_color))
202         {
203                 fprintf(stderr, "Couldn't look up color '%s', reverting to black", tmp);
204                 mw->normalTint.red = mw->normalTint.green = mw->normalTint.blue = 0;
205         }
206         else
207         {
208                 mw->normalTint.red = exact_color.red;
209                 mw->normalTint.green = exact_color.green;
210                 mw->normalTint.blue = exact_color.blue;
211         }
212         mw->normalTint.alpha = MAX(0, MIN(strtol(config_get(config, "normal", "tintOpacity", "0"), 0, 0) * 256, 65535));
213         
214         tmp = config_get(config, "highlight", "tint", "#101020");
215         if(! XParseColor(mw->dpy, mw->colormap, tmp, &exact_color))
216         {
217                 fprintf(stderr, "Couldn't look up color '%s', reverting to #101020", tmp);
218                 mw->highlightTint.red = mw->highlightTint.green = 0x10;
219                 mw->highlightTint.blue = 0x20;
220         }
221         else
222         {
223                 mw->highlightTint.red = exact_color.red;
224                 mw->highlightTint.green = exact_color.green;
225                 mw->highlightTint.blue = exact_color.blue;
226         }
227         mw->highlightTint.alpha = MAX(0, MIN(strtol(config_get(config, "highlight", "tintOpacity", "64"), 0, 0) * 256, 65535));
228         
229         pa.repeat = True;
230         clear.alpha = MAX(0, MIN(strtol(config_get(config, "normal", "opacity", "200"), 0, 10) * 256, 65535));
231         mw->normalPixmap = XCreatePixmap(mw->dpy, mw->window, 1, 1, 8);
232         mw->normalPicture = XRenderCreatePicture(mw->dpy, mw->normalPixmap, XRenderFindStandardFormat(mw->dpy, PictStandardA8), CPRepeat, &pa);
233         XRenderFillRectangle(mw->dpy, PictOpSrc, mw->normalPicture, &clear, 0, 0, 1, 1);
234         
235         clear.alpha = MAX(0, MIN(strtol(config_get(config, "highlight", "opacity", "255"), 0, 10) * 256, 65535));
236         mw->highlightPixmap = XCreatePixmap(mw->dpy, mw->window, 1, 1, 8);
237         mw->highlightPicture = XRenderCreatePicture(mw->dpy, mw->highlightPixmap, XRenderFindStandardFormat(mw->dpy, PictStandardA8), CPRepeat, &pa);
238         XRenderFillRectangle(mw->dpy, PictOpSrc, mw->highlightPicture, &clear, 0, 0, 1, 1);
239         
240         tmp = config_get(config, "general", "distance", "50");
241         mw->distance = MAX(1, strtol(tmp, 0, 10));
242         
243         if(! strcasecmp(config_get(config, "tooltip", "show", "true"), "true"))
244                 mw->tooltip = tooltip_create(mw, config);
245         
246         return mw;
247 }
248
249 int
250 load_image (MainWin *mw, Bool rotate, Imlib_Image rootimg)
251 {
252   Imlib_Image buffer;
253
254 if (rotate)
255         buffer = mw->img_p;
256 else
257         buffer = mw->img_l;
258
259   if (!buffer)
260     return 0;
261
262   imlib_context_set_image (buffer);
263
264   imlib_context_set_image (rootimg);
265   imlib_blend_image_onto_image (buffer, 0, 0, 0,  imlib_image_get_width (), imlib_image_get_height (),
266                                     0, 0, mw->width, mw->height);
267
268   imlib_context_set_image (buffer);
269   imlib_free_image ();
270
271   imlib_context_set_image (rootimg);
272
273   return 1;
274 }
275
276
277
278 void
279 mainwin_update_background(MainWin *mw)
280 {
281 /*      Pixmap root = wm_get_root_pmap(mw->dpy);
282         XRenderColor black = { 0, 0, 0, 65535};*/
283         XRenderPictureAttributes pa;
284         Imlib_Context *context;
285         Imlib_Image image;
286         Bool rotate;
287
288         if(mw->width == 800)
289                 rotate = False;
290         else
291                 rotate = True;
292         
293         if(mw->bg_pixmap)
294                 XFreePixmap(mw->dpy, mw->bg_pixmap);
295         if(mw->background)
296                 XRenderFreePicture(mw->dpy, mw->background);
297         
298         mw->bg_pixmap = XCreatePixmap(mw->dpy, mw->window, mw->width, mw->height, mw->depth);
299         pa.repeat = True;
300         mw->background = XRenderCreatePicture(mw->dpy, mw->bg_pixmap, mw->format, CPRepeat, &pa);
301
302
303       context = imlib_context_new ();
304       imlib_context_push (context);
305       imlib_context_set_display (mw->dpy);
306       imlib_context_set_visual (mw->visual);
307       imlib_context_set_colormap (mw->colormap);
308       imlib_context_set_drawable (mw->bg_pixmap);
309       imlib_context_set_color_range (imlib_create_color_range ());
310
311       image = imlib_create_image (mw->width, mw->height);
312       imlib_context_set_image (image);
313
314       imlib_context_set_color (0, 0, 0, 255);
315       imlib_image_fill_rectangle (0, 0, mw->width, mw->height);
316
317       imlib_context_set_dither (1);
318       imlib_context_set_blend (1);
319
320       if (load_image (mw, rotate, image) == 0)
321       {
322         fprintf (stderr, "Bad image\n");
323         exit(1);
324       }
325
326       imlib_render_image_on_drawable (0, 0);
327       imlib_free_image ();
328       imlib_free_color_range ();
329
330 /*      if(root == None)
331                 XRenderFillRectangle(mw->dpy, PictOpSrc, mw->background, &black, 0, 0, mw->width, mw->height);
332         else
333         {
334                 Picture from = XRenderCreatePicture(mw->dpy, root, XRenderFindVisualFormat(mw->dpy, DefaultVisual(mw->dpy, mw->screen)), 0, 0);
335                 XRenderComposite(mw->dpy, PictOpSrc, from, None, mw->background, mw->x, mw->y, 0, 0, 0, 0, mw->width, mw->height);
336                 XRenderFreePicture(mw->dpy, from);
337         }*/
338         
339         XSetWindowBackgroundPixmap(mw->dpy, mw->window, mw->bg_pixmap);
340         XClearWindow(mw->dpy, mw->window);
341 }
342
343 void
344 mainwin_update(MainWin *mw)
345 {
346 #ifdef XINERAMA
347         XineramaScreenInfo *iter;
348         int i;
349         Window dummy_w;
350         int root_x, root_y, dummy_i;
351         unsigned int dummy_u;
352         
353         if(! mw->xin_info || ! mw->xin_screens)
354         {
355                 mainwin_update_background(mw);
356                 return;
357         }
358         
359 # ifdef DEBUG
360         fprintf(stderr, "--> querying pointer... ");
361 # endif /* DEBUG */
362         XQueryPointer(mw->dpy, mw->root, &dummy_w, &dummy_w, &root_x, &root_y, &dummy_i, &dummy_i, &dummy_u);
363 # ifdef DEBUG   
364         fprintf(stderr, "+%i+%i\n", root_x, root_y);
365         
366         fprintf(stderr, "--> figuring out which screen we're on... ");
367 # endif /* DEBUG */
368         iter = mw->xin_info;
369         for(i = 0; i < mw->xin_screens; ++i)
370         {
371                 if(root_x >= iter->x_org && root_x < iter->x_org + iter->width &&
372                    root_y >= iter->y_org && root_y < iter->y_org + iter->height)
373                 {
374 # ifdef DEBUG
375                         fprintf(stderr, "screen %i %ix%i+%i+%i\n", iter->screen_number, iter->width, iter->height, iter->x_org, iter->y_org);
376 # endif /* DEBUG */
377                         break;
378                 }
379                 iter++;
380         }
381         if(i == mw->xin_screens)
382         {
383 # ifdef DEBUG 
384                 fprintf(stderr, "unknown\n");
385 # endif /* DEBUG */
386                 return;
387         }
388         mw->x = iter->x_org;
389         mw->y = iter->y_org;
390         mw->width = iter->width;
391         mw->height = iter->height;
392         XMoveResizeWindow(mw->dpy, mw->window, iter->x_org, iter->y_org, mw->width, mw->height);
393         mw->xin_active = iter;
394 #endif /* XINERAMA */
395         mainwin_update_background(mw);
396 }
397
398 void
399 mainwin_map(MainWin *mw)
400 {
401         wm_set_fullscreen(mw->dpy, mw->window, mw->x, mw->y, mw->width, mw->height);
402         mw->pressed = 0;
403         XMapWindow(mw->dpy, mw->window);
404         XRaiseWindow(mw->dpy, mw->window);
405 }
406
407 void
408 mainwin_unmap(MainWin *mw)
409 {
410         if(mw->tooltip)
411                 tooltip_unmap(mw->tooltip);
412         if(mw->bg_pixmap)
413         {
414                 XFreePixmap(mw->dpy, mw->bg_pixmap);
415                 mw->bg_pixmap = None;
416         }
417         XUnmapWindow(mw->dpy, mw->window);
418 }
419
420 void
421 mainwin_destroy(MainWin *mw)
422 {
423         if(mw->tooltip)
424                 tooltip_destroy(mw->tooltip);
425         
426         if(mw->background != None)
427                 XRenderFreePicture(mw->dpy, mw->background);
428         
429         if(mw->bg_pixmap != None)
430                 XFreePixmap(mw->dpy, mw->bg_pixmap);
431         
432         if(mw->normalPicture != None)
433                 XRenderFreePicture(mw->dpy, mw->normalPicture);
434         
435         if(mw->highlightPicture != None)
436                 XRenderFreePicture(mw->dpy, mw->highlightPicture);
437         
438         if(mw->normalPixmap != None)
439                 XFreePixmap(mw->dpy, mw->normalPixmap);
440         
441         if(mw->highlightPixmap != None)
442                 XFreePixmap(mw->dpy, mw->highlightPixmap);
443         
444         XDestroyWindow(mw->dpy, mw->window);
445         
446 #ifdef XINERAMA
447         if(mw->xin_info)
448                 XFree(mw->xin_info);
449 #endif /* XINERAMA */
450         
451         free(mw);
452 }
453
454 void
455 mainwin_transform(MainWin *mw, float f)
456 {
457         mw->transform.matrix[0][0] = XDoubleToFixed(1.0 / f);
458         mw->transform.matrix[0][1] = 0.0;
459         mw->transform.matrix[0][2] = 0.0;
460         mw->transform.matrix[1][0] = 0.0;
461         mw->transform.matrix[1][1] = XDoubleToFixed(1.0 / f);
462         mw->transform.matrix[1][2] = 0.0;
463         mw->transform.matrix[2][0] = 0.0;
464         mw->transform.matrix[2][1] = 0.0;
465         mw->transform.matrix[2][2] = XDoubleToFixed(1.0);
466 }
467
468 int
469 mainwin_handle(MainWin *mw, XEvent *ev)
470 {
471         switch(ev->type)
472         {
473         case KeyPress:
474                 if(ev->xkey.keycode == XKeysymToKeycode(mw->dpy, XK_q))
475                         return 2;
476                 break;
477         case ButtonRelease:
478                 return 3;
479                 break;
480         case VisibilityNotify:
481                 if(ev->xvisibility.state && mw->focus)
482                 {
483                         XSetInputFocus(mw->dpy, mw->focus->mini.window, RevertToParent, CurrentTime);
484                         mw->focus = 0;
485                 }
486                 break;
487         default:
488                 ;
489         }
490         return 0;
491 }