]> Dogcows Code - chaz/openbox/blob - openbox/dock.c
8cb40fb6c7ca6614c65d95272bc284fada96fbd4
[chaz/openbox] / openbox / dock.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 dock.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "dock.h"
22 #include "mainloop.h"
23 #include "screen.h"
24 #include "prop.h"
25 #include "config.h"
26 #include "grab.h"
27 #include "openbox.h"
28 #include "render/theme.h"
29
30 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
31 EnterWindowMask | LeaveWindowMask)
32 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
33
34 static ObDock *dock;
35
36 StrutPartial dock_strut;
37
38 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
39 {
40 if (grab) {
41 grab_button_full(config_dock_app_move_button,
42 config_dock_app_move_modifiers, app->icon_win,
43 ButtonPressMask | ButtonReleaseMask |
44 ButtonMotionMask,
45 GrabModeAsync, OB_CURSOR_MOVE);
46 } else {
47 ungrab_button(config_dock_app_move_button,
48 config_dock_app_move_modifiers, app->icon_win);
49 }
50 }
51
52 void dock_startup(gboolean reconfig)
53 {
54 XSetWindowAttributes attrib;
55
56 if (reconfig) {
57 GList *it;
58
59 XSetWindowBorder(ob_display, dock->frame,
60 RrColorPixel(ob_rr_theme->osd_border_color));
61 XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->obwidth);
62
63 RrAppearanceFree(dock->a_frame);
64 dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
65
66 stacking_add(DOCK_AS_WINDOW(dock));
67
68 dock_configure();
69 dock_hide(TRUE);
70
71 for (it = dock->dock_apps; it; it = g_list_next(it))
72 dock_app_grab_button(it->data, TRUE);
73 return;
74 }
75
76 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
77 0, 0, 0, 0, 0, 0, 0, 0);
78
79 dock = g_new0(ObDock, 1);
80 dock->obwin.type = Window_Dock;
81
82 dock->hidden = TRUE;
83
84 attrib.event_mask = DOCK_EVENT_MASK;
85 attrib.override_redirect = True;
86 dock->frame = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen),
87 0, 0, 1, 1, 0,
88 RrDepth(ob_rr_inst), InputOutput,
89 RrVisual(ob_rr_inst),
90 CWOverrideRedirect | CWEventMask,
91 &attrib);
92 dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
93 XSetWindowBorder(ob_display, dock->frame,
94 RrColorPixel(ob_rr_theme->osd_border_color));
95 XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->obwidth);
96
97 /* Setting the window type so xcompmgr can tell what it is */
98 PROP_SET32(dock->frame, net_wm_window_type, atom,
99 prop_atoms.net_wm_window_type_dock);
100
101 g_hash_table_insert(window_map, &dock->frame, dock);
102 stacking_add(DOCK_AS_WINDOW(dock));
103 }
104
105 void dock_shutdown(gboolean reconfig)
106 {
107 if (reconfig) {
108 GList *it;
109
110 stacking_remove(DOCK_AS_WINDOW(dock));
111
112 for (it = dock->dock_apps; it; it = g_list_next(it))
113 dock_app_grab_button(it->data, FALSE);
114 return;
115 }
116
117 XDestroyWindow(ob_display, dock->frame);
118 RrAppearanceFree(dock->a_frame);
119 g_hash_table_remove(window_map, &dock->frame);
120 stacking_remove(dock);
121 }
122
123 void dock_add(Window win, XWMHints *wmhints)
124 {
125 ObDockApp *app;
126 XWindowAttributes attrib;
127 gchar **data;
128
129 app = g_new0(ObDockApp, 1);
130 app->obwin.type = Window_DockApp;
131 app->win = win;
132 app->icon_win = (wmhints->flags & IconWindowHint) ?
133 wmhints->icon_window : win;
134
135 if (PROP_GETSS(app->win, wm_class, locale, &data)) {
136 if (data[0]) {
137 app->name = g_strdup(data[0]);
138 if (data[1])
139 app->class = g_strdup(data[1]);
140 }
141 g_strfreev(data);
142 }
143
144 if (app->name == NULL) app->name = g_strdup("");
145 if (app->class == NULL) app->class = g_strdup("");
146
147 if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
148 app->w = attrib.width;
149 app->h = attrib.height;
150 } else {
151 app->w = app->h = 64;
152 }
153
154 dock->dock_apps = g_list_append(dock->dock_apps, app);
155 dock_configure();
156
157 XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
158 /*
159 This is the same case as in frame.c for client windows. When Openbox is
160 starting, the window is already mapped so we see unmap events occur for
161 it. There are 2 unmap events generated that we see, one with the 'event'
162 member set the root window, and one set to the client, but both get
163 handled and need to be ignored.
164 */
165 if (ob_state() == OB_STATE_STARTING)
166 app->ignore_unmaps += 2;
167
168 if (app->win != app->icon_win) {
169 /* have to map it so that it can be re-managed on a restart */
170 XMoveWindow(ob_display, app->win, -1000, -1000);
171 XMapWindow(ob_display, app->win);
172 }
173 XMapWindow(ob_display, app->icon_win);
174 XSync(ob_display, False);
175
176 /* specify that if we exit, the window should not be destroyed and should
177 be reparented back to root automatically */
178 XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
179 XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
180
181 dock_app_grab_button(app, TRUE);
182
183 g_hash_table_insert(window_map, &app->icon_win, app);
184
185 ob_debug("Managed Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
186 }
187
188 void dock_remove_all()
189 {
190 while (dock->dock_apps)
191 dock_remove(dock->dock_apps->data, TRUE);
192 }
193
194 void dock_remove(ObDockApp *app, gboolean reparent)
195 {
196 dock_app_grab_button(app, FALSE);
197 XSelectInput(ob_display, app->icon_win, NoEventMask);
198 /* remove the window from our save set */
199 XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
200 XSync(ob_display, False);
201
202 g_hash_table_remove(window_map, &app->icon_win);
203
204 if (reparent)
205 XReparentWindow(ob_display, app->icon_win,
206 RootWindow(ob_display, ob_screen), app->x, app->y);
207
208 dock->dock_apps = g_list_remove(dock->dock_apps, app);
209 dock_configure();
210
211 ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
212
213 g_free(app->name);
214 g_free(app->class);
215 g_free(app);
216 }
217
218 void dock_configure()
219 {
220 GList *it;
221 gint hspot, vspot;
222 gint gravity;
223 gint l, r, t, b;
224 gint strw, strh;
225 Rect *a;
226
227 RrMargins(dock->a_frame, &l, &t, &r, &b);
228
229 dock->area.width = dock->area.height = 0;
230
231 /* get the size */
232 for (it = dock->dock_apps; it; it = g_list_next(it)) {
233 ObDockApp *app = it->data;
234 switch (config_dock_orient) {
235 case OB_ORIENTATION_HORZ:
236 dock->area.width += app->w;
237 dock->area.height = MAX(dock->area.height, app->h);
238 break;
239 case OB_ORIENTATION_VERT:
240 dock->area.width = MAX(dock->area.width, app->w);
241 dock->area.height += app->h;
242 break;
243 }
244 }
245
246 dock->area.width += l + r;
247 dock->area.height += t + b;
248
249 hspot = l;
250 vspot = t;
251
252 /* position the apps */
253 for (it = dock->dock_apps; it; it = g_list_next(it)) {
254 ObDockApp *app = it->data;
255 switch (config_dock_orient) {
256 case OB_ORIENTATION_HORZ:
257 app->x = hspot;
258 app->y = (dock->area.height - app->h) / 2;
259 hspot += app->w;
260 break;
261 case OB_ORIENTATION_VERT:
262 app->x = (dock->area.width - app->w) / 2;
263 app->y = vspot;
264 vspot += app->h;
265 break;
266 }
267
268 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
269 }
270
271 /* used for calculating offsets */
272 dock->area.width += ob_rr_theme->obwidth * 2;
273 dock->area.height += ob_rr_theme->obwidth * 2;
274
275 a = screen_physical_area_all_monitors();
276
277 /* calculate position */
278 if (config_dock_floating) {
279 dock->area.x = config_dock_x;
280 dock->area.y = config_dock_y;
281 gravity = NorthWestGravity;
282 } else {
283 switch (config_dock_pos) {
284 case OB_DIRECTION_NORTHWEST:
285 dock->area.x = 0;
286 dock->area.y = 0;
287 gravity = NorthWestGravity;
288 break;
289 case OB_DIRECTION_NORTH:
290 dock->area.x = a->width / 2;
291 dock->area.y = 0;
292 gravity = NorthGravity;
293 break;
294 case OB_DIRECTION_NORTHEAST:
295 dock->area.x = a->width;
296 dock->area.y = 0;
297 gravity = NorthEastGravity;
298 break;
299 case OB_DIRECTION_WEST:
300 dock->area.x = 0;
301 dock->area.y = a->height / 2;
302 gravity = WestGravity;
303 break;
304 case OB_DIRECTION_EAST:
305 dock->area.x = a->width;
306 dock->area.y = a->height / 2;
307 gravity = EastGravity;
308 break;
309 case OB_DIRECTION_SOUTHWEST:
310 dock->area.x = 0;
311 dock->area.y = a->height;
312 gravity = SouthWestGravity;
313 break;
314 case OB_DIRECTION_SOUTH:
315 dock->area.x = a->width / 2;
316 dock->area.y = a->height;
317 gravity = SouthGravity;
318 break;
319 case OB_DIRECTION_SOUTHEAST:
320 dock->area.x = a->width;
321 dock->area.y = a->height;
322 gravity = SouthEastGravity;
323 break;
324 default:
325 g_assert_not_reached();
326 }
327 }
328
329 switch(gravity) {
330 case NorthGravity:
331 case CenterGravity:
332 case SouthGravity:
333 dock->area.x -= dock->area.width / 2;
334 break;
335 case NorthEastGravity:
336 case EastGravity:
337 case SouthEastGravity:
338 dock->area.x -= dock->area.width;
339 break;
340 }
341 switch(gravity) {
342 case WestGravity:
343 case CenterGravity:
344 case EastGravity:
345 dock->area.y -= dock->area.height / 2;
346 break;
347 case SouthWestGravity:
348 case SouthGravity:
349 case SouthEastGravity:
350 dock->area.y -= dock->area.height;
351 break;
352 }
353
354 if (config_dock_hide && dock->hidden) {
355 if (!config_dock_floating) {
356 switch (config_dock_pos) {
357 case OB_DIRECTION_NORTHWEST:
358 switch (config_dock_orient) {
359 case OB_ORIENTATION_HORZ:
360 dock->area.y -= dock->area.height - ob_rr_theme->obwidth;
361 break;
362 case OB_ORIENTATION_VERT:
363 dock->area.x -= dock->area.width - ob_rr_theme->obwidth;
364 break;
365 }
366 break;
367 case OB_DIRECTION_NORTH:
368 dock->area.y -= dock->area.height - ob_rr_theme->obwidth;
369 break;
370 case OB_DIRECTION_NORTHEAST:
371 switch (config_dock_orient) {
372 case OB_ORIENTATION_HORZ:
373 dock->area.y -= dock->area.height - ob_rr_theme->obwidth;
374 break;
375 case OB_ORIENTATION_VERT:
376 dock->area.x += dock->area.width - ob_rr_theme->obwidth;
377 break;
378 }
379 break;
380 case OB_DIRECTION_WEST:
381 dock->area.x -= dock->area.width - ob_rr_theme->obwidth;
382 break;
383 case OB_DIRECTION_EAST:
384 dock->area.x += dock->area.width - ob_rr_theme->obwidth;
385 break;
386 case OB_DIRECTION_SOUTHWEST:
387 switch (config_dock_orient) {
388 case OB_ORIENTATION_HORZ:
389 dock->area.y += dock->area.height - ob_rr_theme->obwidth;
390 break;
391 case OB_ORIENTATION_VERT:
392 dock->area.x -= dock->area.width - ob_rr_theme->obwidth;
393 break;
394 } break;
395 case OB_DIRECTION_SOUTH:
396 dock->area.y += dock->area.height - ob_rr_theme->obwidth;
397 break;
398 case OB_DIRECTION_SOUTHEAST:
399 switch (config_dock_orient) {
400 case OB_ORIENTATION_HORZ:
401 dock->area.y += dock->area.height - ob_rr_theme->obwidth;
402 break;
403 case OB_ORIENTATION_VERT:
404 dock->area.x += dock->area.width - ob_rr_theme->obwidth;
405 break;
406 }
407 break;
408 }
409 }
410 }
411
412 if (!config_dock_floating && config_dock_hide) {
413 strw = ob_rr_theme->obwidth;
414 strh = ob_rr_theme->obwidth;
415 } else {
416 strw = dock->area.width;
417 strh = dock->area.height;
418 }
419
420 /* set the strut */
421 if (!dock->dock_apps) {
422 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
423 0, 0, 0, 0, 0, 0, 0, 0);
424 } else if (config_dock_floating || config_dock_nostrut)
425 {
426 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
427 0, 0, 0, 0, 0, 0, 0, 0);
428 } else {
429 switch (config_dock_pos) {
430 case OB_DIRECTION_NORTHWEST:
431 switch (config_dock_orient) {
432 case OB_ORIENTATION_HORZ:
433 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
434 0, 0, dock->area.x, dock->area.x + dock->area.width - 1,
435 0, 0, 0, 0);
436 break;
437 case OB_ORIENTATION_VERT:
438 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
439 dock->area.y, dock->area.y + dock->area.height - 1,
440 0, 0, 0, 0, 0, 0);
441 break;
442 }
443 break;
444 case OB_DIRECTION_NORTH:
445 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
446 0, 0, dock->area.x, dock->area.x + dock->area.width - 1,
447 0, 0, 0, 0);
448 break;
449 case OB_DIRECTION_NORTHEAST:
450 switch (config_dock_orient) {
451 case OB_ORIENTATION_HORZ:
452 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
453 0, 0, dock->area.x, dock->area.x + dock->area.width -1,
454 0, 0, 0, 0);
455 break;
456 case OB_ORIENTATION_VERT:
457 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
458 0, 0, 0, 0,
459 dock->area.y, dock->area.y + dock->area.height - 1, 0, 0);
460 break;
461 }
462 break;
463 case OB_DIRECTION_WEST:
464 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
465 dock->area.y, dock->area.y + dock->area.height - 1,
466 0, 0, 0, 0, 0, 0);
467 break;
468 case OB_DIRECTION_EAST:
469 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
470 0, 0, 0, 0,
471 dock->area.y, dock->area.y + dock->area.height - 1, 0, 0);
472 break;
473 case OB_DIRECTION_SOUTHWEST:
474 switch (config_dock_orient) {
475 case OB_ORIENTATION_HORZ:
476 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
477 0, 0, 0, 0, 0, 0,
478 dock->area.x, dock->area.x + dock->area.width - 1);
479 break;
480 case OB_ORIENTATION_VERT:
481 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
482 dock->area.y, dock->area.y + dock->area.height - 1,
483 0, 0, 0, 0, 0, 0);
484 break;
485 }
486 break;
487 case OB_DIRECTION_SOUTH:
488 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
489 0, 0, 0, 0, 0, 0,
490 dock->area.x, dock->area.x + dock->area.width - 1);
491 break;
492 case OB_DIRECTION_SOUTHEAST:
493 switch (config_dock_orient) {
494 case OB_ORIENTATION_HORZ:
495 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
496 0, 0, 0, 0, 0, 0,
497 dock->area.x, dock->area.x + dock->area.width - 1);
498 break;
499 case OB_ORIENTATION_VERT:
500 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
501 0, 0, 0, 0,
502 dock->area.y, dock->area.y + dock->area.height - 1, 0, 0);
503 break;
504 }
505 break;
506 }
507 }
508
509 /* not used for actually sizing shit */
510 dock->area.width -= ob_rr_theme->obwidth * 2;
511 dock->area.height -= ob_rr_theme->obwidth * 2;
512
513 if (dock->dock_apps) {
514 g_assert(dock->area.width > 0);
515 g_assert(dock->area.height > 0);
516
517 XMoveResizeWindow(ob_display, dock->frame,
518 dock->area.x, dock->area.y, dock->area.width, dock->area.height);
519
520 RrPaint(dock->a_frame, dock->frame, dock->area.width, dock->area.height);
521 XMapWindow(ob_display, dock->frame);
522 } else
523 XUnmapWindow(ob_display, dock->frame);
524
525 /* but they are useful outside of this function! */
526 dock->area.width += ob_rr_theme->obwidth * 2;
527 dock->area.height += ob_rr_theme->obwidth * 2;
528
529 screen_update_areas();
530
531 g_free(a);
532 }
533
534 void dock_app_configure(ObDockApp *app, gint w, gint h)
535 {
536 app->w = w;
537 app->h = h;
538 dock_configure();
539 }
540
541 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
542 {
543 ObDockApp *over = NULL;
544 GList *it;
545 gint x, y;
546 gboolean after;
547 gboolean stop;
548
549 x = e->x_root;
550 y = e->y_root;
551
552 /* are we on top of the dock? */
553 if (!(x >= dock->area.x &&
554 y >= dock->area.y &&
555 x < dock->area.x + dock->area.width &&
556 y < dock->area.y + dock->area.height))
557 return;
558
559 x -= dock->area.x;
560 y -= dock->area.y;
561
562 /* which dock app are we on top of? */
563 stop = FALSE;
564 for (it = dock->dock_apps; it; it = g_list_next(it)) {
565 over = it->data;
566 switch (config_dock_orient) {
567 case OB_ORIENTATION_HORZ:
568 if (x >= over->x && x < over->x + over->w)
569 stop = TRUE;
570 break;
571 case OB_ORIENTATION_VERT:
572 if (y >= over->y && y < over->y + over->h)
573 stop = TRUE;
574 break;
575 }
576 /* dont go to it->next! */
577 if (stop) break;
578 }
579 if (!it || app == over) return;
580
581 x -= over->x;
582 y -= over->y;
583
584 switch (config_dock_orient) {
585 case OB_ORIENTATION_HORZ:
586 after = (x > over->w / 2);
587 break;
588 case OB_ORIENTATION_VERT:
589 after = (y > over->h / 2);
590 break;
591 default:
592 g_assert_not_reached();
593 }
594
595 /* remove before doing the it->next! */
596 dock->dock_apps = g_list_remove(dock->dock_apps, app);
597
598 if (after) it = it->next;
599
600 dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
601 dock_configure();
602 }
603
604 static gboolean hide_timeout(gpointer data)
605 {
606 /* hide */
607 dock->hidden = TRUE;
608 dock_configure();
609
610 return FALSE; /* don't repeat */
611 }
612
613 static gboolean show_timeout(gpointer data)
614 {
615 /* hide */
616 dock->hidden = FALSE;
617 dock_configure();
618
619 return FALSE; /* don't repeat */
620 }
621
622 void dock_hide(gboolean hide)
623 {
624 if (!hide) {
625 if (dock->hidden && config_dock_hide) {
626 ob_main_loop_timeout_add(ob_main_loop, config_dock_show_delay,
627 show_timeout, NULL, g_direct_equal, NULL);
628 } else if (!dock->hidden && config_dock_hide) {
629 ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
630 }
631 } else {
632 if (!dock->hidden && config_dock_hide) {
633 ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_delay,
634 hide_timeout, NULL, g_direct_equal, NULL);
635 } else if (dock->hidden && config_dock_hide) {
636 ob_main_loop_timeout_remove(ob_main_loop, show_timeout);
637 }
638 }
639 }
640
641 void dock_get_area(Rect *a)
642 {
643 RECT_SET(*a, dock->area.x, dock->area.y, dock->area.width, dock->area.height);
644 }
This page took 0.063619 seconds and 4 git commands to generate.