6 #include "render/theme.h"
7 #include "render/render.h"
9 #define SLIT_EVENT_MASK (EnterWindowMask | LeaveWindowMask)
10 #define SLITAPP_EVENT_MASK (StructureNotifyMask)
15 /* user-requested position stuff */
20 /* actual position (when not auto-hidden) */
35 GHashTable
*slit_map
= NULL
;
36 GHashTable
*slit_app_map
= NULL
;
41 static guint slit_hide_timeout
= 3000; /* XXX make a config option */
43 static void slit_configure(Slit
*self
);
47 XSetWindowAttributes attrib
;
50 slit_map
= g_hash_table_new(g_int_hash
, g_int_equal
);
51 slit_app_map
= g_hash_table_new(g_int_hash
, g_int_equal
);
54 slit
= g_new0(struct Slit
, nslits
);
56 for (i
= 0; i
< nslits
; ++i
) {
59 slit
[i
].hidden
= TRUE
;
60 slit
[i
].pos
= SlitPos_TopRight
;
62 attrib
.event_mask
= SLIT_EVENT_MASK
;
63 attrib
.override_redirect
= True
;
64 slit
[i
].frame
= XCreateWindow(ob_display
, ob_root
, 0, 0, 1, 1, 0,
65 render_depth
, InputOutput
, render_visual
,
66 CWOverrideRedirect
| CWEventMask
,
68 slit
[i
].a_frame
= appearance_copy(theme_a_unfocused_title
);
69 XSetWindowBorder(ob_display
, slit
[i
].frame
, theme_b_color
->pixel
);
70 XSetWindowBorderWidth(ob_display
, slit
[i
].frame
, theme_bwidth
);
72 g_hash_table_insert(slit_map
, &slit
[i
].frame
, &slit
[i
]);
80 for (i
= 0; i
< nslits
; ++i
) {
81 XDestroyWindow(ob_display
, slit
[i
].frame
);
82 appearance_free(slit
[i
].a_frame
);
83 g_hash_table_remove(slit_map
, &slit
[i
].frame
);
85 g_hash_table_destroy(slit_app_map
);
86 g_hash_table_destroy(slit_map
);
89 void slit_add(Window win
, XWMHints
*wmhints
, XWindowAttributes
*attrib
)
97 app
= g_new0(SlitApp
, 1);
100 app
->icon_win
= (wmhints
->flags
& IconWindowHint
) ?
101 wmhints
->icon_window
: win
;
103 app
->w
= attrib
->width
;
104 app
->h
= attrib
->height
;
106 s
->slit_apps
= g_list_append(s
->slit_apps
, app
);
109 XReparentWindow(ob_display
, app
->icon_win
, s
->frame
, app
->x
, app
->y
);
111 This is the same case as in frame.c for client windows. When Openbox is
112 starting, the window is already mapped so we see unmap events occur for
113 it. There are 2 unmap events generated that we see, one with the 'event'
114 member set the root window, and one set to the client, but both get
115 handled and need to be ignored.
117 if (ob_state
== State_Starting
)
118 app
->ignore_unmaps
+= 2;
120 if (app
->win
!= app
->icon_win
) {
121 /* have to map it so that it can be re-managed on a restart */
122 XMoveWindow(ob_display
, app
->win
, -1000, -1000);
123 XMapWindow(ob_display
, app
->win
);
125 XMapWindow(ob_display
, app
->icon_win
);
126 XSync(ob_display
, False
);
128 /* specify that if we exit, the window should not be destroyed and should
129 be reparented back to root automatically */
130 XChangeSaveSet(ob_display
, app
->icon_win
, SetModeInsert
);
131 XSelectInput(ob_display
, app
->icon_win
, SLITAPP_EVENT_MASK
);
133 grab_button_full(2, 0, app
->icon_win
, ButtonMotionMask
, GrabModeAsync
,
136 g_hash_table_insert(slit_app_map
, &app
->icon_win
, app
);
138 g_message("Managed Slit App: 0x%lx", app
->icon_win
);
141 void slit_remove_all()
145 for (i
= 0; i
< nslits
; ++i
)
146 while (slit
[i
].slit_apps
)
147 slit_remove(slit
[i
].slit_apps
->data
, TRUE
);
150 void slit_remove(SlitApp
*app
, gboolean reparent
)
152 ungrab_button(2, 0, app
->icon_win
);
153 XSelectInput(ob_display
, app
->icon_win
, NoEventMask
);
154 /* remove the window from our save set */
155 XChangeSaveSet(ob_display
, app
->icon_win
, SetModeDelete
);
156 XSync(ob_display
, False
);
158 g_hash_table_remove(slit_app_map
, &app
->icon_win
);
161 XReparentWindow(ob_display
, app
->icon_win
, ob_root
, app
->x
, app
->y
);
163 app
->slit
->slit_apps
= g_list_remove(app
->slit
->slit_apps
, app
);
164 slit_configure(app
->slit
);
166 g_message("Unmanaged Slit App: 0x%lx", app
->icon_win
);
171 void slit_configure_all()
173 int i
; for (i
= 0; i
< nslits
; ++i
) slit_configure(&slit
[i
]);
176 static void slit_configure(Slit
*self
)
181 self
->w
= self
->h
= spot
= 0;
183 for (it
= self
->slit_apps
; it
; it
= it
->next
) {
184 struct SlitApp
*app
= it
->data
;
189 self
->h
= MAX(self
->h
, app
->h
);
194 self
->w
= MAX(self
->w
, app
->w
);
199 XMoveWindow(ob_display
, app
->icon_win
, app
->x
, app
->y
);
202 /* used for calculating offsets */
203 self
->w
+= theme_bwidth
* 2;
204 self
->h
+= theme_bwidth
* 2;
206 /* calculate position */
208 case SlitPos_Floating
:
209 self
->x
= self
->user_x
;
210 self
->y
= self
->user_y
;
212 case SlitPos_TopLeft
:
215 self
->gravity
= NorthWestGravity
;
218 self
->x
= screen_physical_size
.width
/ 2;
220 self
->gravity
= NorthGravity
;
222 case SlitPos_TopRight
:
223 self
->x
= screen_physical_size
.width
;
225 self
->gravity
= NorthEastGravity
;
229 self
->y
= screen_physical_size
.height
/ 2;
230 self
->gravity
= WestGravity
;
233 self
->x
= screen_physical_size
.width
;
234 self
->y
= screen_physical_size
.height
/ 2;
235 self
->gravity
= EastGravity
;
237 case SlitPos_BottomLeft
:
239 self
->y
= screen_physical_size
.height
;
240 self
->gravity
= SouthWestGravity
;
243 self
->x
= screen_physical_size
.width
/ 2;
244 self
->y
= screen_physical_size
.height
;
245 self
->gravity
= SouthGravity
;
247 case SlitPos_BottomRight
:
248 self
->x
= screen_physical_size
.width
;
249 self
->y
= screen_physical_size
.height
;
250 self
->gravity
= SouthEastGravity
;
254 switch(self
->gravity
) {
258 self
->x
-= self
->w
/ 2;
260 case NorthEastGravity
:
262 case SouthEastGravity
:
266 switch(self
->gravity
) {
270 self
->y
-= self
->h
/ 2;
272 case SouthWestGravity
:
274 case SouthEastGravity
:
279 if (self
->hide
&& self
->hidden
) {
282 case SlitPos_Floating
:
284 case SlitPos_TopLeft
:
286 self
->y
-= self
->h
- theme_bwidth
;
288 self
->x
-= self
->w
- theme_bwidth
;
291 self
->y
-= self
->h
- theme_bwidth
;
293 case SlitPos_TopRight
:
295 self
->y
-= self
->h
- theme_bwidth
;
297 self
->x
+= self
->w
- theme_bwidth
;
300 self
->x
-= self
->w
- theme_bwidth
;
303 self
->x
+= self
->w
- theme_bwidth
;
305 case SlitPos_BottomLeft
:
307 self
->y
+= self
->h
- theme_bwidth
;
309 self
->x
-= self
->w
- theme_bwidth
;
312 self
->y
+= self
->h
- theme_bwidth
;
314 case SlitPos_BottomRight
:
316 self
->y
+= self
->h
- theme_bwidth
;
318 self
->x
+= self
->w
- theme_bwidth
;
323 /* not used for actually sizing shit */
324 self
->w
-= theme_bwidth
* 2;
325 self
->h
-= theme_bwidth
* 2;
327 if (self
->w
> 0 && self
->h
> 0) {
328 RECT_SET(self
->a_frame
->area
, 0, 0, self
->w
, self
->h
);
329 XMoveResizeWindow(ob_display
, self
->frame
,
330 self
->x
, self
->y
, self
->w
, self
->h
);
332 paint(self
->frame
, self
->a_frame
);
333 XMapWindow(ob_display
, self
->frame
);
335 XUnmapWindow(ob_display
, self
->frame
);
337 /* but they are useful outside of this function! */
338 self
->w
+= theme_bwidth
* 2;
339 self
->h
+= theme_bwidth
* 2;
342 void slit_app_configure(SlitApp
*app
, int w
, int h
)
346 slit_configure(app
->slit
);
349 void slit_app_drag(SlitApp
*app
, XMotionEvent
*e
)
351 Slit
*src
, *dest
= NULL
;
352 SlitApp
*over
= NULL
;
362 /* which slit are we on top of? */
363 for (i
= 0; i
< nslits
; ++i
)
364 if (x
>= slit
[i
].x
&&
366 x
< slit
[i
].x
+ slit
[i
].w
&&
367 y
< slit
[i
].y
+ slit
[i
].h
) {
376 /* which slit app are we on top of? */
377 for (it
= dest
->slit_apps
; it
; it
= it
->next
) {
380 if (x
>= over
->x
&& x
< over
->x
+ over
->w
)
383 if (y
>= over
->y
&& y
< over
->y
+ over
->h
)
387 if (!it
|| app
== over
) return;
393 after
= (x
> over
->w
/ 2);
395 after
= (y
> over
->h
/ 2);
397 /* remove before doing the it->next! */
398 src
->slit_apps
= g_list_remove(src
->slit_apps
, app
);
399 if (src
!= dest
) slit_configure(src
);
401 if (after
) it
= it
->next
;
403 dest
->slit_apps
= g_list_insert_before(dest
->slit_apps
, it
, app
);
404 slit_configure(dest
);
407 static void hide_timeout(Slit
*self
)
410 timer_stop(self
->hide_timer
);
411 self
->hide_timer
= NULL
;
415 slit_configure(self
);
418 void slit_hide(Slit
*self
, gboolean hide
)
420 if (self
->hidden
== hide
|| !self
->hide
)
424 self
->hidden
= FALSE
;
425 slit_configure(self
);
427 /* if was hiding, stop it */
428 if (self
->hide_timer
) {
429 timer_stop(self
->hide_timer
);
430 self
->hide_timer
= NULL
;
433 g_assert(!self
->hide_timer
);
434 self
->hide_timer
= timer_start(slit_hide_timeout
* 1000,
435 (TimeoutHandler
)hide_timeout
, self
);