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