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