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