add shit that i made in the last week!
[mikachu/openbox.git] / tools / slit / slit.c
1 #include "render/render.h"
2 #include "render/theme.h"
3
4 #include <X11/Xlib.h>
5 #include <stdlib.h>
6 #include <glib.h>
7 #ifdef HAVE_SIGNAL_H
8 #  include <signal.h>
9 #endif
10 #ifdef HAVE_SYS_SELECT_H
11 #  include <sys/select.h>
12 #endif
13
14 #define TITLE_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
15                           ButtonMotionMask)
16 #define ROOT_EVENT_MASK (PropertyChangeMask | StructureNotifyMask | \
17                          SubstructureNotifyMask)
18 #define SLITAPP_EVENT_MASK (StructureNotifyMask)
19
20 Display *ob_display;
21 Window ob_root;
22 int ob_screen;
23
24 static struct Slit {
25     Window frame;
26     Window title;
27
28     /* user-requested position stuff */
29     int gravity;
30     int user_x, user_y;
31
32     /* actual position (when not auto-hidden) */
33     int x, y;
34     int w, h;
35
36     gboolean horz;
37
38     Appearance *a_frame;
39     Appearance *a_title;
40
41     GList *slit_apps;
42 } *slit;
43 static int nslits;
44
45 struct SlitApp {
46     Window icon_win;
47     Window win;
48     int x;
49     int y;
50     int w;
51     int h;
52 };
53
54 static Atom atom_atom;
55 static Atom atom_card;
56 static Atom atom_theme;
57 static Atom atom_type;
58 static Atom atom_type_dock;
59 static Atom atom_desktop;
60 static Atom atom_state;
61 static Atom atom_strut;
62
63 static gboolean quit = FALSE;
64 static gboolean reconfig = FALSE;
65
66 void slit_read_theme();
67 void slit_configure();
68 void event_handle(XEvent *e);
69 void slit_add_existing();
70 void slit_add_app(Window win);
71 void slit_remove_app(struct Slit *slit, struct SlitApp *app,gboolean reparent);
72
73 void sighandler(int signal)
74 {
75     if (signal == SIGUSR1) 
76         reconfig =TRUE;
77     else
78         quit = TRUE;
79 }
80
81 int xerrorhandler(Display *d, XErrorEvent *e)
82 {
83     char errtxt[128];
84     XGetErrorText(d, e->error_code, errtxt, 127);
85     g_error("X Error: %s", errtxt);
86     return 0;
87 }
88
89 int main()
90 {
91     int i;
92     guint desk = 0xffffffff;
93     XEvent e;
94     XSetWindowAttributes attrib;
95     struct sigaction action;
96     sigset_t sigset;
97     int xfd;
98     fd_set selset;
99
100     /* set up signal handler */
101     sigemptyset(&sigset);
102     action.sa_handler = sighandler;
103     action.sa_mask = sigset;
104     action.sa_flags = SA_NOCLDSTOP;
105     sigaction(SIGUSR1, &action, (struct sigaction *) NULL);
106     sigaction(SIGPIPE, &action, (struct sigaction *) NULL);
107     sigaction(SIGSEGV, &action, (struct sigaction *) NULL);
108     sigaction(SIGFPE, &action, (struct sigaction *) NULL);
109     sigaction(SIGTERM, &action, (struct sigaction *) NULL);
110     sigaction(SIGINT, &action, (struct sigaction *) NULL);
111     sigaction(SIGHUP, &action, (struct sigaction *) NULL);
112
113     ob_display = XOpenDisplay(NULL);
114     ob_screen = DefaultScreen(ob_display);
115     ob_root = RootWindow(ob_display, ob_screen);
116
117    XSetErrorHandler(xerrorhandler);
118
119     render_startup();
120     theme_startup();
121
122     atom_atom = XInternAtom(ob_display, "ATOM", False);
123     atom_card = XInternAtom(ob_display, "CARDINAL", False);
124     atom_theme = XInternAtom(ob_display, "_OPENBOX_THEME", False);
125     atom_type = XInternAtom(ob_display, "_NET_WM_WINDOW_TYPE", False);
126     atom_type_dock = XInternAtom(ob_display, "_NET_WM_WINDOW_TYPE_DOCK",False);
127     atom_desktop =XInternAtom(ob_display, "_NET_WM_DESKTOP", False);
128     atom_state = XInternAtom(ob_display, "WM_STATE", False);
129     atom_strut = XInternAtom(ob_display, "_NET_WM_STRUT", False);
130
131     nslits = 1;
132     slit = g_new0(struct Slit, nslits);
133
134     for (i = 0; i < nslits; ++i) {
135         slit[i].horz = TRUE;
136
137         slit[i].frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
138                                       render_depth, InputOutput, render_visual,
139                                       0, NULL);
140         attrib.event_mask = TITLE_EVENT_MASK;
141         slit[i].title = XCreateWindow(ob_display, slit[i].frame, 0, 0, 1, 1, 0,
142                                       render_depth, InputOutput, render_visual,
143                                       CWEventMask, &attrib);
144         XMapWindow(ob_display, slit[i].title);
145
146         XChangeProperty(ob_display, slit[i].frame, atom_type, atom_atom,
147                         32, PropModeReplace, (guchar*)&atom_type_dock, 1);
148
149         XChangeProperty(ob_display, slit[i].frame, atom_desktop, atom_card,
150                         32, PropModeReplace, (guchar*)&desk, 1);
151     }
152
153     slit_read_theme();
154
155     XSelectInput(ob_display, ob_root, ROOT_EVENT_MASK);
156
157     slit_add_existing();
158
159     xfd = ConnectionNumber(ob_display);
160     FD_ZERO(&selset);
161     FD_SET(xfd, &selset);
162     while (!quit) {
163         gboolean hadevent = FALSE;
164         while (XPending(ob_display)) {
165             XNextEvent(ob_display, &e);
166             event_handle(&e);
167             hadevent = TRUE;
168         }
169         if (!hadevent) {
170             if (reconfig)
171                 slit_read_theme();
172
173             if (!quit)
174                 select(xfd + 1, &selset, NULL, NULL, NULL);
175         }
176     }
177
178     for (i = 0; i < nslits; ++i) {
179         while (slit[i].slit_apps)
180             slit_remove_app(&slit[i], slit[i].slit_apps->data, TRUE);
181
182         XDestroyWindow(ob_display, slit[i].title);
183         XDestroyWindow(ob_display, slit[i].frame);
184
185         appearance_free(slit[i].a_frame);
186         appearance_free(slit[i].a_title);
187     }
188
189     theme_shutdown();
190     render_shutdown();
191     XCloseDisplay(ob_display);
192     return 0;
193 }
194
195 Window find_client(Window win)
196 {
197   Window r, *children;
198   unsigned int n, i;
199   Atom ret_type;
200   int ret_format;
201   unsigned long ret_items, ret_bytesleft;
202   unsigned long *prop_return;
203
204   XQueryTree(ob_display, win, &r, &r, &children, &n);
205   for (i = 0; i < n; ++i) {
206     Window w = find_client(children[i]);
207     if (w) return w;
208   }
209
210   /* try me */
211   XGetWindowProperty(ob_display, win, atom_state, 0, 1,
212                      False, atom_state, &ret_type, &ret_format,
213                      &ret_items, &ret_bytesleft,
214                      (unsigned char**) &prop_return); 
215   if (ret_type == None || ret_items < 1)
216     return None;
217   return win; /* found it! */
218 }
219
220 void event_handle(XEvent *e)
221 {
222     int i;
223     Window win;
224     static guint button = 0;
225     int sw, sh;
226     int xpos, ypos;
227     int x, y, g;
228
229     switch (e->type) {
230     case ButtonPress:
231         if (!button) {
232             button = e->xbutton.button;
233         }
234         break;
235     case ButtonRelease:
236         if (button == e->xbutton.button)
237             button = 0;
238         break;
239     case MotionNotify:
240         if (button == 1) {
241             for (i = 0; i < nslits; ++i)
242                 if (slit[i].title == e->xmotion.window) {
243                     /* pick a corner and move it */
244                     sw = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
245                     sh = HeightOfScreen(ScreenOfDisplay(ob_display,ob_screen));
246
247                     if (e->xmotion.x_root < sw / 3) /* left edge */
248                         xpos = 0;
249                     else if (e->xmotion.x_root < sw / 3 * 2) /* middle */
250                         xpos = 1;
251                     else /* right edge */
252                         xpos = 2;
253                     if (e->xmotion.y_root < sh / 3) /* top edge */
254                         ypos = 0;
255                     else if (e->xmotion.y_root < sh / 3 * 2) /* middle */
256                         ypos = 1;
257                     else /* bottom edge */
258                         ypos = 2;
259
260                     if (xpos == 1 && ypos == 1)
261                         return; /* cant go in middle middle */
262
263                     if (xpos == 0) {
264                         if (ypos == 0) {
265                             x = 0;
266                             y = 0;
267                             g = NorthWestGravity;
268                         } else if (ypos == 1) {
269                             x = 0;
270                             y = sh / 2;
271                             g = WestGravity;
272                         } else {
273                             x = 0;
274                             y = sh;
275                             g = SouthWestGravity;
276                         }
277                     } else if (xpos == 1) {
278                         if (ypos == 0) {
279                             x = sw / 2;
280                             y = 0;
281                             g = NorthGravity;
282                         } else {
283                             x = sw / 2;
284                             y = sh;
285                             g = SouthGravity;
286                         }
287                     } else {
288                         if (ypos == 0) {
289                             x = sw;
290                             y = 0;
291                             g = NorthEastGravity;
292                         } else if (ypos == 1) {
293                             x = sw;
294                             y = sh / 2;
295                             g = EastGravity;
296                         } else {
297                             x = sw;
298                             y = sh;
299                             g = SouthEastGravity;
300                         }
301                     }
302                     if (x != slit[i].x || y != slit[i].y ||
303                         g != slit[i].gravity) {
304                         slit[i].user_x = x;
305                         slit[i].user_y = y;
306                         slit[i].gravity = g;
307                         slit_configure();
308                     }
309                 }
310         }
311         break;
312     case PropertyNotify:
313         g_message("PropertyNotify on 0x%lx", e->xproperty.window);
314         if (e->xproperty.window == ob_root) {
315             if (e->xproperty.atom == atom_theme)
316                 slit_read_theme();
317         }
318         break;
319     case ConfigureNotify:
320         g_message("ConfigureNotify on 0x%lx", e->xconfigure.window);
321         if (e->xconfigure.window == ob_root) {
322             slit_configure();
323             return;
324         }
325
326         /* an owned slitapp? */
327         for (i = 0; i < nslits; ++i) {
328             GList *it;
329
330             for (it = slit[i].slit_apps; it; it = it->next) {
331                 struct SlitApp *app = it->data;
332                 if (e->xconfigure.window == app->icon_win) {
333                     if (app->w != e->xconfigure.width ||
334                         app->h != e->xconfigure.height) {
335                         g_message("w %d h %d w %d h %d",
336                                   app->w, e->xconfigure.width,
337                                   app->h, e->xconfigure.height);
338                         app->w = e->xconfigure.width;
339                         app->h = e->xconfigure.height;
340                         slit_configure();
341                     }
342                     return;
343                 }
344             }
345         }
346         break;
347     case MapNotify:
348         g_message("MapNotify on 0x%lx", e->xmap.window);
349
350         win = find_client(e->xmap.window);
351         if (!win) return;
352
353         for (i = 0; i < nslits; ++i)
354             if (win == slit[i].frame)
355                 return;
356
357         slit_add_app(win);
358         break;
359     case UnmapNotify:
360         g_message("UnmapNotify on 0x%lx", e->xunmap.window);
361         for (i = 0; i < nslits; ++i) {
362             GList *it;
363
364             for (it = slit[i].slit_apps; it; it = it->next) {
365                 struct SlitApp *app = it->data;
366                 if (e->xunmap.window == app->icon_win) {
367                     gboolean r;
368                     XEvent e;
369
370                     r = !XCheckTypedWindowEvent(ob_display, app->icon_win,
371                                                 DestroyNotify, &e);
372                     if (r) {
373                         if (XCheckTypedWindowEvent(ob_display, app->icon_win,
374                                                    ReparentNotify, &e)) {
375                             XPutBackEvent(ob_display, &e);
376                             r = FALSE;
377                         }
378                     }
379                     slit_remove_app(&slit[i], app, r);
380                     break;
381                 }
382             }
383         }
384         break;
385     case ReparentNotify:
386         g_message("ReparentNotify on 0x%lx", e->xdestroywindow.window);
387         for (i = 0; i < nslits; ++i) {
388             GList *it;
389
390             for (it = slit[i].slit_apps; it; it = it->next) {
391                 struct SlitApp *app = it->data;
392                 if (e->xdestroywindow.window == app->icon_win) {
393                     slit_remove_app(&slit[i], app, FALSE);
394                     break;
395                 }
396             }
397         }
398     case DestroyNotify:
399         g_message("DestroyNotify on 0x%lx", e->xdestroywindow.window);
400         for (i = 0; i < nslits; ++i) {
401             GList *it;
402
403             for (it = slit[i].slit_apps; it; it = it->next) {
404                 struct SlitApp *app = it->data;
405                 if (e->xdestroywindow.window == app->icon_win) {
406                     slit_remove_app(&slit[i], app, FALSE);
407                     break;
408                 }
409             }
410         }
411         break;
412     }
413 }
414
415 void slit_add_existing()
416 {
417     unsigned int i, nchild;
418     int j;
419     Window w, *children;
420     XWindowAttributes attrib;
421
422     XQueryTree(ob_display, ob_root, &w, &w, &children, &nchild);
423
424     for (i = 0; i < nchild; ++i) {
425         for (j = 0; j < nslits; ++j)
426             if (children[i] == slit[j].frame)
427                 continue;
428         if (children[i] == None)
429             continue;
430         if ((children[i] = find_client(children[i])) == None)
431             continue;
432         if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
433             if (attrib.override_redirect) continue;
434
435             slit_add_app(children[i]);
436         }
437     }
438     XFree(children);
439 }
440
441 void slit_add_app(Window win)
442 {
443     int i;
444     XWMHints *h;
445     XWindowAttributes attrib;
446     struct Slit *s;
447
448     s = &slit[0];
449
450     if ((h = XGetWMHints(ob_display, win))) {
451         if (h->flags & StateHint && h->initial_state == WithdrawnState) {
452             struct SlitApp *app = g_new(struct SlitApp, 1);
453
454             app->win = win;
455             app->icon_win = (h->flags & IconWindowHint) ?
456                 h->icon_window : win;
457
458             XFree(h);
459
460             for (i = 0; i < nslits; ++i) {
461                 GList *it;
462                 for (it = slit[i].slit_apps; it; it = it->next)
463                     if (app->icon_win ==
464                         ((struct SlitApp*)it->data)->icon_win)
465                         /* already managed! */
466                         return;
467             }
468
469             if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
470                 app->w = attrib.width;
471                 app->h = attrib.height;
472             } else {
473                 g_free(app);
474                 app = NULL;
475             }
476
477             if (app) {
478                 s->slit_apps = g_list_append(s->slit_apps, app);
479                 slit_configure();
480                 XReparentWindow(ob_display, app->icon_win,
481                                 s->frame, app->x, app->y);
482 /*                if (app->win != app->icon_win)
483                   XUnmapWindow(ob_display, app->win);*/
484                 XSync(ob_display, False);
485                 XSelectInput(ob_display, app->icon_win,
486                              SLITAPP_EVENT_MASK);
487             }
488             g_message("Managed: 0x%lx", app->icon_win);
489         } else
490             XFree(h);
491     }
492 }
493
494 void slit_remove_app(struct Slit *slit, struct SlitApp *app, gboolean reparent)
495 {
496
497     XSelectInput(ob_display, app->icon_win, NoEventMask);
498     XSync(ob_display, False);
499     if (reparent) {
500         g_message("reparenting");
501 /*        if (app->win != app->icon_win)
502           XMapWindow(ob_display, app->win);*/
503         XReparentWindow(ob_display, app->icon_win, ob_root, 0, 0);
504     }
505
506     g_free(app);
507     slit->slit_apps = g_list_remove(slit->slit_apps, app);
508     slit_configure();
509 }
510
511 void slit_read_theme()
512 {
513     XTextProperty prop;
514     int i;
515     char *theme = NULL;
516
517     if (XGetTextProperty(ob_display, ob_root, &prop,
518                          XInternAtom(ob_display, "_OPENBOX_THEME", False))) {
519         theme = theme_load((char*)prop.value);
520         XFree(prop.value);
521     } else
522         theme = theme_load(NULL);
523  
524     g_free(theme);
525     if (!theme) exit(EXIT_FAILURE);
526
527     for (i = 0; i < nslits; ++i) {
528         appearance_free(slit[i].a_frame);
529         appearance_free(slit[i].a_title);
530
531         slit[i].a_frame = appearance_copy(theme_a_unfocused_title);
532         slit[i].a_title = appearance_copy(theme_a_unfocused_title);
533
534         XSetWindowBorder(ob_display, slit[i].frame, theme_b_color->pixel);
535         XSetWindowBorderWidth(ob_display, slit[i].frame, theme_bwidth);
536         XSetWindowBorder(ob_display, slit[i].frame, BlackPixel(ob_display, ob_screen));
537         XSetWindowBorderWidth(ob_display, slit[i].frame, 30);
538     }
539
540     slit_configure();
541 }
542
543 void slit_configure()
544 {
545     int i;
546     int titleh;
547     GList *it;
548     int spot;
549
550     titleh = 4;
551
552     for (i = 0; i < nslits; ++i) {
553         if (slit[i].horz) {
554             slit[i].w = titleh;
555             slit[i].h = 0;
556         } else {
557             slit[i].w = 0;
558             slit[i].h = titleh;
559         }
560         spot = titleh;
561
562         for (it = slit[i].slit_apps; it; it = it->next) {
563             struct SlitApp *app = it->data;
564             if (slit[i].horz) {
565                 g_message("%d", spot);
566                 app->x = spot;
567                 app->y = 0;
568                 slit[i].w += app->w;
569                 slit[i].h = MAX(slit[i].h, app->h);
570                 spot += app->w;
571             } else {
572                 app->x = 0;
573                 app->y = spot;
574                 slit[i].w = MAX(slit[i].h, app->w);
575                 slit[i].h += app->h;
576                 spot += app->h;
577             }
578
579             XMoveWindow(ob_display, app->icon_win, app->x, app->y);
580         }
581
582         /* calculate position */
583         slit[i].x = slit[i].user_x;
584         slit[i].y = slit[i].user_y;
585
586         switch(slit[i].gravity) {
587         case NorthGravity:
588         case CenterGravity:
589         case SouthGravity:
590             slit[i].x -= slit[i].w / 2;
591             break;
592         case NorthEastGravity:
593         case EastGravity:
594         case SouthEastGravity:
595             slit[i].x -= slit[i].w;
596             break;
597         }
598         switch(slit[i].gravity) {
599         case WestGravity:
600         case CenterGravity:
601         case EastGravity:
602             slit[i].y -= slit[i].h / 2;
603             break;
604         case SouthWestGravity:
605         case SouthGravity:
606         case SouthEastGravity:
607             slit[i].y -= slit[i].h;
608             break;
609         }
610
611         if (slit[i].w > 0 && slit[i].h > 0) {
612             RECT_SET(slit[i].a_frame->area, 0, 0, slit[i].w, slit[i].h);
613             XMoveResizeWindow(ob_display, slit[i].frame,
614                               slit[i].x - theme_bwidth,
615                               slit[i].y - theme_bwidth,
616                               slit[i].w, slit[i].h);
617
618             if (slit[i].horz) {
619                 RECT_SET(slit[i].a_title->area, 0, 0, titleh, slit[i].h);
620                 XMoveResizeWindow(ob_display, slit[i].title, 0, 0,
621                                   titleh, slit[i].h);
622             } else {
623                 RECT_SET(slit[i].a_title->area, 0, 0, slit[i].w, titleh);
624                 XMoveResizeWindow(ob_display, slit[i].title, 0, 0,
625                                   slit[i].w, titleh);
626             }
627
628             paint(slit[i].frame, slit[i].a_frame);
629             paint(slit[i].title, slit[i].a_title);
630             XMapWindow(ob_display, slit[i].frame);
631         } else
632             XUnmapWindow(ob_display, slit[i].frame);
633     }
634 }