]> Dogcows Code - chaz/openbox/blob - openbox/slit.c
a5d8be3b6666188845ef4fc0d8658f112b3f6502
[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, ButtonMotionMask, GrabModeAsync,
140 ob_cursors.move);
141
142 g_hash_table_insert(slit_app_map, &app->icon_win, app);
143
144 g_message("Managed Slit App: 0x%lx", app->icon_win);
145 }
146
147 void slit_remove_all()
148 {
149 int i;
150
151 for (i = 0; i < nslits; ++i)
152 while (slit[i].slit_apps)
153 slit_remove(slit[i].slit_apps->data, TRUE);
154 }
155
156 void slit_remove(SlitApp *app, gboolean reparent)
157 {
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);
163
164 g_hash_table_remove(slit_app_map, &app->icon_win);
165
166 if (reparent)
167 XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
168
169 app->slit->slit_apps = g_list_remove(app->slit->slit_apps, app);
170 slit_configure(app->slit);
171
172 g_message("Unmanaged Slit App: 0x%lx", app->icon_win);
173
174 g_free(app);
175 }
176
177 void slit_configure_all()
178 {
179 int i; for (i = 0; i < nslits; ++i) slit_configure(&slit[i]);
180 }
181
182 static void slit_configure(Slit *self)
183 {
184 GList *it;
185 int spot;
186
187 self->w = self->h = spot = 0;
188
189 for (it = self->slit_apps; it; it = it->next) {
190 struct SlitApp *app = it->data;
191 if (self->horz) {
192 app->x = spot;
193 app->y = 0;
194 self->w += app->w;
195 self->h = MAX(self->h, app->h);
196 spot += app->w;
197 } else {
198 app->x = 0;
199 app->y = spot;
200 self->w = MAX(self->w, app->w);
201 self->h += app->h;
202 spot += app->h;
203 }
204
205 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
206 }
207
208 /* used for calculating offsets */
209 self->w += theme_bwidth * 2;
210 self->h += theme_bwidth * 2;
211
212 /* calculate position */
213 switch (self->pos) {
214 case SlitPos_Floating:
215 self->x = self->user_x;
216 self->y = self->user_y;
217 break;
218 case SlitPos_TopLeft:
219 self->x = 0;
220 self->y = 0;
221 self->gravity = NorthWestGravity;
222 break;
223 case SlitPos_Top:
224 self->x = screen_physical_size.width / 2;
225 self->y = 0;
226 self->gravity = NorthGravity;
227 break;
228 case SlitPos_TopRight:
229 self->x = screen_physical_size.width;
230 self->y = 0;
231 self->gravity = NorthEastGravity;
232 break;
233 case SlitPos_Left:
234 self->x = 0;
235 self->y = screen_physical_size.height / 2;
236 self->gravity = WestGravity;
237 break;
238 case SlitPos_Right:
239 self->x = screen_physical_size.width;
240 self->y = screen_physical_size.height / 2;
241 self->gravity = EastGravity;
242 break;
243 case SlitPos_BottomLeft:
244 self->x = 0;
245 self->y = screen_physical_size.height;
246 self->gravity = SouthWestGravity;
247 break;
248 case SlitPos_Bottom:
249 self->x = screen_physical_size.width / 2;
250 self->y = screen_physical_size.height;
251 self->gravity = SouthGravity;
252 break;
253 case SlitPos_BottomRight:
254 self->x = screen_physical_size.width;
255 self->y = screen_physical_size.height;
256 self->gravity = SouthEastGravity;
257 break;
258 }
259
260 switch(self->gravity) {
261 case NorthGravity:
262 case CenterGravity:
263 case SouthGravity:
264 self->x -= self->w / 2;
265 break;
266 case NorthEastGravity:
267 case EastGravity:
268 case SouthEastGravity:
269 self->x -= self->w;
270 break;
271 }
272 switch(self->gravity) {
273 case WestGravity:
274 case CenterGravity:
275 case EastGravity:
276 self->y -= self->h / 2;
277 break;
278 case SouthWestGravity:
279 case SouthGravity:
280 case SouthEastGravity:
281 self->y -= self->h;
282 break;
283 }
284
285 if (self->hide && self->hidden) {
286 g_message("hidden");
287 switch (self->pos) {
288 case SlitPos_Floating:
289 break;
290 case SlitPos_TopLeft:
291 if (self->horz)
292 self->y -= self->h - theme_bwidth;
293 else
294 self->x -= self->w - theme_bwidth;
295 break;
296 case SlitPos_Top:
297 self->y -= self->h - theme_bwidth;
298 break;
299 case SlitPos_TopRight:
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_Left:
306 self->x -= self->w - theme_bwidth;
307 break;
308 case SlitPos_Right:
309 self->x += self->w - theme_bwidth;
310 break;
311 case SlitPos_BottomLeft:
312 if (self->horz)
313 self->y += self->h - theme_bwidth;
314 else
315 self->x -= self->w - theme_bwidth;
316 break;
317 case SlitPos_Bottom:
318 self->y += self->h - theme_bwidth;
319 break;
320 case SlitPos_BottomRight:
321 if (self->horz)
322 self->y += self->h - theme_bwidth;
323 else
324 self->x += self->w - theme_bwidth;
325 break;
326 }
327 }
328
329 /* not used for actually sizing shit */
330 self->w -= theme_bwidth * 2;
331 self->h -= theme_bwidth * 2;
332
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);
337
338 paint(self->frame, self->a_frame);
339 XMapWindow(ob_display, self->frame);
340 } else
341 XUnmapWindow(ob_display, self->frame);
342
343 /* but they are useful outside of this function! */
344 self->w += theme_bwidth * 2;
345 self->h += theme_bwidth * 2;
346 }
347
348 void slit_app_configure(SlitApp *app, int w, int h)
349 {
350 app->w = w;
351 app->h = h;
352 slit_configure(app->slit);
353 }
354
355 void slit_app_drag(SlitApp *app, XMotionEvent *e)
356 {
357 Slit *src, *dest = NULL;
358 SlitApp *over = NULL;
359 GList *it;
360 int i;
361 int x, y;
362 gboolean after;
363
364 src = app->slit;
365 x = e->x_root;
366 y = e->y_root;
367
368 /* which slit are we on top of? */
369 for (i = 0; i < nslits; ++i)
370 if (x >= slit[i].x &&
371 y >= slit[i].y &&
372 x < slit[i].x + slit[i].w &&
373 y < slit[i].y + slit[i].h) {
374 dest = &slit[i];
375 break;
376 }
377 if (!dest) return;
378
379 x -= dest->x;
380 y -= dest->y;
381
382 /* which slit app are we on top of? */
383 for (it = dest->slit_apps; it; it = it->next) {
384 over = it->data;
385 if (dest->horz) {
386 if (x >= over->x && x < over->x + over->w)
387 break;
388 } else {
389 if (y >= over->y && y < over->y + over->h)
390 break;
391 }
392 }
393 if (!it || app == over) return;
394
395 x -= over->x;
396 y -= over->y;
397
398 if (dest->horz)
399 after = (x > over->w / 2);
400 else
401 after = (y > over->h / 2);
402
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);
406
407 if (after) it = it->next;
408
409 dest->slit_apps = g_list_insert_before(dest->slit_apps, it, app);
410 slit_configure(dest);
411 }
412
413 static void hide_timeout(Slit *self)
414 {
415 /* dont repeat */
416 timer_stop(self->hide_timer);
417 self->hide_timer = NULL;
418
419 /* hide */
420 self->hidden = TRUE;
421 slit_configure(self);
422 }
423
424 void slit_hide(Slit *self, gboolean hide)
425 {
426 if (self->hidden == hide || !self->hide)
427 return;
428 if (!hide) {
429 /* show */
430 self->hidden = FALSE;
431 slit_configure(self);
432
433 /* if was hiding, stop it */
434 if (self->hide_timer) {
435 timer_stop(self->hide_timer);
436 self->hide_timer = NULL;
437 }
438 } else {
439 g_assert(!self->hide_timer);
440 self->hide_timer = timer_start(slit_hide_timeout * 1000,
441 (TimeoutHandler)hide_timeout, self);
442 }
443 }
This page took 0.048304 seconds and 3 git commands to generate.