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