6 #include "render/theme.h"
7 #include "render/render.h"
9 #define SLIT_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
10 EnterWindowMask | LeaveWindowMask)
11 #define SLITAPP_EVENT_MASK (StructureNotifyMask)
16 /* user-requested position stuff */
21 /* actual position (when not auto-hidden) */
36 GHashTable *slit_map = NULL;
37 GHashTable *slit_app_map = NULL;
42 static guint slit_hide_timeout = 3000; /* XXX make a config option */
44 static void slit_configure(Slit *self);
48 XSetWindowAttributes attrib;
51 slit_map = g_hash_table_new(g_int_hash, g_int_equal);
52 slit_app_map = g_hash_table_new(g_int_hash, g_int_equal);
55 slit = g_new0(struct Slit, nslits);
57 for (i = 0; i < nslits; ++i) {
60 slit[i].hidden = TRUE;
61 slit[i].pos = SlitPos_TopRight;
63 attrib.event_mask = SLIT_EVENT_MASK;
64 attrib.override_redirect = True;
65 slit[i].frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
66 render_depth, InputOutput, render_visual,
67 CWOverrideRedirect | CWEventMask,
69 slit[i].a_frame = appearance_copy(theme_a_unfocused_title);
70 XSetWindowBorder(ob_display, slit[i].frame, theme_b_color->pixel);
71 XSetWindowBorderWidth(ob_display, slit[i].frame, theme_bwidth);
73 g_hash_table_insert(slit_map, &slit[i].frame, &slit[i]);
81 for (i = 0; i < nslits; ++i) {
82 XDestroyWindow(ob_display, slit[i].frame);
83 appearance_free(slit[i].a_frame);
84 g_hash_table_remove(slit_map, &slit[i].frame);
86 g_hash_table_destroy(slit_app_map);
87 g_hash_table_destroy(slit_map);
90 void slit_add(Window win, XWMHints *wmhints)
94 XWindowAttributes attrib;
99 app = g_new0(SlitApp, 1);
102 app->icon_win = (wmhints->flags & IconWindowHint) ?
103 wmhints->icon_window : win;
105 if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
106 app->w = attrib.width;
107 app->h = attrib.height;
109 app->w = app->h = 64;
112 s->slit_apps = g_list_append(s->slit_apps, app);
115 XReparentWindow(ob_display, app->icon_win, s->frame, app->x, app->y);
117 This is the same case as in frame.c for client windows. When Openbox is
118 starting, the window is already mapped so we see unmap events occur for
119 it. There are 2 unmap events generated that we see, one with the 'event'
120 member set the root window, and one set to the client, but both get
121 handled and need to be ignored.
123 if (ob_state == State_Starting)
124 app->ignore_unmaps += 2;
126 if (app->win != app->icon_win) {
127 /* have to map it so that it can be re-managed on a restart */
128 XMoveWindow(ob_display, app->win, -1000, -1000);
129 XMapWindow(ob_display, app->win);
131 XMapWindow(ob_display, app->icon_win);
132 XSync(ob_display, False);
134 /* specify that if we exit, the window should not be destroyed and should
135 be reparented back to root automatically */
136 XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
137 XSelectInput(ob_display, app->icon_win, SLITAPP_EVENT_MASK);
139 grab_button_full(2, 0, app->icon_win, ButtonMotionMask, GrabModeAsync,
142 g_hash_table_insert(slit_app_map, &app->icon_win, app);
144 g_message("Managed Slit App: 0x%lx", app->icon_win);
147 void slit_remove_all()
151 for (i = 0; i < nslits; ++i)
152 while (slit[i].slit_apps)
153 slit_remove(slit[i].slit_apps->data, TRUE);
156 void slit_remove(SlitApp *app, gboolean reparent)
158 ungrab_button(2, 0, app->icon_win);
159 XSelectInput(ob_display, app->icon_win, NoEventMask);
160 /* remove the window from our save set */
161 XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
162 XSync(ob_display, False);
164 g_hash_table_remove(slit_app_map, &app->icon_win);
167 XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
169 app->slit->slit_apps = g_list_remove(app->slit->slit_apps, app);
170 slit_configure(app->slit);
172 g_message("Unmanaged Slit App: 0x%lx", app->icon_win);
177 void slit_configure_all()
179 int i; for (i = 0; i < nslits; ++i) slit_configure(&slit[i]);
182 static void slit_configure(Slit *self)
187 self->w = self->h = spot = 0;
189 for (it = self->slit_apps; it; it = it->next) {
190 struct SlitApp *app = it->data;
195 self->h = MAX(self->h, app->h);
200 self->w = MAX(self->w, app->w);
205 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
208 /* used for calculating offsets */
209 self->w += theme_bwidth * 2;
210 self->h += theme_bwidth * 2;
212 /* calculate position */
214 case SlitPos_Floating:
215 self->x = self->user_x;
216 self->y = self->user_y;
218 case SlitPos_TopLeft:
221 self->gravity = NorthWestGravity;
224 self->x = screen_physical_size.width / 2;
226 self->gravity = NorthGravity;
228 case SlitPos_TopRight:
229 self->x = screen_physical_size.width;
231 self->gravity = NorthEastGravity;
235 self->y = screen_physical_size.height / 2;
236 self->gravity = WestGravity;
239 self->x = screen_physical_size.width;
240 self->y = screen_physical_size.height / 2;
241 self->gravity = EastGravity;
243 case SlitPos_BottomLeft:
245 self->y = screen_physical_size.height;
246 self->gravity = SouthWestGravity;
249 self->x = screen_physical_size.width / 2;
250 self->y = screen_physical_size.height;
251 self->gravity = SouthGravity;
253 case SlitPos_BottomRight:
254 self->x = screen_physical_size.width;
255 self->y = screen_physical_size.height;
256 self->gravity = SouthEastGravity;
260 switch(self->gravity) {
264 self->x -= self->w / 2;
266 case NorthEastGravity:
268 case SouthEastGravity:
272 switch(self->gravity) {
276 self->y -= self->h / 2;
278 case SouthWestGravity:
280 case SouthEastGravity:
285 if (self->hide && self->hidden) {
288 case SlitPos_Floating:
290 case SlitPos_TopLeft:
292 self->y -= self->h - theme_bwidth;
294 self->x -= self->w - theme_bwidth;
297 self->y -= self->h - theme_bwidth;
299 case SlitPos_TopRight:
301 self->y -= self->h - theme_bwidth;
303 self->x += self->w - theme_bwidth;
306 self->x -= self->w - theme_bwidth;
309 self->x += self->w - theme_bwidth;
311 case SlitPos_BottomLeft:
313 self->y += self->h - theme_bwidth;
315 self->x -= self->w - theme_bwidth;
318 self->y += self->h - theme_bwidth;
320 case SlitPos_BottomRight:
322 self->y += self->h - theme_bwidth;
324 self->x += self->w - theme_bwidth;
329 /* not used for actually sizing shit */
330 self->w -= theme_bwidth * 2;
331 self->h -= theme_bwidth * 2;
333 if (self->w > 0 && self->h > 0) {
334 RECT_SET(self->a_frame->area, 0, 0, self->w, self->h);
335 XMoveResizeWindow(ob_display, self->frame,
336 self->x, self->y, self->w, self->h);
338 paint(self->frame, self->a_frame);
339 XMapWindow(ob_display, self->frame);
341 XUnmapWindow(ob_display, self->frame);
343 /* but they are useful outside of this function! */
344 self->w += theme_bwidth * 2;
345 self->h += theme_bwidth * 2;
348 void slit_app_configure(SlitApp *app, int w, int h)
352 slit_configure(app->slit);
355 void slit_app_drag(SlitApp *app, XMotionEvent *e)
357 Slit *src, *dest = NULL;
358 SlitApp *over = NULL;
368 /* which slit are we on top of? */
369 for (i = 0; i < nslits; ++i)
370 if (x >= slit[i].x &&
372 x < slit[i].x + slit[i].w &&
373 y < slit[i].y + slit[i].h) {
382 /* which slit app are we on top of? */
383 for (it = dest->slit_apps; it; it = it->next) {
386 if (x >= over->x && x < over->x + over->w)
389 if (y >= over->y && y < over->y + over->h)
393 if (!it || app == over) return;
399 after = (x > over->w / 2);
401 after = (y > over->h / 2);
403 /* remove before doing the it->next! */
404 src->slit_apps = g_list_remove(src->slit_apps, app);
405 if (src != dest) slit_configure(src);
407 if (after) it = it->next;
409 dest->slit_apps = g_list_insert_before(dest->slit_apps, it, app);
410 slit_configure(dest);
413 static void hide_timeout(Slit *self)
416 timer_stop(self->hide_timer);
417 self->hide_timer = NULL;
421 slit_configure(self);
424 void slit_hide(Slit *self, gboolean hide)
426 if (self->hidden == hide || !self->hide)
430 self->hidden = FALSE;
431 slit_configure(self);
433 /* if was hiding, stop it */
434 if (self->hide_timer) {
435 timer_stop(self->hide_timer);
436 self->hide_timer = NULL;
439 g_assert(!self->hide_timer);
440 self->hide_timer = timer_start(slit_hide_timeout * 1000,
441 (TimeoutHandler)hide_timeout, self);