1 /*
2 * Copyright �� 2006 Novell, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 *
19 * Author: David Reveman <davidr@novell.com>
20 *
21 * 2D Mode: Copyright �� 2010 Sam Spilsbury <smspillaz@gmail.com>
22 * Frames Management: Copright �� 2011 Canonical Ltd.
23 * Authored By: Sam Spilsbury <sam.spilsbury@canonical.com>
24 */
25
26 #include "gtk-window-decorator.h"
27 #include "local-menus.h"
28
29 const gchar *
30 get_frame_type (WnckWindow *win)
31 {
32 WnckWindowType wnck_type = wnck_window_get_window_type (win);
33
34 switch (wnck_type)
35 {
36 case WNCK_WINDOW_NORMAL:
37 return "normal";
38 case WNCK_WINDOW_DIALOG:
39 {
40 Atom actual;
41 int result, format;
42 unsigned long n, left;
43 unsigned char *data;
44
45 result = XGetWindowProperty (gdk_x11_get_default_xdisplay (), wnck_window_get_xid (win),
46 net_wm_state_atom,
47 0L, 1024L, FALSE, XA_ATOM, &actual, &format,
48 &n, &left, &data);
49
50 if (result == Success && data)
51 {
52 Atom *a = (Atom *) data;
53
54 while (n--)
55 if (*a++ == net_wm_state_modal_atom)
56 {
57 XFree ((void *) data);
58 return "modal_dialog";
59 }
60
61
62 }
63
64 return "dialog";
65 }
66 case WNCK_WINDOW_MENU:
67 return "menu";
68 case WNCK_WINDOW_UTILITY:
69 return "utility";
70 default:
71 return "bare";
72 }
73
74 return "normal";
75 }
76
77 static void
78 window_name_changed (WnckWindow *win)
79 {
80 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
81
82 if (d->decorated)
83 {
84 if (!update_window_decoration_size (win))
85 queue_decor_draw (d);
86 }
87 }
88
89 static void
90 window_geometry_changed (WnckWindow *win)
91 {
92 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
93
94 if (d->decorated)
95 {
96 int width, height;
97
98 wnck_window_get_client_window_geometry (win, NULL, NULL,
99 &width, &height);
100
101 if (width != d->client_width || height != d->client_height)
102 {
103 d->client_width = width;
104 d->client_height = height;
105
106 update_window_decoration_size (win);
107 update_event_windows (win);
108 }
109 }
110 }
111
112 static void
113 window_icon_changed (WnckWindow *win)
114 {
115 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
116
117 if (d->decorated)
118 {
119 update_window_decoration_icon (win);
120 queue_decor_draw (d);
121 }
122 }
123
124 static void
125 window_state_changed (WnckWindow *win)
126 {
127 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
128
129 if (d->decorated)
130 {
131 update_window_decoration_state (win);
132 if (!update_window_decoration_size (win))
133 queue_decor_draw (d);
134
135 update_event_windows (win);
136 }
137 }
138
139 static void
140 window_actions_changed (WnckWindow *win)
141 {
142 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
143
144 if (d->decorated)
145 {
146 update_window_decoration_actions (win);
147 if (!update_window_decoration_size (win))
148 queue_decor_draw (d);
149
150 update_event_windows (win);
151 }
152 }
153
154 void
155 update_frames_border_extents (gpointer key,
156 gpointer value,
157 gpointer user_data)
158 {
159 decor_frame_t *frame = (decor_frame_t *) value;
160
161 (*theme_update_border_extents) (frame);
162 }
163
164 void
165 decorations_changed (WnckScreen *screen)
166 {
167 GdkDisplay *gdkdisplay;
168 GdkScreen *gdkscreen;
169 GList *windows;
170 Window select;
171
172 gdkdisplay = gdk_display_get_default ();
173 gdkscreen = gdk_display_get_default_screen (gdkdisplay);
174
175 gwd_frames_foreach (set_frames_scales, (gpointer) settings->font);
176
177 update_titlebar_font ();
178 gwd_process_frames (update_frames_border_extents,
179 window_type_frames,
180 WINDOW_TYPE_FRAMES_NUM,
181 NULL);
182 update_shadow ();
183
184 update_default_decorations (gdkscreen);
185
186 if (minimal)
187 return;
188
189 /* Update all normal windows */
190
191 windows = wnck_screen_get_windows (screen);
192 while (windows != NULL)
193 {
194 decor_t *d = g_object_get_data (G_OBJECT (windows->data), "decor");
195
196 if (d->decorated)
197 {
198
199 #ifdef USE_METACITY
200 if (d->draw == draw_window_decoration ||
201 d->draw == meta_draw_window_decoration)
202 d->draw = theme_draw_window_decoration;
203 #endif
204
205 }
206
207 update_window_decoration (WNCK_WINDOW (windows->data));
208 windows = windows->next;
209 }
210
211 /* Update switcher window */
212
213 if (switcher_window &&
214 get_window_prop (switcher_window->prop_xid,
215 select_window_atom, &select))
216 {
217 decor_t *d = switcher_window;
218 /* force size update */
219 d->context = NULL;
220 d->width = d->height = 0;
221
222 update_switcher_window (d->prop_xid, select);
223 }
224 }
225
226 void
227 restack_window (WnckWindow *win,
228 int stack_mode)
229 {
230 Display *xdisplay;
231 GdkDisplay *gdkdisplay;
232 GdkScreen *screen;
233 Window xroot;
234 XEvent ev;
235
236 gdkdisplay = gdk_display_get_default ();
237 xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
238 screen = gdk_display_get_default_screen (gdkdisplay);
239 xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
240
241 if (action_menu_mapped)
242 {
243 gtk_object_destroy (GTK_OBJECT (action_menu));
244 action_menu_mapped = FALSE;
245 action_menu = NULL;
246 return;
247 }
248
249 ev.xclient.type = ClientMessage;
250 ev.xclient.display = xdisplay;
251
252 ev.xclient.serial = 0;
253 ev.xclient.send_event = TRUE;
254
255 ev.xclient.window = wnck_window_get_xid (win);
256 ev.xclient.message_type = restack_window_atom;
257 ev.xclient.format = 32;
258
259 ev.xclient.data.l[0] = 2;
260 ev.xclient.data.l[1] = None;
261 ev.xclient.data.l[2] = stack_mode;
262 ev.xclient.data.l[3] = 0;
263 ev.xclient.data.l[4] = 0;
264
265 XSendEvent (xdisplay, xroot, FALSE,
266 SubstructureRedirectMask | SubstructureNotifyMask,
267 &ev);
268
269 XSync (xdisplay, FALSE);
270 }
271
272
273 void
274 add_frame_window (WnckWindow *win,
275 Window frame,
276 Bool mode)
277 {
278 Display *xdisplay;
279 XSetWindowAttributes attr;
280 gulong xid = wnck_window_get_xid (win);
281 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
282 gint i, j;
283
284 xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
285
286 /* If we have already done this, there is no need to do it again, except
287 * if the property changed.
288 *
289 * The reason this check is here is because sometimes the PropertyNotify X
290 * event can come a bit after the property on the window is actually set
291 * which might result in this function being called twice - once by
292 * wnck through window_opened and once through our X event handler
293 * event_filter_func
294 */
295
296 if (d->created && mode && d->frame_window)
297 return;
298
299 d->active = wnck_window_is_active (win);
300 d->win = win;
301 d->frame = gwd_get_decor_frame (get_frame_type (win));
302 d->last_pos_entered = NULL;
303
304 attr.event_mask = ButtonPressMask | EnterWindowMask |
305 LeaveWindowMask | ExposureMask;
306 attr.override_redirect = TRUE;
307
308 gdk_error_trap_push ();
309
310 if (mode)
311 {
312 GdkColormap *colormap;
313
314 d->frame_window = create_gdk_window (frame);
315 d->decor_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
316
317 colormap = get_colormap_for_drawable (GDK_DRAWABLE (d->frame_window));
318
319 d->decor_image = gtk_image_new ();
320
321 gtk_widget_set_colormap (d->decor_window, colormap);
322 gtk_widget_set_colormap (d->decor_image, colormap);
323
324 d->decor_event_box = gtk_event_box_new ();
325 gtk_event_box_set_visible_window (GTK_EVENT_BOX (d->decor_event_box),
326 FALSE);
327 gtk_widget_set_events (d->decor_event_box, GDK_BUTTON_PRESS_MASK |
328 GDK_BUTTON_RELEASE_MASK |
329 GDK_POINTER_MOTION_MASK);
330
331 g_signal_connect (G_OBJECT (d->decor_event_box), "button-press-event",
332 G_CALLBACK (frame_handle_button_press),
333 (gpointer) (d));
334
335 g_signal_connect (G_OBJECT (d->decor_event_box), "button-release-event",
336 G_CALLBACK (frame_handle_button_release),
337 (gpointer) (d));
338
339 g_signal_connect (G_OBJECT (d->decor_event_box), "motion-notify-event",
340 G_CALLBACK (frame_handle_motion),
341 (gpointer) (d));
342
343 gtk_container_add (GTK_CONTAINER (d->decor_event_box), d->decor_image);
344 gtk_event_box_set_above_child (GTK_EVENT_BOX (d->decor_event_box), TRUE);
345 gtk_widget_show_all (d->decor_event_box);
346 gtk_window_set_decorated (GTK_WINDOW (d->decor_window), FALSE);
347 gtk_window_set_default_size (GTK_WINDOW (d->decor_window), 1000, 1000);
348 gtk_container_add (GTK_CONTAINER (d->decor_window), d->decor_event_box);
349
350 /* Assumed realization happens here */
351
352 g_signal_connect (G_OBJECT (d->decor_window), "realize",
353 G_CALLBACK (frame_window_realized), (gpointer) d);
354
355 gtk_widget_show_all (d->decor_window);
356 gtk_widget_show (d->decor_window);
357
358 g_object_set_data (G_OBJECT (d->frame_window),
359 "client_wnck_window", win);
360 }
361 else
362 {
363 d->frame_window = NULL;
364
365 for (i = 0; i < 3; i++)
366 {
367 for (j = 0; j < 3; j++)
368 {
369 d->event_windows[i][j].window =
370 XCreateWindow (xdisplay,
371 frame,
372 0, 0, 1, 1, 0,
373 CopyFromParent, CopyFromParent, CopyFromParent,
374 CWOverrideRedirect | CWEventMask, &attr);
375
376 if (cursor[i][j].cursor)
377 XDefineCursor (xdisplay, d->event_windows[i][j].window,
378 cursor[i][j].cursor);
379
380 XGrabButton (xdisplay,
381 Button1Mask,
382 AnyModifier,
383 d->event_windows[i][j].window,
384 FALSE,
385 ButtonPressMask |
386 ButtonReleaseMask |
387 PointerMotionMask,
388 GrabModeAsync,
389 GrabModeAsync,
390 None,
391 None);
392 }
393 }
394
395 attr.event_mask |= ButtonReleaseMask;
396
397 for (i = 0; i < BUTTON_NUM; i++)
398 {
399 d->button_windows[i].window =
400 XCreateWindow (xdisplay,
401 frame,
402 0, 0, 1, 1, 0,
403 CopyFromParent, CopyFromParent, CopyFromParent,
404 CWOverrideRedirect | CWEventMask, &attr);
405
406
407 XGrabButton (xdisplay,
408 Button1Mask,
409 AnyModifier,
410 d->button_windows[i].window,
411 FALSE,
412 ButtonPressMask |
413 ButtonReleaseMask |
414 PointerMotionMask,
415 GrabModeAsync,
416 GrabModeAsync,
417 None,
418 None);
419
420 d->button_states[i] = 0;
421 }
422 }
423
424 gdk_display_sync (gdk_display_get_default ());
425 if (!gdk_error_trap_pop ())
426 {
427 if (get_mwm_prop (xid) & (MWM_DECOR_ALL | MWM_DECOR_TITLE))
428 d->decorated = TRUE;
429
430 for (i = 0; i < 3; i++)
431 for (j = 0; j < 3; j++)
432 {
CID 10013 - PW.PARAMETER_HIDDEN
declaration hides parameter "win" (declared at line 274)
433 Window win = d->event_windows[i][j].window;
434 g_hash_table_insert (frame_table,
435 GINT_TO_POINTER (win),
436 GINT_TO_POINTER (xid));
437 }
438
439 for (i = 0; i < BUTTON_NUM; i++)
440 g_hash_table_insert (frame_table,
441 GINT_TO_POINTER (d->button_windows[i].window),
442 GINT_TO_POINTER (xid));
443
444 if (d->frame_window)
445 {
446 g_hash_table_insert (frame_table,
447 GINT_TO_POINTER (frame),
448 GINT_TO_POINTER (xid));
449 }
450 update_window_decoration_state (win);
451 update_window_decoration_actions (win);
452 update_window_decoration_icon (win);
453 update_window_decoration_size (win);
454
455 update_event_windows (win);
456 }
457 else
458 {
459 for (i = 0; i < 3; i++)
460 for (j = 0; j < 3; j++)
461 d->event_windows[i][j].window = None;
462 }
463
464 d->created = TRUE;
465 }
466
467 void
468 remove_frame_window (WnckWindow *win)
469 {
470 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
471 Display *xdisplay;
472
473 xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
474
475 if (d->pixmap)
476 {
477 g_object_unref (G_OBJECT (d->pixmap));
478 d->pixmap = NULL;
479 }
480
481 if (d->buffer_pixmap)
482 {
483 g_object_unref (G_OBJECT (d->buffer_pixmap));
484 d->buffer_pixmap = NULL;
485 }
486
487 if (d->cr)
488 {
489 cairo_destroy (d->cr);
490 d->cr = NULL;
491 }
492
493 if (d->picture && !d->frame_window)
494 {
495 XRenderFreePicture (xdisplay, d->picture);
496 d->picture = 0;
497 }
498
499 if (d->name)
500 {
501 g_free (d->name);
502 d->name = NULL;
503 }
504
505 if (d->layout)
506 {
507 g_object_unref (G_OBJECT (d->layout));
508 d->layout = NULL;
509 }
510
511 if (d->icon)
512 {
513 cairo_pattern_destroy (d->icon);
514 d->icon = NULL;
515 }
516
517 if (d->icon_pixmap)
518 {
519 g_object_unref (G_OBJECT (d->icon_pixmap));
520 d->icon_pixmap = NULL;
521 }
522
523 if (d->icon_pixbuf)
524 {
525 g_object_unref (G_OBJECT (d->icon_pixbuf));
526 d->icon_pixbuf = NULL;
527 }
528
529 if (d->force_quit_dialog)
530 {
531 GtkWidget *dialog = d->force_quit_dialog;
532
533 d->force_quit_dialog = NULL;
534 gtk_widget_destroy (dialog);
535 }
536
537 if (d->frame_window)
538 {
539 gdk_window_destroy (d->frame_window);
540 d->frame_window = NULL;
541 }
542
543 if (d->decor_image)
544 {
545 g_object_unref (d->decor_image);
546 d->decor_image = NULL;
547 }
548
549 if (d->decor_event_box)
550 {
551 g_object_unref (d->decor_event_box);
552 d->decor_event_box = NULL;
553 }
554
555 if (d->decor_window)
556 {
557 g_object_unref (d->decor_window);
558 d->decor_window = NULL;
559 }
560
561 if (d->frame)
562 {
563 gwd_decor_frame_unref (d->frame);
564 d->frame = NULL;
565 }
566
567 gdk_error_trap_push ();
568 XDeleteProperty (xdisplay, wnck_window_get_xid (win), win_decor_atom);
569 gdk_display_sync (gdk_display_get_default ());
570 gdk_error_trap_pop ();
571
572 d->width = 0;
573 d->height = 0;
574
575 d->decorated = FALSE;
576
577 d->state = 0;
578 d->actions = 0;
579
580 d->context = NULL;
581 d->shadow = NULL;
582
583 draw_list = g_slist_remove (draw_list, d);
584 }
585
586 void
587 connect_window (WnckWindow *win)
588 {
589 g_signal_connect_object (win, "name_changed",
590 G_CALLBACK (window_name_changed),
591 0, 0);
592 g_signal_connect_object (win, "geometry_changed",
593 G_CALLBACK (window_geometry_changed),
594 0, 0);
595 g_signal_connect_object (win, "icon_changed",
596 G_CALLBACK (window_icon_changed),
597 0, 0);
598 g_signal_connect_object (win, "state_changed",
599 G_CALLBACK (window_state_changed),
600 0, 0);
601 g_signal_connect_object (win, "actions_changed",
602 G_CALLBACK (window_actions_changed),
603 0, 0);
604 }
605
606 static void
607 active_window_changed (WnckScreen *screen)
608 {
609 WnckWindow *win;
610 decor_t *d;
611
612 win = wnck_screen_get_previously_active_window (screen);
613 if (win)
614 {
615 d = g_object_get_data (G_OBJECT (win), "decor");
616 if (d && d->pixmap)
617 {
618 d->active = wnck_window_is_active (win);
619
620 decor_frame_t *frame = d->decorated ? d->frame : gwd_get_decor_frame (get_frame_type (win));
621
622 if ((d->state & META_MAXIMIZED) == META_MAXIMIZED)
623 {
624 if (!d->frame_window)
625 {
626 if (d->active)
627 {
628 d->context = &frame->max_window_context_active;
629 d->shadow = frame->max_border_shadow_active;
630 }
631 else
632 {
633 d->context = &frame->max_window_context_inactive;
634 d->shadow = frame->max_border_shadow_inactive;
635 }
636 }
637 else
638 {
639 d->shadow = d->frame->max_border_no_shadow;
640 }
641 }
642 else
643 {
644 if (!d->frame_window)
645 {
646 if (d->active)
647 {
648 d->context = &frame->window_context_active;
649 d->shadow = frame->border_shadow_active;
650 }
651 else
652 {
653 d->context = &frame->window_context_inactive;
654 d->shadow = frame->border_shadow_inactive;
655 }
656 }
657 else
658 {
659 d->shadow = d->frame->border_no_shadow;
660 }
661 }
662
663 if (!d->decorated)
664 gwd_decor_frame_unref (frame);
665
666 /* We need to update the decoration size here
667 * since the shadow size might have changed and
668 * in that case the decoration will be redrawn,
669 * however if the shadow size doesn't change
670 * then we need to redraw the decoration anyways
671 * since the image would have changed */
672 if (d->win != NULL &&
673 !update_window_decoration_size (d->win) &&
674 d->decorated)
675 queue_decor_draw (d);
676
677 }
678 }
679
680 win = wnck_screen_get_active_window (screen);
681 if (win)
682 {
683 d = g_object_get_data (G_OBJECT (win), "decor");
684 if (d && d->pixmap)
685 {
686 d->active = wnck_window_is_active (win);
687
688 decor_frame_t *frame = d->decorated ? d->frame : gwd_get_decor_frame (get_frame_type (win));
689
690 if ((d->state & META_MAXIMIZED) == META_MAXIMIZED)
691 {
692 if (!d->frame_window)
693 {
694 if (d->active)
695 {
696 d->context = &frame->max_window_context_active;
697 d->shadow = frame->max_border_shadow_active;
698 }
699 else
700 {
701 d->context = &frame->max_window_context_inactive;
702 d->shadow = frame->max_border_shadow_inactive;
703 }
704 }
705 else
706 {
707 d->shadow = frame->max_border_no_shadow;
708 }
709 }
710 else
711 {
712 if (!d->frame_window)
713 {
714 if (d->active)
715 {
716 d->context = &frame->window_context_active;
717 d->shadow = frame->border_shadow_active;
718 }
719 else
720 {
721 d->context = &frame->window_context_inactive;
722 d->shadow = frame->border_shadow_inactive;
723 }
724 }
725 else
726 {
727 d->shadow = frame->border_no_shadow;
728 }
729 }
730
731 if (!d->decorated)
732 gwd_decor_frame_unref (frame);
733
734 /* We need to update the decoration size here
735 * since the shadow size might have changed and
736 * in that case the decoration will be redrawn,
737 * however if the shadow size doesn't change
738 * then we need to redraw the decoration anyways
739 * since the image would have changed */
740 if (d->win != NULL &&
741 !update_window_decoration_size (d->win) &&
742 d->decorated)
743 queue_decor_draw (d);
744
745 }
746 }
747 }
748
749 void
750 window_opened (WnckScreen *screen,
751 WnckWindow *win)
752 {
753 decor_t *d;
754 Window window;
755 gulong xid;
756 unsigned int i, j;
757
758 static event_callback callback[3][3] = {
759 { top_left_event, top_event, top_right_event },
760 { left_event, title_event, right_event },
761 { bottom_left_event, bottom_event, bottom_right_event }
762 };
763 static event_callback button_callback[BUTTON_NUM] = {
764 close_button_event,
765 max_button_event,
766 min_button_event,
767 menu_button_event,
768 shade_button_event,
769 above_button_event,
770 stick_button_event,
771 unshade_button_event,
772 unabove_button_event,
773 unstick_button_event,
774 window_menu_button_event
775 };
776
777 d = calloc (1, sizeof (decor_t));
778 if (!d)
779 return;
780
781 for (i = 0; i < 3; i++)
782 for (j = 0; j < 3; j++)
783 d->event_windows[i][j].callback = callback[i][j];
784
785 for (i = 0; i < BUTTON_NUM; i++)
786 d->button_windows[i].callback = button_callback[i];
787
788 wnck_window_get_client_window_geometry (win, NULL, NULL,
789 &d->client_width,
790 &d->client_height);
791
792 d->draw = theme_draw_window_decoration;
793
794 d->created = FALSE;
795 d->pixmap = NULL;
796 d->cr = NULL;
797 d->buffer_pixmap = NULL;
798 d->picture = None;
799
800 connect_window (win);
801
802 g_object_set_data (G_OBJECT (win), "decor", d);
803
804 xid = wnck_window_get_xid (win);
805
806 if (get_window_prop (xid, frame_input_window_atom, &window))
807 {
808 add_frame_window (win, window, FALSE);
809 }
810 else if (get_window_prop (xid, frame_output_window_atom, &window))
811 {
812 add_frame_window (win, window, TRUE);
813 }
814 }
815
816 void
817 window_closed (WnckScreen *screen,
818 WnckWindow *win)
819 {
820 local_menu_cache_notify_window_destroyed (wnck_window_get_xid (win));
821
822 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
823
824 if (d)
825 {
826 remove_frame_window (win);
827 g_object_set_data (G_OBJECT (win), "decor", NULL);
828 g_free (d);
829 }
830 }
831
832 void
833 connect_screen (WnckScreen *screen)
834 {
835 GList *windows;
836
837 g_signal_connect_object (G_OBJECT (screen), "active_window_changed",
838 G_CALLBACK (active_window_changed),
839 0, 0);
840 g_signal_connect_object (G_OBJECT (screen), "window_opened",
841 G_CALLBACK (window_opened),
842 0, 0);
843 g_signal_connect_object (G_OBJECT (screen), "window_closed",
844 G_CALLBACK (window_closed),
845 0, 0);
846
847 windows = wnck_screen_get_windows (screen);
848 while (windows != NULL)
849 {
850 window_opened (screen, windows->data);
851 windows = windows->next;
852 }
853 }