]> icculus.org git repositories - dana/openbox.git/blob - openbox/slit.c
add autohiding to the slit
[dana/openbox.git] / openbox / slit.c
1 #include "slit.h"
2 #include "screen.h"
3 #include "timer.h"
4 #include "openbox.h"
5 #include "render/theme.h"
6 #include "render/render.h"
7
8 #define SLIT_EVENT_MASK (EnterWindowMask | LeaveWindowMask)
9 #define SLITAPP_EVENT_MASK (StructureNotifyMask)
10
11 struct Slit {
12     Window frame;
13
14     /* user-requested position stuff */
15     SlitPosition pos;
16     int gravity;
17     int user_x, user_y;
18
19     /* actual position (when not auto-hidden) */
20     int x, y;
21     int w, h;
22
23     gboolean horz;
24     gboolean hide;
25     gboolean hidden;
26
27     Appearance *a_frame;
28
29     Timer *hide_timer;
30
31     GList *slit_apps;
32 };
33
34 GHashTable *slit_map = NULL;
35 GHashTable *slit_app_map = NULL;
36
37 static Slit *slit;
38 static int nslits;
39
40 static void slit_configure(Slit *self);
41
42 void slit_startup()
43 {
44     XSetWindowAttributes attrib;
45     int i;
46
47     slit_map = g_hash_table_new(g_int_hash, g_int_equal);
48     slit_app_map = g_hash_table_new(g_int_hash, g_int_equal);
49
50     nslits = 1;
51     slit = g_new0(struct Slit, nslits);
52
53     for (i = 0; i < nslits; ++i) {
54         slit[i].horz = FALSE;
55         slit[i].hide = TRUE;
56         slit[i].hidden = TRUE;
57         slit[i].pos = SlitPos_TopRight;
58
59         attrib.event_mask = SLIT_EVENT_MASK;
60         attrib.override_redirect = True;
61         slit[i].frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
62                                       render_depth, InputOutput, render_visual,
63                                       CWOverrideRedirect | CWEventMask,
64                                       &attrib);
65         slit[i].a_frame = appearance_copy(theme_a_unfocused_title);
66         XSetWindowBorder(ob_display, slit[i].frame, theme_b_color->pixel);
67         XSetWindowBorderWidth(ob_display, slit[i].frame, theme_bwidth);
68
69         g_hash_table_insert(slit_map, &slit[i].frame, &slit[i]);
70     }
71 }
72
73 void slit_shutdown()
74 {
75     int i;
76
77     for (i = 0; i < nslits; ++i) {
78         XDestroyWindow(ob_display, slit[i].frame);
79         appearance_free(slit[i].a_frame);
80         g_hash_table_remove(slit_map, &slit[i].frame);
81     }
82     g_hash_table_destroy(slit_app_map);
83     g_hash_table_destroy(slit_map);
84 }
85
86 void slit_add(Window win, XWMHints *wmhints, XWindowAttributes *attrib)
87 {
88     Slit *s;
89     SlitApp *app;
90
91     /* XXX pick a slit */
92     s = &slit[0];
93
94     app = g_new0(SlitApp, 1);
95     app->slit = s;
96     app->win = win;
97     app->icon_win = (wmhints->flags & IconWindowHint) ?
98         wmhints->icon_window : win;
99     
100     app->w = attrib->width;
101     app->h = attrib->height;
102
103     s->slit_apps = g_list_append(s->slit_apps, app);
104     slit_configure(s);
105
106     XReparentWindow(ob_display, app->icon_win, s->frame, app->x, app->y);
107     /*
108       This is the same case as in frame.c for client windows. When Openbox is
109       starting, the window is already mapped so we see unmap events occur for
110       it. There are 2 unmap events generated that we see, one with the 'event'
111       member set the root window, and one set to the client, but both get
112       handled and need to be ignored.
113     */
114     if (ob_state == State_Starting)
115         app->ignore_unmaps += 2;
116
117     if (app->win != app->icon_win) {
118         /* have to map it so that it can be re-managed on a restart */
119         XMoveWindow(ob_display, app->win, -1000, -1000);
120         XMapWindow(ob_display, app->win);
121     }
122     g_message("   Slitting 0x%lx 0x%lx", app->icon_win, app->win);
123     XMapWindow(ob_display, app->icon_win);
124     XSync(ob_display, False);
125
126     /* specify that if we exit, the window should not be destroyed and should
127        be reparented back to root automatically */
128     XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
129     XSelectInput(ob_display, app->icon_win, SLITAPP_EVENT_MASK);
130
131     g_hash_table_insert(slit_app_map, &app->icon_win, app);
132
133     g_message("Managed Slit App: 0x%lx", app->icon_win);
134 }
135
136 void slit_remove_all()
137 {
138     int i;
139
140     for (i = 0; i < nslits; ++i)
141         while (slit[i].slit_apps)
142             slit_remove(slit[i].slit_apps->data, TRUE);
143 }
144
145 void slit_remove(SlitApp *app, gboolean reparent)
146 {
147     XSelectInput(ob_display, app->icon_win, NoEventMask);
148     /* remove the window from our save set */
149     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
150     XSync(ob_display, False);
151
152     g_hash_table_remove(slit_app_map, &app->icon_win);
153
154     if (reparent)
155         XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
156
157     app->slit->slit_apps = g_list_remove(app->slit->slit_apps, app);
158     slit_configure(app->slit);
159
160     g_message("Unmanaged Slit App: 0x%lx", app->icon_win);
161
162     g_free(app);
163 }
164
165 void slit_configure_all()
166 {
167     int i; for (i = 0; i < nslits; ++i) slit_configure(&slit[i]);
168 }
169
170 static void slit_configure(Slit *self)
171 {
172     GList *it;
173     int spot;
174
175     self->w = self->h = spot = 0;
176
177     for (it = self->slit_apps; it; it = it->next) {
178         struct SlitApp *app = it->data;
179         if (self->horz) {
180             app->x = spot;
181             app->y = 0;
182             self->w += app->w;
183             self->h = MAX(self->h, app->h);
184             spot += app->w;
185         } else {
186             app->x = 0;
187             app->y = spot;
188             self->w = MAX(self->h, app->w);
189             self->h += app->h;
190             spot += app->h;
191         }
192
193         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
194     }
195
196     /* used for calculating offsets */
197     self->w += theme_bwidth * 2;
198     self->h += theme_bwidth * 2;
199
200     /* calculate position */
201     switch (self->pos) {
202     case SlitPos_Floating:
203         self->x = self->user_x;
204         self->y = self->user_y;
205         break;
206     case SlitPos_TopLeft:
207         self->x = 0;
208         self->y = 0;
209         self->gravity = NorthWestGravity;
210         break;
211     case SlitPos_Top:
212         self->x = screen_physical_size.width / 2;
213         self->y = 0;
214         self->gravity = NorthGravity;
215         break;
216     case SlitPos_TopRight:
217         self->x = screen_physical_size.width;
218         self->y = 0;
219         self->gravity = NorthEastGravity;
220         break;
221     case SlitPos_Left:
222         self->x = 0;
223         self->y = screen_physical_size.height / 2;
224         self->gravity = WestGravity;
225         break;
226     case SlitPos_Right:
227         self->x = screen_physical_size.width;
228         self->y = screen_physical_size.height / 2;
229         self->gravity = EastGravity;
230         break;
231     case SlitPos_BottomLeft:
232         self->x = 0;
233         self->y = screen_physical_size.height;
234         self->gravity = SouthWestGravity;
235         break;
236     case SlitPos_Bottom:
237         self->x = screen_physical_size.width / 2;
238         self->y = screen_physical_size.height;
239         self->gravity = SouthGravity;
240         break;
241     case SlitPos_BottomRight:
242         self->x = screen_physical_size.width;
243         self->y = screen_physical_size.height;
244         self->gravity = SouthEastGravity;
245         break;
246     }
247
248     switch(self->gravity) {
249     case NorthGravity:
250     case CenterGravity:
251     case SouthGravity:
252         self->x -= self->w / 2;
253         break;
254     case NorthEastGravity:
255     case EastGravity:
256     case SouthEastGravity:
257         self->x -= self->w;
258         break;
259     }
260     switch(self->gravity) {
261     case WestGravity:
262     case CenterGravity:
263     case EastGravity:
264         self->y -= self->h / 2;
265         break;
266     case SouthWestGravity:
267     case SouthGravity:
268     case SouthEastGravity:
269         self->y -= self->h;
270         break;
271     }
272
273     if (self->hide && self->hidden) {
274         g_message("hidden");
275         switch (self->pos) {
276         case SlitPos_Floating:
277             break;
278         case SlitPos_TopLeft:
279             if (self->horz)
280                 self->y -= self->h - theme_bwidth;
281             else
282                 self->x -= self->w - theme_bwidth;
283             break;
284         case SlitPos_Top:
285             self->y -= self->h - theme_bwidth;
286             break;
287         case SlitPos_TopRight:
288             if (self->horz)
289                 self->y -= self->h - theme_bwidth;
290             else
291                 self->x += self->w - theme_bwidth;
292             break;
293         case SlitPos_Left:
294             self->x -= self->w - theme_bwidth;
295             break;
296         case SlitPos_Right:
297             self->x += self->w - theme_bwidth;
298             break;
299         case SlitPos_BottomLeft:
300             if (self->horz)
301                 self->y += self->h - theme_bwidth;
302             else
303                 self->x -= self->w - theme_bwidth;
304             break;
305         case SlitPos_Bottom:
306             self->y += self->h - theme_bwidth;
307             break;
308         case SlitPos_BottomRight:
309             if (self->horz)
310                 self->y += self->h - theme_bwidth;
311             else
312                 self->x += self->w - theme_bwidth;
313             break;
314         }    
315     }
316
317     /* not used for actually sizing shit */
318     self->w -= theme_bwidth * 2;
319     self->h -= theme_bwidth * 2;
320
321     if (self->w > 0 && self->h > 0) {
322         RECT_SET(self->a_frame->area, 0, 0, self->w, self->h);
323         XMoveResizeWindow(ob_display, self->frame,
324                           self->x, self->y, self->w, self->h);
325
326         paint(self->frame, self->a_frame);
327         XMapWindow(ob_display, self->frame);
328     } else
329         XUnmapWindow(ob_display, self->frame);
330 }
331
332 void slit_app_configure(SlitApp *app, int w, int h)
333 {
334     app->w = w;
335     app->h = h;
336     slit_configure(app->slit);
337 }
338
339 static void hide_timeout(Slit *self)
340 {
341     /* dont repeat */
342     timer_stop(self->hide_timer);
343     self->hide_timer = NULL;
344
345     /* hide */
346     self->hidden = TRUE;
347     slit_configure(self);
348 }
349
350 void slit_hide(Slit *self, gboolean hide)
351 {
352     if (self->hidden == hide)
353         return;
354     if (!hide) {
355         /* show */
356         self->hidden = FALSE;
357         slit_configure(self);
358
359         /* if was hiding, stop it */
360         if (self->hide_timer) {
361             timer_stop(self->hide_timer);
362             self->hide_timer = NULL;
363         }
364     } else {
365         g_assert(!self->hide_timer);
366         self->hide_timer = timer_start(3000000,
367                                        (TimeoutHandler)hide_timeout, self);
368     }
369 }