5 #include "render/theme.h"
7 #define SLIT_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
8 EnterWindowMask | LeaveWindowMask)
9 #define SLITAPP_EVENT_MASK (StructureNotifyMask)
11 GHashTable *slit_map = NULL;
12 GHashTable *slit_app_map = NULL;
17 static guint slit_hide_timeout = 3000; /* XXX make a config option */
19 static void slit_configure(Slit *self);
23 XSetWindowAttributes attrib;
26 slit_map = g_hash_table_new(g_int_hash, g_int_equal);
27 slit_app_map = g_hash_table_new(g_int_hash, g_int_equal);
30 slit = g_new0(struct Slit, nslits);
31 slit->obwin.type = Window_Slit;
33 for (i = 0; i < nslits; ++i) {
36 slit[i].hidden = TRUE;
37 slit[i].pos = SlitPos_TopRight;
38 slit[i].layer = Layer_Top;
40 attrib.event_mask = SLIT_EVENT_MASK;
41 attrib.override_redirect = True;
42 slit[i].frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
43 render_depth, InputOutput, render_visual,
44 CWOverrideRedirect | CWEventMask,
46 slit[i].a_frame = appearance_copy(theme_a_unfocused_title);
47 XSetWindowBorder(ob_display, slit[i].frame, theme_b_color->pixel);
48 XSetWindowBorderWidth(ob_display, slit[i].frame, theme_bwidth);
50 g_hash_table_insert(slit_map, &slit[i].frame, &slit[i]);
51 stacking_add(SLIT_AS_WINDOW(&slit[i]));
52 stacking_raise(SLIT_AS_WINDOW(&slit[i]));
60 for (i = 0; i < nslits; ++i) {
61 XDestroyWindow(ob_display, slit[i].frame);
62 appearance_free(slit[i].a_frame);
63 g_hash_table_remove(slit_map, &slit[i].frame);
64 stacking_remove(&slit[i]);
66 g_hash_table_destroy(slit_app_map);
67 g_hash_table_destroy(slit_map);
70 void slit_add(Window win, XWMHints *wmhints)
74 XWindowAttributes attrib;
79 app = g_new0(SlitApp, 1);
82 app->icon_win = (wmhints->flags & IconWindowHint) ?
83 wmhints->icon_window : win;
85 if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
86 app->w = attrib.width;
87 app->h = attrib.height;
92 s->slit_apps = g_list_append(s->slit_apps, app);
95 XReparentWindow(ob_display, app->icon_win, s->frame, app->x, app->y);
97 This is the same case as in frame.c for client windows. When Openbox is
98 starting, the window is already mapped so we see unmap events occur for
99 it. There are 2 unmap events generated that we see, one with the 'event'
100 member set the root window, and one set to the client, but both get
101 handled and need to be ignored.
103 if (ob_state == State_Starting)
104 app->ignore_unmaps += 2;
106 if (app->win != app->icon_win) {
107 /* have to map it so that it can be re-managed on a restart */
108 XMoveWindow(ob_display, app->win, -1000, -1000);
109 XMapWindow(ob_display, app->win);
111 XMapWindow(ob_display, app->icon_win);
112 XSync(ob_display, False);
114 /* specify that if we exit, the window should not be destroyed and should
115 be reparented back to root automatically */
116 XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
117 XSelectInput(ob_display, app->icon_win, SLITAPP_EVENT_MASK);
119 grab_button_full(2, 0, app->icon_win,
120 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
121 GrabModeAsync, ob_cursors.move);
123 g_hash_table_insert(slit_app_map, &app->icon_win, app);
125 g_message("Managed Slit App: 0x%lx", app->icon_win);
128 void slit_remove_all()
132 for (i = 0; i < nslits; ++i)
133 while (slit[i].slit_apps)
134 slit_remove(slit[i].slit_apps->data, TRUE);
137 void slit_remove(SlitApp *app, gboolean reparent)
139 ungrab_button(2, 0, app->icon_win);
140 XSelectInput(ob_display, app->icon_win, NoEventMask);
141 /* remove the window from our save set */
142 XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
143 XSync(ob_display, False);
145 g_hash_table_remove(slit_app_map, &app->icon_win);
148 XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
150 app->slit->slit_apps = g_list_remove(app->slit->slit_apps, app);
151 slit_configure(app->slit);
153 g_message("Unmanaged Slit App: 0x%lx", app->icon_win);
158 void slit_configure_all()
160 int i; for (i = 0; i < nslits; ++i) slit_configure(&slit[i]);
163 static void slit_configure(Slit *self)
168 self->w = self->h = spot = 0;
170 for (it = self->slit_apps; it; it = it->next) {
171 struct SlitApp *app = it->data;
176 self->h = MAX(self->h, app->h);
181 self->w = MAX(self->w, app->w);
186 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
189 /* used for calculating offsets */
190 self->w += theme_bwidth * 2;
191 self->h += theme_bwidth * 2;
193 /* calculate position */
195 case SlitPos_Floating:
196 self->x = self->user_x;
197 self->y = self->user_y;
199 case SlitPos_TopLeft:
202 self->gravity = NorthWestGravity;
205 self->x = screen_physical_size.width / 2;
207 self->gravity = NorthGravity;
209 case SlitPos_TopRight:
210 self->x = screen_physical_size.width;
212 self->gravity = NorthEastGravity;
216 self->y = screen_physical_size.height / 2;
217 self->gravity = WestGravity;
220 self->x = screen_physical_size.width;
221 self->y = screen_physical_size.height / 2;
222 self->gravity = EastGravity;
224 case SlitPos_BottomLeft:
226 self->y = screen_physical_size.height;
227 self->gravity = SouthWestGravity;
230 self->x = screen_physical_size.width / 2;
231 self->y = screen_physical_size.height;
232 self->gravity = SouthGravity;
234 case SlitPos_BottomRight:
235 self->x = screen_physical_size.width;
236 self->y = screen_physical_size.height;
237 self->gravity = SouthEastGravity;
241 switch(self->gravity) {
245 self->x -= self->w / 2;
247 case NorthEastGravity:
249 case SouthEastGravity:
253 switch(self->gravity) {
257 self->y -= self->h / 2;
259 case SouthWestGravity:
261 case SouthEastGravity:
266 if (self->hide && self->hidden) {
269 case SlitPos_Floating:
271 case SlitPos_TopLeft:
273 self->y -= self->h - theme_bwidth;
275 self->x -= self->w - theme_bwidth;
278 self->y -= self->h - theme_bwidth;
280 case SlitPos_TopRight:
282 self->y -= self->h - theme_bwidth;
284 self->x += self->w - theme_bwidth;
287 self->x -= self->w - theme_bwidth;
290 self->x += self->w - theme_bwidth;
292 case SlitPos_BottomLeft:
294 self->y += self->h - theme_bwidth;
296 self->x -= self->w - theme_bwidth;
299 self->y += self->h - theme_bwidth;
301 case SlitPos_BottomRight:
303 self->y += self->h - theme_bwidth;
305 self->x += self->w - theme_bwidth;
310 /* not used for actually sizing shit */
311 self->w -= theme_bwidth * 2;
312 self->h -= theme_bwidth * 2;
314 if (self->w > 0 && self->h > 0) {
315 RECT_SET(self->a_frame->area, 0, 0, self->w, self->h);
316 XMoveResizeWindow(ob_display, self->frame,
317 self->x, self->y, self->w, self->h);
319 paint(self->frame, self->a_frame);
320 XMapWindow(ob_display, self->frame);
322 XUnmapWindow(ob_display, self->frame);
324 /* but they are useful outside of this function! */
325 self->w += theme_bwidth * 2;
326 self->h += theme_bwidth * 2;
329 void slit_app_configure(SlitApp *app, int w, int h)
333 slit_configure(app->slit);
336 void slit_app_drag(SlitApp *app, XMotionEvent *e)
338 Slit *src, *dest = NULL;
339 SlitApp *over = NULL;
349 /* which slit are we on top of? */
350 for (i = 0; i < nslits; ++i)
351 if (x >= slit[i].x &&
353 x < slit[i].x + slit[i].w &&
354 y < slit[i].y + slit[i].h) {
363 /* which slit app are we on top of? */
364 for (it = dest->slit_apps; it; it = it->next) {
367 if (x >= over->x && x < over->x + over->w)
370 if (y >= over->y && y < over->y + over->h)
374 if (!it || app == over) return;
380 after = (x > over->w / 2);
382 after = (y > over->h / 2);
384 /* remove before doing the it->next! */
385 src->slit_apps = g_list_remove(src->slit_apps, app);
386 if (src != dest) slit_configure(src);
388 if (after) it = it->next;
390 dest->slit_apps = g_list_insert_before(dest->slit_apps, it, app);
391 slit_configure(dest);
394 static void hide_timeout(Slit *self)
397 timer_stop(self->hide_timer);
398 self->hide_timer = NULL;
402 slit_configure(self);
405 void slit_hide(Slit *self, gboolean hide)
407 if (self->hidden == hide || !self->hide)
411 self->hidden = FALSE;
412 slit_configure(self);
414 /* if was hiding, stop it */
415 if (self->hide_timer) {
416 timer_stop(self->hide_timer);
417 self->hide_timer = NULL;
420 g_assert(!self->hide_timer);
421 self->hide_timer = timer_start(slit_hide_timeout * 1000,
422 (TimeoutHandler)hide_timeout, self);