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 "screen.h"
23 #include "config.h"
24 #include "grab.h"
25 #include "openbox.h"
26 #include "obrender/theme.h"
27 #include "obt/prop.h"
28
29 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
30 EnterWindowMask | LeaveWindowMask)
31 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
32 #define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
33 ButtonMotionMask)
34
35 static ObDock *dock;
36 static guint show_timeout_id;
37 static guint hide_timeout_id;
38
39 StrutPartial dock_strut;
40
dock_app_grab_button(ObDockApp * app,gboolean grab)41 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
42 {
43 if (grab) {
44 grab_button_full(config_dock_app_move_button,
45 config_dock_app_move_modifiers, app->icon_win,
46 ButtonPressMask | ButtonReleaseMask |
47 ButtonMotionMask,
48 GrabModeAsync, OB_CURSOR_MOVE);
49 } else {
50 ungrab_button(config_dock_app_move_button,
51 config_dock_app_move_modifiers, app->icon_win);
52 }
53 }
54
window_hash(Window * w)55 static guint window_hash(Window *w) { return *w; }
window_comp(Window * w1,Window * w2)56 static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
57
dock_startup(gboolean reconfig)58 void dock_startup(gboolean reconfig)
59 {
60 XSetWindowAttributes attrib;
61
62 if (reconfig) {
63 GList *it;
64
65 XSetWindowBorder(obt_display, dock->frame,
66 RrColorPixel(ob_rr_theme->osd_border_color));
67 XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
68
69 RrAppearanceFree(dock->a_frame);
70 dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg);
71
72 stacking_add(DOCK_AS_WINDOW(dock));
73
74 dock_configure();
75 dock_hide(TRUE);
76
77 for (it = dock->dock_apps; it; it = g_list_next(it))
78 dock_app_grab_button(it->data, TRUE);
79 return;
80 }
81
82 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0);
84
85 dock = g_slice_new0(ObDock);
86 dock->obwin.type = OB_WINDOW_CLASS_DOCK;
87
88 dock->hidden = TRUE;
89
90 dock->dock_map = g_hash_table_new((GHashFunc)window_hash,
91 (GEqualFunc)window_comp);
92
93 attrib.event_mask = DOCK_EVENT_MASK;
94 attrib.override_redirect = True;
95 attrib.do_not_propagate_mask = DOCK_NOPROPAGATEMASK;
96 dock->frame = XCreateWindow(obt_display, obt_root(ob_screen),
97 0, 0, 1, 1, 0,
98 RrDepth(ob_rr_inst), InputOutput,
99 RrVisual(ob_rr_inst),
100 CWOverrideRedirect | CWEventMask |
101 CWDontPropagate,
102 &attrib);
103 dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg);
104 XSetWindowBorder(obt_display, dock->frame,
105 RrColorPixel(ob_rr_theme->osd_border_color));
106 XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
107
108 /* Setting the window type so xcompmgr can tell what it is */
109 OBT_PROP_SET32(dock->frame, NET_WM_WINDOW_TYPE, ATOM,
110 OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK));
111
112 window_add(&dock->frame, DOCK_AS_WINDOW(dock));
113 stacking_add(DOCK_AS_WINDOW(dock));
114 }
115
dock_shutdown(gboolean reconfig)116 void dock_shutdown(gboolean reconfig)
117 {
118 if (reconfig) {
119 GList *it;
120
121 stacking_remove(DOCK_AS_WINDOW(dock));
122
123 for (it = dock->dock_apps; it; it = g_list_next(it))
124 dock_app_grab_button(it->data, FALSE);
125 return;
126 }
127
128 g_hash_table_destroy(dock->dock_map);
129
130 XDestroyWindow(obt_display, dock->frame);
131 RrAppearanceFree(dock->a_frame);
132 window_remove(dock->frame);
133 stacking_remove(dock);
134 g_slice_free(ObDock, dock);
135 dock = NULL;
136 }
137
dock_manage(Window icon_win,Window name_win)138 void dock_manage(Window icon_win, Window name_win)
139 {
140 ObDockApp *app;
141 XWindowAttributes attrib;
142 gchar **data;
143
144 app = g_slice_new0(ObDockApp);
145 app->name_win = name_win;
146 app->icon_win = icon_win;
147
148 if (OBT_PROP_GETSS_TYPE(app->name_win, WM_CLASS, STRING_NO_CC, &data)) {
149 if (data[0]) {
150 app->name = g_strdup(data[0]);
151 if (data[1])
152 app->class = g_strdup(data[1]);
153 }
154 g_strfreev(data);
155 }
156
157 if (app->name == NULL) app->name = g_strdup("");
158 if (app->class == NULL) app->class = g_strdup("");
159
160 if (XGetWindowAttributes(obt_display, app->icon_win, &attrib)) {
161 app->w = attrib.width;
162 app->h = attrib.height;
163 } else {
164 app->w = app->h = 64;
165 }
166
167 dock->dock_apps = g_list_append(dock->dock_apps, app);
168 g_hash_table_insert(dock->dock_map, &app->icon_win, app);
169 dock_configure();
170
171 XReparentWindow(obt_display, app->icon_win, dock->frame, app->x, app->y);
172 /*
173 This is the same case as in frame.c for client windows. When Openbox is
174 starting, the window is already mapped so we see unmap events occur for
175 it. There are 2 unmap events generated that we see, one with the 'event'
176 member set the root window, and one set to the client, but both get
177 handled and need to be ignored.
178 */
179 if (ob_state() == OB_STATE_STARTING)
180 app->ignore_unmaps += 2;
181 XChangeSaveSet(obt_display, app->icon_win, SetModeInsert);
182 XMapWindow(obt_display, app->icon_win);
183
184 if (app->name_win != app->icon_win) {
185 XReparentWindow(obt_display, app->name_win, dock->frame, -1000, -1000);
186 XChangeSaveSet(obt_display, app->name_win, SetModeInsert);
187 XMapWindow(obt_display, app->name_win);
188 }
189
190 XSync(obt_display, False);
191
192 XSelectInput(obt_display, app->icon_win, DOCKAPP_EVENT_MASK);
193
194 dock_app_grab_button(app, TRUE);
195
196 ob_debug("Managed Dock App: 0x%lx 0x%lx (%s)",
197 app->icon_win, app->name_win, app->class);
198
199 grab_server(FALSE);
200 }
201
dock_unmanage_all(void)202 void dock_unmanage_all(void)
203 {
204 while (dock->dock_apps)
205 dock_unmanage(dock->dock_apps->data, TRUE);
206 }
207
dock_unmanage(ObDockApp * app,gboolean reparent)208 void dock_unmanage(ObDockApp *app, gboolean reparent)
209 {
210 dock_app_grab_button(app, FALSE);
211 XSelectInput(obt_display, app->icon_win, NoEventMask);
212 /* remove the window from our save set */
213 XChangeSaveSet(obt_display, app->icon_win, SetModeDelete);
214 XSync(obt_display, False);
215
216 if (reparent) {
217 XReparentWindow(obt_display, app->icon_win, obt_root(ob_screen), 0, 0);
218 if (app->name_win != app->icon_win)
219 XReparentWindow(obt_display, app->name_win,
220 obt_root(ob_screen), 0, 0);
221 }
222
223 dock->dock_apps = g_list_remove(dock->dock_apps, app);
224 g_hash_table_remove(dock->dock_map, &app->icon_win);
225 dock_configure();
226
227 ob_debug("Unmanaged Dock App: 0x%lx (%s)", app->icon_win, app->class);
228
229 g_free(app->name);
230 g_free(app->class);
231 g_slice_free(ObDockApp, app);
232 }
233
dock_configure(void)234 void dock_configure(void)
235 {
236 GList *it;
237 gint hspot, vspot;
238 gint gravity;
239 gint l, r, t, b;
240 gint strw, strh;
241 const Rect *a;
242 gint hidesize;
243
244 RrMargins(dock->a_frame, &l, &t, &r, &b);
245 hidesize = MAX(1, ob_rr_theme->obwidth);
246
247 dock->area.width = dock->area.height = 0;
248
249 /* get the size */
250 for (it = dock->dock_apps; it; it = g_list_next(it)) {
251 ObDockApp *app = it->data;
252 switch (config_dock_orient) {
253 case OB_ORIENTATION_HORZ:
254 dock->area.width += app->w;
255 dock->area.height = MAX(dock->area.height, app->h);
256 break;
257 case OB_ORIENTATION_VERT:
258 dock->area.width = MAX(dock->area.width, app->w);
259 dock->area.height += app->h;
260 break;
261 }
262 }
263
264 if (dock->dock_apps) {
265 dock->area.width += l + r;
266 dock->area.height += t + b;
267 }
268
269 hspot = l;
270 vspot = t;
271
272 /* position the apps */
273 for (it = dock->dock_apps; it; it = g_list_next(it)) {
274 ObDockApp *app = it->data;
275 switch (config_dock_orient) {
276 case OB_ORIENTATION_HORZ:
277 app->x = hspot;
278 app->y = (dock->area.height - app->h) / 2;
279 hspot += app->w;
280 break;
281 case OB_ORIENTATION_VERT:
282 app->x = (dock->area.width - app->w) / 2;
283 app->y = vspot;
284 vspot += app->h;
285 break;
286 }
287
288 XMoveWindow(obt_display, app->icon_win, app->x, app->y);
289 }
290
291 /* used for calculating offsets */
292 dock->area.width += ob_rr_theme->obwidth * 2;
293 dock->area.height += ob_rr_theme->obwidth * 2;
294
295 a = screen_physical_area_all_monitors();
296
297 /* calculate position */
298 if (config_dock_floating) {
299 dock->area.x = config_dock_x;
300 dock->area.y = config_dock_y;
301 gravity = NorthWestGravity;
302 } else {
303 switch (config_dock_pos) {
304 case OB_DIRECTION_NORTHWEST:
305 dock->area.x = 0;
306 dock->area.y = 0;
307 gravity = NorthWestGravity;
308 break;
309 case OB_DIRECTION_NORTH:
310 dock->area.x = a->width / 2;
311 dock->area.y = 0;
312 gravity = NorthGravity;
313 break;
314 case OB_DIRECTION_NORTHEAST:
315 dock->area.x = a->width;
316 dock->area.y = 0;
317 gravity = NorthEastGravity;
318 break;
319 case OB_DIRECTION_WEST:
320 dock->area.x = 0;
321 dock->area.y = a->height / 2;
322 gravity = WestGravity;
323 break;
324 case OB_DIRECTION_EAST:
325 dock->area.x = a->width;
326 dock->area.y = a->height / 2;
327 gravity = EastGravity;
328 break;
329 case OB_DIRECTION_SOUTHWEST:
330 dock->area.x = 0;
331 dock->area.y = a->height;
332 gravity = SouthWestGravity;
333 break;
334 case OB_DIRECTION_SOUTH:
335 dock->area.x = a->width / 2;
336 dock->area.y = a->height;
337 gravity = SouthGravity;
338 break;
339 case OB_DIRECTION_SOUTHEAST:
340 dock->area.x = a->width;
341 dock->area.y = a->height;
342 gravity = SouthEastGravity;
343 break;
344 default:
345 g_assert_not_reached();
346 }
347 }
348
349 switch(gravity) {
350 case NorthGravity:
351 case CenterGravity:
352 case SouthGravity:
353 dock->area.x -= dock->area.width / 2;
354 break;
355 case NorthEastGravity:
356 case EastGravity:
357 case SouthEastGravity:
358 dock->area.x -= dock->area.width;
359 break;
360 }
361 switch(gravity) {
362 case WestGravity:
363 case CenterGravity:
364 case EastGravity:
365 dock->area.y -= dock->area.height / 2;
366 break;
367 case SouthWestGravity:
368 case SouthGravity:
369 case SouthEastGravity:
370 dock->area.y -= dock->area.height;
371 break;
372 }
373
374 if (config_dock_hide && dock->hidden) {
375 if (!config_dock_floating) {
376 switch (config_dock_pos) {
377 case OB_DIRECTION_NORTHWEST:
378 switch (config_dock_orient) {
379 case OB_ORIENTATION_HORZ:
380 dock->area.y -= dock->area.height - hidesize;
381 break;
382 case OB_ORIENTATION_VERT:
383 dock->area.x -= dock->area.width - hidesize;
384 break;
385 }
386 break;
387 case OB_DIRECTION_NORTH:
388 dock->area.y -= dock->area.height - hidesize;
389 break;
390 case OB_DIRECTION_NORTHEAST:
391 switch (config_dock_orient) {
392 case OB_ORIENTATION_HORZ:
393 dock->area.y -= dock->area.height - hidesize;
394 break;
395 case OB_ORIENTATION_VERT:
396 dock->area.x += dock->area.width - hidesize;
397 break;
398 }
399 break;
400 case OB_DIRECTION_WEST:
401 dock->area.x -= dock->area.width - hidesize;
402 break;
403 case OB_DIRECTION_EAST:
404 dock->area.x += dock->area.width - hidesize;
405 break;
406 case OB_DIRECTION_SOUTHWEST:
407 switch (config_dock_orient) {
408 case OB_ORIENTATION_HORZ:
409 dock->area.y += dock->area.height - hidesize;
410 break;
411 case OB_ORIENTATION_VERT:
412 dock->area.x -= dock->area.width - hidesize;
413 break;
414 } break;
415 case OB_DIRECTION_SOUTH:
416 dock->area.y += dock->area.height - hidesize;
417 break;
418 case OB_DIRECTION_SOUTHEAST:
419 switch (config_dock_orient) {
420 case OB_ORIENTATION_HORZ:
421 dock->area.y += dock->area.height - hidesize;
422 break;
423 case OB_ORIENTATION_VERT:
424 dock->area.x += dock->area.width - hidesize;
425 break;
426 }
427 break;
428 }
429 }
430 }
431
432 if (!config_dock_floating && config_dock_hide) {
433 strw = hidesize;
434 strh = hidesize;
435 } else {
436 strw = dock->area.width;
437 strh = dock->area.height;
438 }
439
440 /* set the strut */
441 if (!dock->dock_apps) {
442 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
443 0, 0, 0, 0, 0, 0, 0, 0);
444 }
445 else if (config_dock_floating || config_dock_nostrut) {
446 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
447 0, 0, 0, 0, 0, 0, 0, 0);
448 }
449 else {
450 switch (config_dock_pos) {
451 case OB_DIRECTION_NORTHWEST:
452 switch (config_dock_orient) {
453 case OB_ORIENTATION_HORZ:
454 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
455 0, 0, dock->area.x, dock->area.x
456 + dock->area.width - 1, 0, 0, 0, 0);
457 break;
458 case OB_ORIENTATION_VERT:
459 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
460 dock->area.y, dock->area.y
461 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
462 break;
463 }
464 break;
465 case OB_DIRECTION_NORTH:
466 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
467 0, 0, dock->area.x, dock->area.x
468 + dock->area.width - 1, 0, 0, 0, 0);
469 break;
470 case OB_DIRECTION_NORTHEAST:
471 switch (config_dock_orient) {
472 case OB_ORIENTATION_HORZ:
473 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
474 0, 0, dock->area.x, dock->area.x
475 + dock->area.width -1, 0, 0, 0, 0);
476 break;
477 case OB_ORIENTATION_VERT:
478 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
479 0, 0, 0, 0, dock->area.y, dock->area.y
480 + dock->area.height - 1, 0, 0);
481 break;
482 }
483 break;
484 case OB_DIRECTION_WEST:
485 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
486 dock->area.y, dock->area.y
487 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
488 break;
489 case OB_DIRECTION_EAST:
490 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
491 0, 0, 0, 0, dock->area.y, dock->area.y
492 + dock->area.height - 1, 0, 0);
493 break;
494 case OB_DIRECTION_SOUTHWEST:
495 switch (config_dock_orient) {
496 case OB_ORIENTATION_HORZ:
497 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
498 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
499 + dock->area.width - 1);
500 break;
501 case OB_ORIENTATION_VERT:
502 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
503 dock->area.y, dock->area.y
504 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
505 break;
506 }
507 break;
508 case OB_DIRECTION_SOUTH:
509 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
510 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
511 + dock->area.width - 1);
512 break;
513 case OB_DIRECTION_SOUTHEAST:
514 switch (config_dock_orient) {
515 case OB_ORIENTATION_HORZ:
516 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
517 0, 0, 0, 0, 0, 0, dock->area.x,
518 dock->area.x + dock->area.width - 1);
519 break;
520 case OB_ORIENTATION_VERT:
521 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
522 0, 0, 0, 0, dock->area.y, dock->area.y
523 + dock->area.height - 1, 0, 0);
524 break;
525 }
526 break;
527 }
528 }
529
530 /* not used for actually sizing shit */
531 dock->area.width -= ob_rr_theme->obwidth * 2;
532 dock->area.height -= ob_rr_theme->obwidth * 2;
533
534 if (dock->dock_apps) {
535 g_assert(dock->area.width > 0);
536 g_assert(dock->area.height > 0);
537
538 XMoveResizeWindow(obt_display, dock->frame, dock->area.x, dock->area.y,
539 dock->area.width, dock->area.height);
540
541 RrPaint(dock->a_frame, dock->frame, dock->area.width,
542 dock->area.height);
543 XMapWindow(obt_display, dock->frame);
544 } else
545 XUnmapWindow(obt_display, dock->frame);
546
547 /* but they are useful outside of this function! but don't add it if the
548 dock is actually not visible */
549 if (dock->dock_apps) {
550 dock->area.width += ob_rr_theme->obwidth * 2;
551 dock->area.height += ob_rr_theme->obwidth * 2;
552 }
553
554 /* screen_resize() depends on this function to call screen_update_areas(),
555 so if this changes, also update screen_resize(). */
556 screen_update_areas();
557 }
558
dock_app_configure(ObDockApp * app,gint w,gint h)559 void dock_app_configure(ObDockApp *app, gint w, gint h)
560 {
561 app->w = w;
562 app->h = h;
563 dock_configure();
564 }
565
dock_app_drag(ObDockApp * app,XMotionEvent * e)566 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
567 {
568 ObDockApp *over = NULL;
569 GList *it;
570 gint x, y;
571 gboolean after;
572 gboolean stop;
573
574 x = e->x_root;
575 y = e->y_root;
576
577 /* are we on top of the dock? */
578 if (!(x >= dock->area.x &&
579 y >= dock->area.y &&
580 x < dock->area.x + dock->area.width &&
581 y < dock->area.y + dock->area.height))
582 return;
583
584 x -= dock->area.x;
585 y -= dock->area.y;
586
587 /* which dock app are we on top of? */
588 stop = FALSE;
589 for (it = dock->dock_apps; it; it = g_list_next(it)) {
590 over = it->data;
591 switch (config_dock_orient) {
592 case OB_ORIENTATION_HORZ:
593 if (x >= over->x && x < over->x + over->w)
594 stop = TRUE;
595 break;
596 case OB_ORIENTATION_VERT:
597 if (y >= over->y && y < over->y + over->h)
598 stop = TRUE;
599 break;
600 }
601 /* dont go to it->next! */
602 if (stop) break;
603 }
604 if (!it || app == over) return;
605
606 x -= over->x;
607 y -= over->y;
608
609 switch (config_dock_orient) {
610 case OB_ORIENTATION_HORZ:
611 after = (x > over->w / 2);
612 break;
613 case OB_ORIENTATION_VERT:
614 after = (y > over->h / 2);
615 break;
616 default:
617 g_assert_not_reached();
618 }
619
620 /* remove before doing the it->next! */
621 dock->dock_apps = g_list_remove(dock->dock_apps, app);
622
623 if (after) it = it->next;
624
625 dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
626 dock_configure();
627 }
628
hide_timeout(gpointer data)629 static gboolean hide_timeout(gpointer data)
630 {
631 /* hide */
632 dock->hidden = TRUE;
633 dock_configure();
634
635 return FALSE; /* don't repeat */
636 }
637
show_timeout(gpointer data)638 static gboolean show_timeout(gpointer data)
639 {
640 /* show */
641 dock->hidden = FALSE;
642 dock_configure();
643
644 return FALSE; /* don't repeat */
645 }
646
destroy_timeout(gpointer data)647 static void destroy_timeout(gpointer data)
648 {
649 gint *id = data;
650 *id = 0;
651 }
652
dock_hide(gboolean hide)653 void dock_hide(gboolean hide)
654 {
655 if (!hide) {
656 if (dock->hidden && config_dock_hide) {
657 show_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
658 config_dock_show_delay,
659 show_timeout, &show_timeout_id, destroy_timeout);
660 } else if (!dock->hidden && config_dock_hide && hide_timeout_id) {
661 if (hide_timeout_id) g_source_remove(hide_timeout_id);
662 }
663 } else {
664 if (!dock->hidden && config_dock_hide) {
665 hide_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
666 config_dock_hide_delay,
667 hide_timeout, &hide_timeout_id, destroy_timeout);
668 } else if (dock->hidden && config_dock_hide && show_timeout_id) {
669 if (show_timeout_id) g_source_remove(show_timeout_id);
670 }
671 }
672 }
673
dock_get_area(Rect * a)674 void dock_get_area(Rect *a)
675 {
676 RECT_SET(*a, dock->area.x, dock->area.y,
677 dock->area.width, dock->area.height);
678 }
679
dock_raise_dock(void)680 void dock_raise_dock(void)
681 {
682 stacking_raise(DOCK_AS_WINDOW(dock));
683 }
684
dock_lower_dock(void)685 void dock_lower_dock(void)
686 {
687 stacking_lower(DOCK_AS_WINDOW(dock));
688 }
689
dock_find_dockapp(Window xwin)690 ObDockApp* dock_find_dockapp(Window xwin)
691 {
692 return g_hash_table_lookup(dock->dock_map, &xwin);
693 }
694