083197c8a7bdab6fcb18a589d4e4eacfd6eef2d2
[skippy-xd] / skippy.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 const char *f7prog;
23 static int DIE_NOW = 0;
24
25 static int
26 time_in_millis (void)
27 {
28     struct timeval  tp;
29
30     gettimeofday (&tp, 0);
31     return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
32 }
33
34 static dlist *
35 update_clients(MainWin *mw, dlist *clients, Bool *touched)
36 {
37         dlist *stack, *iter;
38         
39         stack = dlist_first(wm_get_stack(mw->dpy));
40         iter = clients = dlist_first(clients);
41         
42         if(touched)
43                 *touched = False;
44         
45         /* Terminate clients that are no longer managed */
46         while(iter)
47         {
48                 ClientWin *cw = (ClientWin *)iter->data;
49                 if(! dlist_find_data(stack, (void *)cw->client.window))
50                 {
51                         dlist *tmp = iter->next;
52                         clientwin_destroy((ClientWin *)iter->data, True);
53                         clients = dlist_remove(iter);
54                         iter = tmp;
55                         if(touched)
56                                 *touched = True;
57                         continue;
58                 }
59                 clientwin_update(cw);
60                 iter = iter->next;
61         }
62         
63         /* Add new clients */
64         for(iter = dlist_first(stack); iter; iter = iter->next)
65         {
66                 ClientWin *cw = (ClientWin*)dlist_find(clients, clientwin_cmp_func, iter->data);
67                 if(! cw && (Window)iter->data != mw->window)
68                 {
69                         cw = clientwin_create(mw, (Window)iter->data);
70                         if(! cw)
71                                 continue;
72                         clients = dlist_add(clients, cw);
73                         clientwin_update(cw);
74                         if(touched)
75                                 *touched = True;
76                 }
77         }
78         
79         dlist_free(stack);
80         
81         return clients;
82 }
83
84 static dlist *
85 do_layout(MainWin *mw, dlist *clients, Window focus, Window leader)
86 {
87         CARD32 desktop = wm_get_current_desktop(mw->dpy);
88         unsigned int width, height;
89         float factor;
90         int xoff, yoff;
91         dlist *iter, *tmp;
92         
93         /* Update the client table, pick the ones we want and sort them */
94         clients = update_clients(mw, clients, 0);
95         
96         if(mw->cod)
97                 dlist_free(mw->cod);
98         
99         tmp = dlist_first(dlist_find_all(clients, (dlist_match_func)clientwin_validate_func, &desktop));
100         if(leader != None)
101         {
102                 mw->cod = dlist_first(dlist_find_all(tmp, clientwin_check_group_leader_func, (void*)&leader));
103                 dlist_free(tmp);
104         } else
105                 mw->cod = tmp;
106         
107         if(! mw->cod)
108                 return clients;
109         
110         dlist_sort(mw->cod, clientwin_sort_func, 0);
111         
112         /* Move the mini windows around */
113         layout_run(mw, mw->cod, &width, &height);
114         factor = (float)(mw->width - 100) / width;
115         if(factor * height > mw->height - 100)
116                 factor = (float)(mw->height - 100) / height;
117         
118         xoff = (mw->width - (float)width * factor) / 2;
119         yoff = (mw->height - (float)height * factor) / 2;
120         mainwin_transform(mw, factor);
121         for(iter = mw->cod; iter; iter = iter->next)
122                 clientwin_move((ClientWin*)iter->data, factor, xoff, yoff);
123         
124         /* Get the currently focused window and select which mini-window to focus */
125         iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)focus);
126         if(! iter)
127                 iter = mw->cod;
128         mw->focus = (ClientWin*)iter->data;
129         mw->focus->focused = 1;
130         
131         /* Map the client windows */
132         for(iter = mw->cod; iter; iter = iter->next)
133                 clientwin_map((ClientWin*)iter->data);
134         XWarpPointer(mw->dpy, None, mw->focus->mini.window, 0, 0, 0, 0, mw->focus->mini.width / 2, mw->focus->mini.height / 2);
135         
136         return clients;
137 }
138
139 static dlist *
140 skippy_run(MainWin *mw, dlist *clients, Window focus, Window leader, Bool all_xin)
141 {
142         XEvent ev;
143         int die = 0;
144         Bool refocus = False, refroot = False ;
145         int last_rendered;
146         XWindowAttributes rootattr;
147         
148         /* Update the main window's geometry (and Xinerama info if applicable) */
149         mainwin_update(mw);
150 #ifdef XINERAMA
151         if(all_xin)
152                 mw->xin_active = 0;
153 #else /* ! XINERAMA */
154         if(all_xin);
155 #endif /* XINERAMA */
156         
157         /* Map the main window and run our event loop */
158         if(mw->lazy_trans)
159         {
160                 mainwin_map(mw);
161                 XFlush(mw->dpy);
162         }
163
164         XGetWindowAttributes(mw->dpy, mw->root, &rootattr);
165         if(mw->width != rootattr.width)
166         {
167         /*printf("rotated\n");*/
168         mw->width = rootattr.width;
169         mw->height = rootattr.height;
170         mainwin_update_background(mw);
171         }
172
173         clients = do_layout(mw, clients, focus, leader);
174
175
176         if(! mw->cod)
177                 return clients;
178
179         
180         /* Map the main window and run our event loop */
181         if(! mw->lazy_trans)
182                 mainwin_map(mw);
183         
184         last_rendered = time_in_millis();
185         while(! die) {
186             int i, j, now, timeout, mh;
187             int move_x = -1, move_y = -1;
188             struct pollfd r_fd;
189             
190             XFlush(mw->dpy);
191             
192             r_fd.fd = ConnectionNumber(mw->dpy);
193             r_fd.events = POLLIN;
194             if(mw->poll_time > 0)
195                 timeout = MAX(0, mw->poll_time + last_rendered - time_in_millis());
196             else
197                 timeout = -1;
198             i = poll(&r_fd, 1, timeout);
199             
200             now = time_in_millis();
201             if(now >= last_rendered + mw->poll_time)
202             {
203                 REDUCE(if( ((ClientWin*)iter->data)->damaged ) clientwin_repair(iter->data), mw->cod);
204                 last_rendered = now;
205             }
206             
207             i = XPending(mw->dpy);
208             for(j = 0; j < i; ++j)
209             {
210                 XNextEvent(mw->dpy, &ev);
211                 if (ev.type == MotionNotify)
212                 {
213                         move_x = ev.xmotion.x_root;
214                         move_y = ev.xmotion.y_root;
215                 }
216                 else if(ev.type == DestroyNotify || ev.type == UnmapNotify) {
217                         dlist *iter = dlist_find(clients, clientwin_cmp_func, (void *)ev.xany.window);
218                         if(iter)
219                         {
220                                 ClientWin *cw = (ClientWin *)iter->data;
221                                 clients = dlist_first(dlist_remove(iter));
222                                 iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)ev.xany.window);
223                                 if(iter)
224                                         mw->cod = dlist_first(dlist_remove(iter));
225                                 clientwin_destroy(cw, True);
226                                 if(! mw->cod)
227                                 {
228                                         die = 1;
229                                         break;
230                                 }
231                         }
232                 }
233                 else if (mw->poll_time >= 0 && ev.type == mw->damage_event_base + XDamageNotify)
234                 {
235                         XDamageNotifyEvent *d_ev = (XDamageNotifyEvent *)&ev;
236                         dlist *iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)d_ev->drawable);
237                         if(iter)
238                         {
239                                 if(mw->poll_time == 0)
240                                         clientwin_repair((ClientWin *)iter->data);
241                                 else
242                                         ((ClientWin *)iter->data)->damaged = True;
243                         }
244                                 
245                 }
246                 else if(ev.type == KeyRelease && (ev.xkey.keycode == mw->key_f6 || ev.xkey.keycode == mw->key_escape))
247                 {
248                         refocus = True;
249                         die = 1;
250                         break;
251                 }
252                 else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_f7)
253                 {
254                         system(f7prog);
255                         refocus = True;
256                         die = 1;
257                         break;
258                 }
259                 else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_q)
260                 {
261                         DIE_NOW = 1;
262                         die = 1;
263                         break;
264                 }
265                 else if(ev.type == KeyPress && ev.xkey.keycode == mw->key_f5 )
266                 {
267                         refroot = True;
268                         die = 1;
269                         break;
270                 }
271                 else if(ev.xany.window == mw->window)
272                         mh = mainwin_handle(mw, &ev);
273                         if(mh == 0 || mh == 1 || mh == 2){
274                                 die = mh;
275                         }else if(mh == 3){
276                                 die = 1;
277                                 refroot = True;
278                         }               
279 /*              else if(ev.type == PropertyNotify)
280                 {
281                         if(ev.xproperty.atom == ESETROOT_PMAP_ID || ev.xproperty.atom == _XROOTPMAP_ID)
282                         {
283                                 mainwin_update_background(mw);
284                                 REDUCE(clientwin_render((ClientWin *)iter->data), mw->cod);
285                         }
286
287                 }*/
288                 else if(mw->tooltip && ev.xany.window == mw->tooltip->window)
289                         tooltip_handle(mw->tooltip, &ev);
290                 else
291                 {
292                         dlist *iter;
293                         for(iter = mw->cod; iter; iter = iter->next)
294                         {
295                                 ClientWin *cw = (ClientWin *)iter->data;
296                                 if(cw->mini.window == ev.xany.window)
297                                 {
298                                         die = clientwin_handle(cw, &ev);
299                                         if(die)
300                                                 break;
301                                 }
302                         }
303                         if(die)
304                                 break;
305                 }
306             
307             }
308                 
309             if(mw->tooltip && move_x != -1)
310                 tooltip_move(mw->tooltip, move_x + 20, move_y + 20);
311             
312         }
313
314         /* Unmap the main window and clean up */
315         mainwin_unmap(mw);
316         XFlush(mw->dpy);
317         
318         REDUCE(clientwin_unmap((ClientWin*)iter->data), mw->cod);
319         dlist_free(mw->cod);
320         mw->cod = 0;
321         
322         if(die == 2)
323                 DIE_NOW = 1;
324         
325         if(refocus)
326                 XSetInputFocus(mw->dpy, focus, RevertToPointerRoot, CurrentTime);
327         
328         if(refroot)
329                 client_msg(mw->dpy, DefaultRootWindow(mw->dpy), "_NET_SHOWING_DESKTOP",
330                         1, 0, 0, 0, 0);
331
332         
333         return clients;
334 }
335
336 int
337 main(void)
338 {
339         dlist *clients = 0, *config = 0;
340         Display *dpy = XOpenDisplay(NULL);
341         MainWin *mw;
342         KeyCode keycode;
343         KeySym keysym;
344         const char *tmp, *homedir;
345         char cfgpath[8192];
346         Bool invertShift = False;
347         const char *img_l_file, *img_p_file;
348         Window focused = None;
349         int revert_to;
350         
351         if(! dpy) {
352                 fprintf(stderr, "FATAL: Couldn't connect to display.\n");
353                 return -1;
354         }
355         
356         wm_get_atoms(dpy);
357         
358         if(! wm_check(dpy)) {
359                 fprintf(stderr, "FATAL: WM not NETWM or GNOME WM Spec compliant.\n");
360                 return -1;
361         }
362         
363         homedir = getenv("HOME");
364         if(homedir) {
365                 snprintf(cfgpath, 8191, "%s/%s", homedir, ".skippy-xd.rc");
366                 config = config_load(cfgpath);
367         }
368         else
369                 fprintf(stderr, "WARNING: $HOME not set, not loading config.\n");
370         
371         wm_use_netwm_fullscreen(strcasecmp("true", config_get(config, "general", "useNETWMFullscreen", "true")) == 0);
372         wm_ignore_skip_taskbar(strcasecmp("true", config_get(config, "general", "ignoreSkipTaskbar", "false")) == 0);
373
374         XGetInputFocus(dpy, &focused, &revert_to);
375         
376         mw = mainwin_create(dpy, config);
377         if(! mw)
378         {
379                 fprintf(stderr, "FATAL: Couldn't create main window.\n");
380                 config_free(config);
381                 XCloseDisplay(mw->dpy);
382                 return -1;
383         }
384
385         client_msg(dpy, focused, "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0);
386         XSetInputFocus(dpy, focused, RevertToParent, CurrentTime);
387         
388         f7prog = config_get(config, "maemo", "f7prog", "/usr/bin/osso-xterm &");
389         mw->gquality = atoi(config_get(config, "maemo", "gQuality", "0"));
390         printf("Filter: %d\n", mw->gquality);
391
392         img_l_file = config_get(config, "maemo", "imgLandscape", "/usr/share/skippy-xd/skippy-bg-l.jpg");
393         printf("ImgL: %s\n",  img_l_file);
394         img_p_file = config_get(config, "maemo", "imgPortrait", "/usr/share/skippy-xd/skippy-bg-p.jpg");
395         printf("ImgP: %s\n",  img_p_file);
396
397         mw->img_l = imlib_load_image(img_l_file);
398         mw->img_p = imlib_load_image(img_p_file);
399
400         invertShift = strcasecmp("true", config_get(config, "xinerama", "showAll", "false")) == 0;
401         
402         tmp = config_get(config, "general", "keysym", "F11");
403         keysym = XStringToKeysym(tmp);
404         if(keysym == NoSymbol)
405         {
406                 fprintf(stderr, "FATAL: Couldn't look up keysym for '%s', bailing out.\n", tmp);
407                 config_free(config);
408                 XCloseDisplay(mw->dpy);
409                 return -1;
410         }
411         
412 /*      XSelectInput(mw->dpy, mw->root, ButtonReleaseMask | ButtonPressMask | PropertyChangeMask);*/
413
414
415 /*      XSelectInput(mw->dpy, mw->root, PropertyChangeMask);*/
416         XSelectInput(mw->dpy, mw->window, ButtonReleaseMask | ButtonPressMask );
417         
418         keycode = XKeysymToKeycode(mw->dpy, keysym);
419         XGrabKey(mw->dpy, keycode, AnyModifier, mw->root, False, GrabModeAsync, GrabModeAsync);
420         while(! DIE_NOW)
421         {
422                 XEvent ev;
423                 XNextEvent(mw->dpy, &ev);
424                 /*if(ev.type == PropertyNotify && (ev.xproperty.atom == ESETROOT_PMAP_ID || ev.xproperty.atom == _XROOTPMAP_ID))
425                         mainwin_update_background(mw);
426                 else */if(ev.type == KeyPress && ev.xkey.keycode == keycode)
427                 {
428                         Window leader = None, focused = wm_get_focused(mw->dpy);
429                         Bool shifted = (ev.xkey.state & ShiftMask) ? ! invertShift : invertShift;
430                 
431                         if(ev.xkey.state & Mod1Mask)
432                         {
433                                 if(focused != None)
434                                         leader = wm_get_group_leader(mw->dpy, focused);
435                                 if(! leader)
436                                         continue;
437                         }
438                         clients = skippy_run(mw, clients, focused, leader, shifted);
439                 }
440         }
441         
442         dlist_free_with_func(clients, (dlist_free_func)clientwin_destroy);
443         
444         XFlush(mw->dpy);
445         
446         mainwin_destroy(mw);
447         
448         XSync(dpy, True);
449         XCloseDisplay(dpy);
450         config_free(config);
451         
452         return 0;
453 }