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