]> Dogcows Code - chaz/openbox/blob - openbox/slit.c
3956e0ec9be061db550e7607c954a964a4297f79
[chaz/openbox] / openbox / slit.c
1 #include "slit.h"
2 #include "screen.h"
3 #include "grab.h"
4 #include "timer.h"
5 #include "openbox.h"
6 #include "render/theme.h"
7 #include "render/render.h"
8
9 #define SLIT_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
10 EnterWindowMask | LeaveWindowMask)
11 #define SLITAPP_EVENT_MASK (StructureNotifyMask)
12
13 struct Slit {
14 Window frame;
15
16 /* user-requested position stuff */
17 SlitPosition pos;
18 int gravity;
19 int user_x, user_y;
20
21 /* actual position (when not auto-hidden) */
22 int x, y;
23 int w, h;
24
25 gboolean horz;
26 gboolean hide;
27 gboolean hidden;
28
29 Appearance *a_frame;
30
31 Timer *hide_timer;
32
33 GList *slit_apps;
34 };
35
36 GHashTable *slit_map = NULL;
37 GHashTable *slit_app_map = NULL;
38
39 static Slit *slit;
40 static int nslits;
41
42 static guint slit_hide_timeout = 3000; /* XXX make a config option */
43
44 static void slit_configure(Slit *self);
45
46 void slit_startup()
47 {
48 XSetWindowAttributes attrib;
49 int i;
50
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);
53
54 nslits = 1;
55 slit = g_new0(struct Slit, nslits);
56
57 for (i = 0; i < nslits; ++i) {
58 slit[i].horz = FALSE;
59 slit[i].hide = FALSE;
60 slit[i].hidden = TRUE;
61 slit[i].pos = SlitPos_TopRight;
62
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,
68 &attrib);
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);
72
73 g_hash_table_insert(slit_map, &slit[i].frame, &slit[i]);
74 }
75 }
76
77 void slit_shutdown()
78 {
79 int i;
80
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);
85 }
86 g_hash_table_destroy(slit_app_map);
87 g_hash_table_destroy(slit_map);
88 }
89
90 void slit_add(Window win, XWMHints *wmhints)
91 {
92 Slit *s;
93 SlitApp *app;
94 XWindowAttributes attrib;
95
96 /* XXX pick a slit */
97 s = &slit[0];
98
99 app = g_new0(SlitApp, 1);
100 app->slit = s;
101 app->win = win;
102 app->icon_win = (wmhints->flags & IconWindowHint) ?
103 wmhints->icon_window : win;
104
105 if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
106 app->w = attrib.width;
107 app->h = attrib.height;
108 } else {
109 app->w = app->h = 64;
110 }
111
112 s->slit_apps = g_list_append(s->slit_apps, app);
113 slit_configure(s);
114
115 XReparentWindow(ob_display, app->icon_win, s->frame, app->x, app->y);
116 /*
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.
122 */
123 if (ob_state == State_Starting)
124 app->ignore_unmaps += 2;
125
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);
130 }
131 XMapWindow(ob_display, app->icon_win);
132 XSync(ob_display, False);
133
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);
138
139 grab_button_full(2, 0, app->icon_win,
140 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
141 GrabModeAsync, ob_cursors.move);
142
143 g_hash_table_insert(slit_app_map, &app->icon_win, app);
144
145 g_message("Managed Slit App: 0x%lx", app->icon_win);
146 }
147
148 void slit_remove_all()
149 {
150 int i;
151
152 for (i = 0; i < nslits; ++i)
153 while (slit[i].slit_apps)
154 slit_remove(slit[i].slit_apps->data, TRUE);
155 }
156
157 void slit_remove(SlitApp *app, gboolean reparent)
158 {
159 ungrab_button(2, 0, app->icon_win);
160 XSelectInput(ob_display, app->icon_win, NoEventMask);
161 /* remove the window from our save set */
162 XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
163 XSync(ob_display, False);
164
165 g_hash_table_remove(slit_app_map, &app->icon_win);
166
167 if (reparent)
168 XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
169
170 app->slit->slit_apps = g_list_remove(app->slit->slit_apps, app);
171 slit_configure(app->slit);
172
173 g_message("Unmanaged Slit App: 0x%lx", app->icon_win);
174
175 g_free(app);
176 }
177
178 void slit_configure_all()
179 {
180 int i; for (i = 0; i < nslits; ++i) slit_configure(&slit[i]);
181 }
182
183 static void slit_configure(Slit *self)
184 {
185 GList *it;
186 int spot;
187
188 self->w = self->h = spot = 0;
189
190 for (it = self->slit_apps; it; it = it->next) {
191 struct SlitApp *app = it->data;
192 if (self->horz) {
193 app->x = spot;
194 app->y = 0;
195 self->w += app->w;
196 self->h = MAX(self->h, app->h);
197 spot += app->w;
198 } else {
199 app->x = 0;
200 app->y = spot;
201 self->w = MAX(self->w, app->w);
202 self->h += app->h;
203 spot += app->h;
204 }
205
206 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
207 }
208
209 /* used for calculating offsets */
210 self->w += theme_bwidth * 2;
211 self->h += theme_bwidth * 2;
212
213 /* calculate position */
214 switch (self->pos) {
215 case SlitPos_Floating:
216 self->x = self->user_x;
217 self->y = self->user_y;
218 break;
219 case SlitPos_TopLeft:
220 self->x = 0;
221 self->y = 0;
222 self->gravity = NorthWestGravity;
223 break;
224 case SlitPos_Top:
225 self->x = screen_physical_size.width / 2;
226 self->y = 0;
227 self->gravity = NorthGravity;
228 break;
229 case SlitPos_TopRight:
230 self->x = screen_physical_size.width;
231 self->y = 0;
232 self->gravity = NorthEastGravity;
233 break;
234 case SlitPos_Left:
235 self->x = 0;
236 self->y = screen_physical_size.height / 2;
237 self->gravity = WestGravity;
238 break;
239 case SlitPos_Right:
240 self->x = screen_physical_size.width;
241 self->y = screen_physical_size.height / 2;
242 self->gravity = EastGravity;
243 break;
244 case SlitPos_BottomLeft:
245 self->x = 0;
246 self->y = screen_physical_size.height;
247 self->gravity = SouthWestGravity;
248 break;
249 case SlitPos_Bottom:
250 self->x = screen_physical_size.width / 2;
251 self->y = screen_physical_size.height;
252 self->gravity = SouthGravity;
253 break;
254 case SlitPos_BottomRight:
255 self->x = screen_physical_size.width;
256 self->y = screen_physical_size.height;
257 self->gravity = SouthEastGravity;
258 break;
259 }
260
261 switch(self->gravity) {
262 case NorthGravity:
263 case CenterGravity:
264 case SouthGravity:
265 self->x -= self->w / 2;
266 break;
267 case NorthEastGravity:
268 case EastGravity:
269 case SouthEastGravity:
270 self->x -= self->w;
271 break;
272 }
273 switch(self->gravity) {
274 case WestGravity:
275 case CenterGravity:
276 case EastGravity:
277 self->y -= self->h / 2;
278 break;
279 case SouthWestGravity:
280 case SouthGravity:
281 case SouthEastGravity:
282 self->y -= self->h;
283 break;
284 }
285
286 if (self->hide && self->hidden) {
287 g_message("hidden");
288 switch (self->pos) {
289 case SlitPos_Floating:
290 break;
291 case SlitPos_TopLeft:
292 if (self->horz)
293 self->y -= self->h - theme_bwidth;
294 else
295 self->x -= self->w - theme_bwidth;
296 break;
297 case SlitPos_Top:
298 self->y -= self->h - theme_bwidth;
299 break;
300 case SlitPos_TopRight:
301 if (self->horz)
302 self->y -= self->h - theme_bwidth;
303 else
304 self->x += self->w - theme_bwidth;
305 break;
306 case SlitPos_Left:
307 self->x -= self->w - theme_bwidth;
308 break;
309 case SlitPos_Right:
310 self->x += self->w - theme_bwidth;
311 break;
312 case SlitPos_BottomLeft:
313 if (self->horz)
314 self->y += self->h - theme_bwidth;
315 else
316 self->x -= self->w - theme_bwidth;
317 break;
318 case SlitPos_Bottom:
319 self->y += self->h - theme_bwidth;
320 break;
321 case SlitPos_BottomRight:
322 if (self->horz)
323 self->y += self->h - theme_bwidth;
324 else
325 self->x += self->w - theme_bwidth;
326 break;
327 }
328 }
329
330 /* not used for actually sizing shit */
331 self->w -= theme_bwidth * 2;
332 self->h -= theme_bwidth * 2;
333
334 if (self->w > 0 && self->h > 0) {
335 RECT_SET(self->a_frame->area, 0, 0, self->w, self->h);
336 XMoveResizeWindow(ob_display, self->frame,
337 self->x, self->y, self->w, self->h);
338
339 paint(self->frame, self->a_frame);
340 XMapWindow(ob_display, self->frame);
341 } else
342 XUnmapWindow(ob_display, self->frame);
343
344 /* but they are useful outside of this function! */
345 self->w += theme_bwidth * 2;
346 self->h += theme_bwidth * 2;
347 }
348
349 void slit_app_configure(SlitApp *app, int w, int h)
350 {
351 app->w = w;
352 app->h = h;
353 slit_configure(app->slit);
354 }
355
356 void slit_app_drag(SlitApp *app, XMotionEvent *e)
357 {
358 Slit *src, *dest = NULL;
359 SlitApp *over = NULL;
360 GList *it;
361 int i;
362 int x, y;
363 gboolean after;
364
365 src = app->slit;
366 x = e->x_root;
367 y = e->y_root;
368
369 /* which slit are we on top of? */
370 for (i = 0; i < nslits; ++i)
371 if (x >= slit[i].x &&
372 y >= slit[i].y &&
373 x < slit[i].x + slit[i].w &&
374 y < slit[i].y + slit[i].h) {
375 dest = &slit[i];
376 break;
377 }
378 if (!dest) return;
379
380 x -= dest->x;
381 y -= dest->y;
382
383 /* which slit app are we on top of? */
384 for (it = dest->slit_apps; it; it = it->next) {
385 over = it->data;
386 if (dest->horz) {
387 if (x >= over->x && x < over->x + over->w)
388 break;
389 } else {
390 if (y >= over->y && y < over->y + over->h)
391 break;
392 }
393 }
394 if (!it || app == over) return;
395
396 x -= over->x;
397 y -= over->y;
398
399 if (dest->horz)
400 after = (x > over->w / 2);
401 else
402 after = (y > over->h / 2);
403
404 /* remove before doing the it->next! */
405 src->slit_apps = g_list_remove(src->slit_apps, app);
406 if (src != dest) slit_configure(src);
407
408 if (after) it = it->next;
409
410 dest->slit_apps = g_list_insert_before(dest->slit_apps, it, app);
411 slit_configure(dest);
412 }
413
414 static void hide_timeout(Slit *self)
415 {
416 /* dont repeat */
417 timer_stop(self->hide_timer);
418 self->hide_timer = NULL;
419
420 /* hide */
421 self->hidden = TRUE;
422 slit_configure(self);
423 }
424
425 void slit_hide(Slit *self, gboolean hide)
426 {
427 if (self->hidden == hide || !self->hide)
428 return;
429 if (!hide) {
430 /* show */
431 self->hidden = FALSE;
432 slit_configure(self);
433
434 /* if was hiding, stop it */
435 if (self->hide_timer) {
436 timer_stop(self->hide_timer);
437 self->hide_timer = NULL;
438 }
439 } else {
440 g_assert(!self->hide_timer);
441 self->hide_timer = timer_start(slit_hide_timeout * 1000,
442 (TimeoutHandler)hide_timeout, self);
443 }
444 }
This page took 0.051477 seconds and 3 git commands to generate.