initial release
[skippy-xd] / clientwin.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 #define INTERSECTS(x1, y1, w1, h1, x2, y2, w2, h2) \
23         (((x1 >= x2 && x1 < (x2 + w2)) || (x2 >= x1 && x2 < (x1 + w1))) && \
24          ((y1 >= y2 && y1 < (y2 + h2)) || (y2 >= y1 && y2 < (y1 + h1))))
25
26 int
27 clientwin_cmp_func(dlist *l, void *data)
28 {
29         return ((ClientWin*)l->data)->client.window == (Window)data;
30 }
31
32 int
33 clientwin_validate_func(dlist *l, void *data)
34 {
35         ClientWin *cw = (ClientWin *)l->data;
36         CARD32 desktop = (*(CARD32*)data),
37                 w_desktop = wm_get_window_desktop(cw->mainwin->dpy, cw->client.window);
38         
39 #ifdef XINERAMA
40         if(cw->mainwin->xin_active && ! INTERSECTS(cw->client.x, cw->client.y, cw->client.width, cw->client.height,
41                                                    cw->mainwin->xin_active->x_org, cw->mainwin->xin_active->y_org,
42                                                    cw->mainwin->xin_active->width, cw->mainwin->xin_active->height))
43                 return 0;
44 #endif
45         
46         return (w_desktop == (CARD32)-1 || desktop == w_desktop) &&
47                wm_validate_window(cw->mainwin->dpy, cw->client.window);
48 }
49
50 int
51 clientwin_check_group_leader_func(dlist *l, void *data)
52 {
53         ClientWin *cw = (ClientWin *)l->data;
54         return wm_get_group_leader(cw->mainwin->dpy, cw->client.window) == *((Window*)data);
55 }
56
57 int
58 clientwin_sort_func(dlist* a, dlist* b, void* data)
59 {
60         unsigned int pa = ((ClientWin*)a->data)->client.x * ((ClientWin*)a->data)->client.y,
61                      pb = ((ClientWin*)b->data)->client.x * ((ClientWin*)b->data)->client.y;
62         return (pa < pb) ? -1 : (pa == pb) ? 0 : 1;
63 }
64
65 ClientWin *
66 clientwin_create(MainWin *mw, Window client)
67 {
68         ClientWin *cw = (ClientWin *)malloc(sizeof(ClientWin));
69         XSetWindowAttributes sattr;
70         XWindowAttributes attr;
71         XRenderPictureAttributes pa;
72         
73         cw->mainwin = mw;
74         cw->pixmap = None;
75         cw->focused = 0;
76         cw->origin = cw->destination = None;
77         cw->damage = None;
78         cw->damaged = False;
79         /* cw->repair = None; */
80         
81         sattr.border_pixel = sattr.background_pixel = 0;
82         sattr.colormap = mw->colormap;
83         
84         sattr.event_mask = ButtonPressMask |
85                            ButtonReleaseMask |
86                            KeyReleaseMask |
87                            EnterWindowMask |
88                            LeaveWindowMask |
89                            PointerMotionMask |
90                            ExposureMask |
91                            FocusChangeMask;
92         
93         sattr.override_redirect = mw->lazy_trans;
94         
95         cw->client.window = client;
96         cw->mini.format = mw->format;
97         cw->mini.window = XCreateWindow(mw->dpy, mw->lazy_trans ? mw->root : mw->window, 0, 0, 1, 1, 0,
98                                         mw->depth, InputOutput, mw->visual,
99                                         CWColormap | CWBackPixel | CWBorderPixel | CWEventMask | CWOverrideRedirect, &sattr);
100         
101         if(cw->mini.window == None)
102         {
103                 free(cw);
104                 return 0;
105         }
106         
107         XGetWindowAttributes(mw->dpy, client, &attr);
108         cw->client.format = XRenderFindVisualFormat(mw->dpy, attr.visual);
109         
110         pa.subwindow_mode = IncludeInferiors;
111         cw->origin = XRenderCreatePicture (cw->mainwin->dpy, cw->client.window, cw->client.format, CPSubwindowMode, &pa);
112
113 /*      XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterBest, 0, 0); */
114         if(mw->gquality == 0)
115                 XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterFast, NULL, 0);
116         else if (mw->gquality == 1)
117                 XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterGood, NULL, 0);
118         else if (mw->gquality == 2)
119                 XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterBest, NULL, 0);
120         else if (mw->gquality == 3)
121                 XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterNearest, NULL, 0);
122         else if (mw->gquality == 4)
123                 XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterBilinear, NULL, 0);
124         else if (mw->gquality == 5)
125                 XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterConvolution, NULL, 0);
126
127         XSelectInput(cw->mainwin->dpy, cw->client.window, SubstructureNotifyMask | StructureNotifyMask);
128         
129         
130         return cw;
131 }
132
133 void
134 clientwin_update(ClientWin *cw)
135 {
136         Window tmpwin;
137         XWindowAttributes wattr;
138
139         XGetWindowAttributes(cw->mainwin->dpy, cw->client.window, &wattr);
140
141         cw->client.format = XRenderFindVisualFormat(cw->mainwin->dpy, wattr.visual);
142         XTranslateCoordinates(cw->mainwin->dpy, cw->client.window, wattr.root,
143                                       -wattr.border_width,
144 /*                                      -90,-90,*/
145                                       -wattr.border_width,
146                                       &cw->client.x, &cw->client.y, &tmpwin);
147
148         cw->client.width = wattr.width;
149         cw->client.height = wattr.height;
150         
151         cw->mini.x = cw->mini.y = 0;
152         cw->mini.width = cw->mini.height = 1;
153 }
154
155 void
156 clientwin_destroy(ClientWin *cw, Bool parentDestroyed)
157 {
158         if(! parentDestroyed)
159         {
160                 if(cw->origin != None)
161                         XRenderFreePicture(cw->mainwin->dpy, cw->origin);
162                 if(cw->damage != None)
163                         XDamageDestroy(cw->mainwin->dpy, cw->damage);
164         }
165         if(cw->destination != None)
166                 XRenderFreePicture(cw->mainwin->dpy, cw->destination);
167         if(cw->pixmap != None)
168                 XFreePixmap(cw->mainwin->dpy, cw->pixmap);
169         
170         XDestroyWindow(cw->mainwin->dpy, cw->mini.window);
171         
172         free(cw);
173 }
174
175 static void
176 clientwin_repaint(ClientWin *cw, XRectangle *rect)
177 {
178         XRenderColor *tint = cw->focused ? &cw->mainwin->highlightTint : &cw->mainwin->normalTint;
179         int s_x = (double)rect->x * cw->factor,
180             s_y = (double)rect->y * cw->factor,
181             s_w = (double)rect->width * cw->factor,
182             s_h = (double)rect->height * cw->factor;
183
184         
185         if(cw->mainwin->lazy_trans)
186         {
187                 XRenderComposite(cw->mainwin->dpy, PictOpSrc, cw->origin,
188                                  cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture,
189                                  cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h);
190         }
191         else
192         {
193                 XRenderComposite(cw->mainwin->dpy, PictOpSrc, cw->mainwin->background, None, cw->destination, cw->mini.x + s_x, cw->mini.y + s_y, 0, 0, s_x, s_y, s_w, s_h);
194                 XRenderComposite(cw->mainwin->dpy, PictOpOver, cw->origin,
195                                  cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture,
196                                  cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h);
197         }
198         
199         if(tint->alpha)
200                 XRenderFillRectangle(cw->mainwin->dpy, PictOpOver, cw->destination, tint, s_x, s_y, s_w, s_h);
201         
202         XClearArea(cw->mainwin->dpy, cw->mini.window, s_x, s_y, s_w, s_h, False);
203 }
204
205 void
206 clientwin_render(ClientWin *cw)
207 {
208         XRectangle rect;
209         rect.x = rect.y = 0;
210         rect.width = cw->client.width;
211         rect.height = cw->client.height;
212         clientwin_repaint(cw, &rect);
213 }
214
215 void
216 clientwin_repair(ClientWin *cw)
217 {
218         int nrects, i;
219         XRectangle *rects;
220         XserverRegion rgn = XFixesCreateRegion(cw->mainwin->dpy, 0, 0);
221         
222         XDamageSubtract(cw->mainwin->dpy, cw->damage, None, rgn);
223         
224         rects = XFixesFetchRegion(cw->mainwin->dpy, rgn, &nrects);
225         XFixesDestroyRegion(cw->mainwin->dpy, rgn);
226         
227         for(i = 0; i < nrects; i++)
228                 clientwin_repaint(cw, &rects[i]);
229         
230         if(rects)
231                 XFree(rects);
232         
233         cw->damaged = False;
234 }
235
236 void
237 clientwin_schedule_repair(ClientWin *cw, XRectangle *area)
238 {
239         cw->damaged = True;
240 }
241
242 void
243 clientwin_move(ClientWin *cw, float f, int x, int y)
244 {
245         /* int border = MAX(1, (double)DISTANCE(cw->mainwin) * f * 0.25); */
246         int border = 0;
247         XSetWindowBorderWidth(cw->mainwin->dpy, cw->mini.window, border);
248         
249         cw->factor = f;
250         cw->mini.x = x + (int)cw->x * f;
251         cw->mini.y = y + (int)cw->y * f;
252         if(cw->mainwin->lazy_trans)
253         {
254                 cw->mini.x += cw->mainwin->x;
255                 cw->mini.y += cw->mainwin->y;
256         }
257         /*if(cw->client.width < 800)
258                 cw->client.height -= 65;*/
259         cw->mini.width = MAX(1, (int)cw->client.width * f );
260         cw->mini.height = MAX(1, (int)cw->client.height * f );
261         XMoveResizeWindow(cw->mainwin->dpy, cw->mini.window, cw->mini.x - border, cw->mini.y - border, cw->mini.width, cw->mini.height);
262         
263         if(cw->pixmap)
264                 XFreePixmap(cw->mainwin->dpy, cw->pixmap);
265         
266         if(cw->destination)
267                 XRenderFreePicture(cw->mainwin->dpy, cw->destination);
268         
269         cw->pixmap = XCreatePixmap(cw->mainwin->dpy, cw->mini.window, cw->mini.width, cw->mini.height, cw->mainwin->depth);
270         XSetWindowBackgroundPixmap(cw->mainwin->dpy, cw->mini.window, cw->pixmap);
271         
272         cw->destination = XRenderCreatePicture(cw->mainwin->dpy, cw->pixmap, cw->mini.format, 0, 0);
273 }
274
275 void
276 clientwin_map(ClientWin *cw)
277 {
278         if(cw->damage)
279                 XDamageDestroy(cw->mainwin->dpy, cw->damage);
280         
281         cw->damage = XDamageCreate(cw->mainwin->dpy, cw->client.window, XDamageReportDeltaRectangles);
282         XRenderSetPictureTransform(cw->mainwin->dpy, cw->origin, &cw->mainwin->transform);
283         
284         clientwin_render(cw);
285         
286         XMapWindow(cw->mainwin->dpy, cw->mini.window);
287 }
288
289 void
290 clientwin_unmap(ClientWin *cw)
291 {
292         if(cw->damage)
293         {
294                 XDamageDestroy(cw->mainwin->dpy, cw->damage);
295                 cw->damage = None;
296         }
297         
298         if(cw->destination)
299         {
300                 XRenderFreePicture(cw->mainwin->dpy, cw->destination);
301                 cw->destination = None;
302         }
303         
304         if(cw->pixmap)
305         {
306                 XFreePixmap(cw->mainwin->dpy, cw->pixmap);
307                 cw->pixmap = None;
308         }
309         
310         XUnmapWindow(cw->mainwin->dpy, cw->mini.window);
311         XSetWindowBackgroundPixmap(cw->mainwin->dpy, cw->mini.window, None);
312         
313         cw->focused = 0;
314 }
315
316 int client_msg(Display *disp, Window win, char *msg, /* {{{ */
317         unsigned long data0, unsigned long data1,
318         unsigned long data2, unsigned long data3,
319         unsigned long data4) {
320     XEvent event;
321     long mask = SubstructureRedirectMask | SubstructureNotifyMask;
322
323     event.xclient.type = ClientMessage;
324     event.xclient.serial = 0;
325     event.xclient.send_event = True;
326     event.xclient.message_type = XInternAtom(disp, msg, False);
327     event.xclient.window = win;
328     event.xclient.format = 32;
329     event.xclient.data.l[0] = data0;
330     event.xclient.data.l[1] = data1;
331     event.xclient.data.l[2] = data2;
332     event.xclient.data.l[3] = data3;
333     event.xclient.data.l[4] = data4;
334
335     if (XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event)) {
336         return EXIT_SUCCESS;
337     }
338     else {
339         fprintf(stderr, "Cannot send %s event.\n", msg);
340         return EXIT_FAILURE;
341     }
342 }
343
344
345 static void
346 childwin_focus(ClientWin *cw)
347 {
348         XWarpPointer(cw->mainwin->dpy, None, cw->client.window, 0, 0, 0, 0, cw->client.width / 2, cw->client.height / 2);
349
350         client_msg(cw->mainwin->dpy, cw->client.window, "_NET_ACTIVE_WINDOW",
351                     0, 0, 0, 0, 0);
352         XMapRaised(cw->mainwin->dpy, cw->client.window);
353         /*XRaiseWindow(cw->mainwin->dpy, cw->client.window);*/
354         XSetInputFocus(cw->mainwin->dpy, cw->client.window, RevertToParent, CurrentTime);
355 }
356
357 int
358 clientwin_handle(ClientWin *cw, XEvent *ev)
359 {
360         if((ev->type == ButtonRelease && ev->xbutton.button == 1 && cw->mainwin->pressed == cw)) {
361                 /*if((ev->xbutton.x >= 0 && ev->xbutton.y >= 0 && ev->xbutton.x < cw->mini.width && ev->xbutton.y < cw->mini.height))*/
362                         childwin_focus(cw);
363                 cw->mainwin->pressed = 0;
364                 return 1;
365         } else if(ev->type == KeyRelease) {
366                 if(ev->xkey.keycode == cw->mainwin->key_up)
367                         focus_up(cw);
368                 else if(ev->xkey.keycode == cw->mainwin->key_down)
369                         focus_down(cw);
370                 else if(ev->xkey.keycode == cw->mainwin->key_left)
371                         focus_left(cw);
372                 else if(ev->xkey.keycode == cw->mainwin->key_right)
373                         focus_right(cw);
374                 else if(ev->xkey.keycode == cw->mainwin->key_f8)
375                        client_msg(cw->mainwin->dpy, cw->client.window, "_NET_CLOSE_WINDOW",
376                                 0, 0, 0, 0, 0);
377                         /*client_msg(cw->mainwin->dpy, cw->client.window, "_NET_WM_STATE",
378                                         1, XInternAtom(cw->mainwin->dpy, "_NET_WM_STATE_FULLSCREEN", False), 0, 0, 0);*/
379                 else if(ev->xkey.keycode == cw->mainwin->key_enter || ev->xkey.keycode == cw->mainwin->key_space) {
380                         childwin_focus(cw);
381                         return 1;
382                 }
383         } else if(ev->type == ButtonPress && ev->xbutton.button == 1) {
384                 cw->mainwin->pressed = cw;
385         } else if(ev->type == FocusIn) {
386                 cw->focused = 1;
387                 clientwin_render(cw);
388                 XFlush(cw->mainwin->dpy);
389         } else if(ev->type == FocusOut) {
390                 cw->focused = 0;
391                 clientwin_render(cw);
392                 XFlush(cw->mainwin->dpy);
393         } else if(ev->type == EnterNotify) {
394                 XSetInputFocus(cw->mainwin->dpy, cw->mini.window, RevertToNone, CurrentTime);
395                 if(cw->mainwin->tooltip)
396                 {
397                         int win_title_len = 0;
398                         FcChar8 *win_title = wm_get_window_title(cw->mainwin->dpy, cw->client.window, &win_title_len);
399                         if(win_title)
400                         {
401                                 tooltip_map(cw->mainwin->tooltip,
402                                             ev->xcrossing.x_root + 20, ev->xcrossing.y_root + 20,
403                                             win_title, win_title_len);
404                                 free(win_title);
405                         }
406                 }
407         } else if(ev->type == LeaveNotify) {
408                 if(cw->mainwin->tooltip)
409                         tooltip_unmap(cw->mainwin->tooltip);
410         }
411         return 0;
412 }