]> Dogcows Code - chaz/openbox/blob - openbox/dock.c
support for _NET_WM_USER_TIME_WINDOW round 2 ! yay abstraction
[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->frame_b_color));
61 XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->fbwidth);
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->frame_b_color));
95 XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->fbwidth);
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->fbwidth * 2;
265 dock->h += ob_rr_theme->fbwidth * 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 default:
317 g_assert_not_reached();
318 }
319 }
320
321 switch(gravity) {
322 case NorthGravity:
323 case CenterGravity:
324 case SouthGravity:
325 dock->x -= dock->w / 2;
326 break;
327 case NorthEastGravity:
328 case EastGravity:
329 case SouthEastGravity:
330 dock->x -= dock->w;
331 break;
332 }
333 switch(gravity) {
334 case WestGravity:
335 case CenterGravity:
336 case EastGravity:
337 dock->y -= dock->h / 2;
338 break;
339 case SouthWestGravity:
340 case SouthGravity:
341 case SouthEastGravity:
342 dock->y -= dock->h;
343 break;
344 }
345
346 if (config_dock_hide && dock->hidden) {
347 if (!config_dock_floating) {
348 switch (config_dock_pos) {
349 case OB_DIRECTION_NORTHWEST:
350 switch (config_dock_orient) {
351 case OB_ORIENTATION_HORZ:
352 dock->y -= dock->h - ob_rr_theme->fbwidth;
353 break;
354 case OB_ORIENTATION_VERT:
355 dock->x -= dock->w - ob_rr_theme->fbwidth;
356 break;
357 }
358 break;
359 case OB_DIRECTION_NORTH:
360 dock->y -= dock->h - ob_rr_theme->fbwidth;
361 break;
362 case OB_DIRECTION_NORTHEAST:
363 switch (config_dock_orient) {
364 case OB_ORIENTATION_HORZ:
365 dock->y -= dock->h - ob_rr_theme->fbwidth;
366 break;
367 case OB_ORIENTATION_VERT:
368 dock->x += dock->w - ob_rr_theme->fbwidth;
369 break;
370 }
371 break;
372 case OB_DIRECTION_WEST:
373 dock->x -= dock->w - ob_rr_theme->fbwidth;
374 break;
375 case OB_DIRECTION_EAST:
376 dock->x += dock->w - ob_rr_theme->fbwidth;
377 break;
378 case OB_DIRECTION_SOUTHWEST:
379 switch (config_dock_orient) {
380 case OB_ORIENTATION_HORZ:
381 dock->y += dock->h - ob_rr_theme->fbwidth;
382 break;
383 case OB_ORIENTATION_VERT:
384 dock->x -= dock->w - ob_rr_theme->fbwidth;
385 break;
386 } break;
387 case OB_DIRECTION_SOUTH:
388 dock->y += dock->h - ob_rr_theme->fbwidth;
389 break;
390 case OB_DIRECTION_SOUTHEAST:
391 switch (config_dock_orient) {
392 case OB_ORIENTATION_HORZ:
393 dock->y += dock->h - ob_rr_theme->fbwidth;
394 break;
395 case OB_ORIENTATION_VERT:
396 dock->x += dock->w - ob_rr_theme->fbwidth;
397 break;
398 }
399 break;
400 }
401 }
402 }
403
404 if (!config_dock_floating && config_dock_hide) {
405 strw = ob_rr_theme->fbwidth;
406 strh = ob_rr_theme->fbwidth;
407 } else {
408 strw = dock->w;
409 strh = dock->h;
410 }
411
412 /* set the strut */
413 if (!dock->dock_apps) {
414 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
415 0, 0, 0, 0, 0, 0, 0, 0);
416 } else if (config_dock_floating || config_dock_nostrut) {
417 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
418 0, 0, 0, 0, 0, 0, 0, 0);
419 } else {
420 switch (config_dock_pos) {
421 case OB_DIRECTION_NORTHWEST:
422 switch (config_dock_orient) {
423 case OB_ORIENTATION_HORZ:
424 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
425 0, 0, dock->x, dock->x + dock->w - 1,
426 0, 0, 0, 0);
427 break;
428 case OB_ORIENTATION_VERT:
429 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
430 dock->y, dock->y + dock->h - 1,
431 0, 0, 0, 0, 0, 0);
432 break;
433 }
434 break;
435 case OB_DIRECTION_NORTH:
436 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
437 dock->x, dock->x + dock->w - 1,
438 0, 0, 0, 0, 0, 0);
439 break;
440 case OB_DIRECTION_NORTHEAST:
441 switch (config_dock_orient) {
442 case OB_ORIENTATION_HORZ:
443 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
444 0, 0, dock->x, dock->x + dock->w -1,
445 0, 0, 0, 0);
446 break;
447 case OB_ORIENTATION_VERT:
448 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
449 0, 0, 0, 0,
450 dock->y, dock->y + dock->h - 1, 0, 0);
451 break;
452 }
453 break;
454 case OB_DIRECTION_WEST:
455 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
456 dock->y, dock->y + dock->h - 1,
457 0, 0, 0, 0, 0, 0);
458 break;
459 case OB_DIRECTION_EAST:
460 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
461 0, 0, 0, 0,
462 dock->y, dock->y + dock->h - 1, 0, 0);
463 break;
464 case OB_DIRECTION_SOUTHWEST:
465 switch (config_dock_orient) {
466 case OB_ORIENTATION_HORZ:
467 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
468 0, 0, 0, 0, 0, 0,
469 dock->x, dock->x + dock->w - 1);
470 break;
471 case OB_ORIENTATION_VERT:
472 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
473 dock->y, dock->y + dock->h - 1,
474 0, 0, 0, 0, 0, 0);
475 break;
476 }
477 break;
478 case OB_DIRECTION_SOUTH:
479 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
480 0, 0, 0, 0, 0, 0,
481 dock->x, dock->x + dock->w - 1);
482 break;
483 case OB_DIRECTION_SOUTHEAST:
484 switch (config_dock_orient) {
485 case OB_ORIENTATION_HORZ:
486 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
487 0, 0, 0, 0, 0, 0,
488 dock->x, dock->x + dock->w - 1);
489 break;
490 case OB_ORIENTATION_VERT:
491 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
492 0, 0, 0, 0,
493 dock->y, dock->y + dock->h - 1, 0, 0);
494 break;
495 }
496 break;
497 }
498 }
499
500 dock->w += minw;
501 dock->h += minh;
502
503 /* not used for actually sizing shit */
504 dock->w -= ob_rr_theme->fbwidth * 2;
505 dock->h -= ob_rr_theme->fbwidth * 2;
506
507 if (dock->dock_apps) {
508 g_assert(dock->w > 0);
509 g_assert(dock->h > 0);
510
511 XMoveResizeWindow(ob_display, dock->frame,
512 dock->x, dock->y, dock->w, dock->h);
513
514 RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
515 XMapWindow(ob_display, dock->frame);
516 } else
517 XUnmapWindow(ob_display, dock->frame);
518
519 /* but they are useful outside of this function! */
520 dock->w += ob_rr_theme->fbwidth * 2;
521 dock->h += ob_rr_theme->fbwidth * 2;
522
523 screen_update_areas();
524 }
525
526 void dock_app_configure(ObDockApp *app, gint w, gint h)
527 {
528 app->w = w;
529 app->h = h;
530 dock_configure();
531 }
532
533 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
534 {
535 ObDockApp *over = NULL;
536 GList *it;
537 gint x, y;
538 gboolean after;
539 gboolean stop;
540
541 x = e->x_root;
542 y = e->y_root;
543
544 /* are we on top of the dock? */
545 if (!(x >= dock->x &&
546 y >= dock->y &&
547 x < dock->x + dock->w &&
548 y < dock->y + dock->h))
549 return;
550
551 x -= dock->x;
552 y -= dock->y;
553
554 /* which dock app are we on top of? */
555 stop = FALSE;
556 for (it = dock->dock_apps; it; it = g_list_next(it)) {
557 over = it->data;
558 switch (config_dock_orient) {
559 case OB_ORIENTATION_HORZ:
560 if (x >= over->x && x < over->x + over->w)
561 stop = TRUE;
562 break;
563 case OB_ORIENTATION_VERT:
564 if (y >= over->y && y < over->y + over->h)
565 stop = TRUE;
566 break;
567 }
568 /* dont go to it->next! */
569 if (stop) break;
570 }
571 if (!it || app == over) return;
572
573 x -= over->x;
574 y -= over->y;
575
576 switch (config_dock_orient) {
577 case OB_ORIENTATION_HORZ:
578 after = (x > over->w / 2);
579 break;
580 case OB_ORIENTATION_VERT:
581 after = (y > over->h / 2);
582 break;
583 default:
584 g_assert_not_reached();
585 }
586
587 /* remove before doing the it->next! */
588 dock->dock_apps = g_list_remove(dock->dock_apps, app);
589
590 if (after) it = it->next;
591
592 dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
593 dock_configure();
594 }
595
596 static gboolean hide_timeout(gpointer data)
597 {
598 /* hide */
599 dock->hidden = TRUE;
600 dock_configure();
601
602 return FALSE; /* don't repeat */
603 }
604
605 static gboolean show_timeout(gpointer data)
606 {
607 /* hide */
608 dock->hidden = FALSE;
609 dock_configure();
610
611 return FALSE; /* don't repeat */
612 }
613
614 void dock_hide(gboolean hide)
615 {
616 if (!hide) {
617 if (dock->hidden && config_dock_hide) {
618 ob_main_loop_timeout_add(ob_main_loop, config_dock_show_delay,
619 show_timeout, NULL, g_direct_equal, NULL);
620 } else if (!dock->hidden && config_dock_hide) {
621 ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
622 }
623 } else {
624 if (!dock->hidden && config_dock_hide) {
625 ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_delay,
626 hide_timeout, NULL, g_direct_equal, NULL);
627 } else if (dock->hidden && config_dock_hide) {
628 ob_main_loop_timeout_remove(ob_main_loop, show_timeout);
629 }
630 }
631 }
This page took 0.068499 seconds and 4 git commands to generate.