1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /*
4  * Copyright (C) 2002 Red Hat Inc.
5  * Copyright (C) 2003 Rob Adams
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA
20  * 02110-1335, USA.
21  */
22 
23 /**
24  * SECTION:group
25  * @title: MetaGroup
26  * @short_description: Muffin window groups
27  *
28  */
29 
30 
31 #include <config.h>
32 #include <meta/util.h>
33 #include "group-private.h"
34 #include "group-props.h"
35 #include "window-private.h"
36 #include <meta/window.h>
37 
38 static MetaGroup*
meta_group_new(MetaDisplay * display,Window group_leader)39 meta_group_new (MetaDisplay *display,
40                 Window       group_leader)
41 {
42   MetaGroup *group;
43 #define N_INITIAL_PROPS 3
44   Atom initial_props[N_INITIAL_PROPS];
45   int i;
46 
47   g_assert (N_INITIAL_PROPS == (int) G_N_ELEMENTS (initial_props));
48 
49   group = g_new0 (MetaGroup, 1);
50 
51   group->display = display;
52   group->windows = NULL;
53   group->group_leader = group_leader;
54   group->refcount = 1; /* owned by caller, hash table has only weak ref */
55 
56   if (display->groups_by_leader == NULL)
57     display->groups_by_leader = g_hash_table_new (meta_unsigned_long_hash,
58                                                   meta_unsigned_long_equal);
59 
60   g_assert (g_hash_table_lookup (display->groups_by_leader, &group_leader) == NULL);
61 
62   g_hash_table_insert (display->groups_by_leader,
63                        &group->group_leader,
64                        group);
65 
66   /* Fill these in the order we want them to be gotten */
67   i = 0;
68   initial_props[i++] = display->atom_WM_CLIENT_MACHINE;
69   initial_props[i++] = display->atom__NET_WM_PID;
70   initial_props[i++] = display->atom__NET_STARTUP_ID;
71   g_assert (N_INITIAL_PROPS == i);
72 
73   meta_group_reload_properties (group, initial_props, N_INITIAL_PROPS);
74 
75   meta_topic (META_DEBUG_GROUPS,
76               "Created new group with leader 0x%lx\n",
77               group->group_leader);
78 
79   return group;
80 }
81 
82 static void
meta_group_unref(MetaGroup * group)83 meta_group_unref (MetaGroup *group)
84 {
85   g_return_if_fail (group->refcount > 0);
86 
87   group->refcount -= 1;
88   if (group->refcount == 0)
89     {
90       meta_topic (META_DEBUG_GROUPS,
91                   "Destroying group with leader 0x%lx\n",
92                   group->group_leader);
93 
94       g_assert (group->display->groups_by_leader != NULL);
95 
96       g_hash_table_remove (group->display->groups_by_leader,
97                            &group->group_leader);
98 
99       /* mop up hash table, this is how it gets freed on display close */
100       if (g_hash_table_size (group->display->groups_by_leader) == 0)
101         {
102           g_hash_table_destroy (group->display->groups_by_leader);
103           group->display->groups_by_leader = NULL;
104         }
105 
106       free (group->wm_client_machine);
107       free (group->startup_id);
108 
109       free (group);
110     }
111 }
112 
113 /**
114  * meta_window_get_group: (skip)
115  *
116  */
117 MetaGroup*
meta_window_get_group(MetaWindow * window)118 meta_window_get_group (MetaWindow *window)
119 {
120   if (window->unmanaging)
121     return NULL;
122 
123   return window->group;
124 }
125 
126 void
meta_window_compute_group(MetaWindow * window)127 meta_window_compute_group (MetaWindow* window)
128 {
129   MetaGroup *group;
130   MetaWindow *ancestor;
131 
132   /* use window->xwindow if no window->xgroup_leader */
133 
134   group = NULL;
135 
136   /* Determine the ancestor of the window; its group setting will override the
137    * normal grouping rules; see bug 328211.
138    */
139   ancestor = meta_window_find_root_ancestor (window);
140 
141   if (window->display->groups_by_leader)
142     {
143       if (ancestor != window)
144         group = ancestor->group;
145       else if (window->xgroup_leader != None)
146         group = g_hash_table_lookup (window->display->groups_by_leader,
147                                      &window->xgroup_leader);
148       else
149         group = g_hash_table_lookup (window->display->groups_by_leader,
150                                      &window->xwindow);
151     }
152 
153   if (group != NULL)
154     {
155       window->group = group;
156       group->refcount += 1;
157     }
158   else
159     {
160       if (ancestor != window && ancestor->xgroup_leader != None)
161         group = meta_group_new (window->display,
162                                 ancestor->xgroup_leader);
163       else if (window->xgroup_leader != None)
164         group = meta_group_new (window->display,
165                                 window->xgroup_leader);
166       else
167         group = meta_group_new (window->display,
168                                 window->xwindow);
169 
170       window->group = group;
171     }
172 
173   window->group->windows = g_slist_prepend (window->group->windows, window);
174 
175   meta_topic (META_DEBUG_GROUPS,
176               "Adding %s to group with leader 0x%lx\n",
177               window->desc, group->group_leader);
178 
179 }
180 
181 static void
remove_window_from_group(MetaWindow * window)182 remove_window_from_group (MetaWindow *window)
183 {
184   if (window->group != NULL)
185     {
186       meta_topic (META_DEBUG_GROUPS,
187                   "Removing %s from group with leader 0x%lx\n",
188                   window->desc, window->group->group_leader);
189 
190       window->group->windows =
191         g_slist_remove (window->group->windows,
192                         window);
193       meta_group_unref (window->group);
194       window->group = NULL;
195     }
196 }
197 
198 void
meta_window_group_leader_changed(MetaWindow * window)199 meta_window_group_leader_changed (MetaWindow *window)
200 {
201   remove_window_from_group (window);
202   meta_window_compute_group (window);
203 }
204 
205 void
meta_window_shutdown_group(MetaWindow * window)206 meta_window_shutdown_group (MetaWindow *window)
207 {
208   remove_window_from_group (window);
209 }
210 
211 /**
212  * meta_display_lookup_group: (skip)
213  *
214  */
215 MetaGroup*
meta_display_lookup_group(MetaDisplay * display,Window group_leader)216 meta_display_lookup_group (MetaDisplay *display,
217                            Window       group_leader)
218 {
219   MetaGroup *group;
220 
221   group = NULL;
222 
223   if (display->groups_by_leader)
224     group = g_hash_table_lookup (display->groups_by_leader,
225                                  &group_leader);
226 
227   return group;
228 }
229 
230 /**
231  * meta_group_list_windows:
232  * @group: A #MetaGroup
233  *
234  * Returns: (transfer container) (element-type Meta.Window): List of windows
235  */
236 GSList*
meta_group_list_windows(MetaGroup * group)237 meta_group_list_windows (MetaGroup *group)
238 {
239   return g_slist_copy (group->windows);
240 }
241 
242 void
meta_group_update_layers(MetaGroup * group)243 meta_group_update_layers (MetaGroup *group)
244 {
245   GSList *tmp;
246   GSList *frozen_stacks;
247 
248   if (group->windows == NULL)
249     return;
250 
251   frozen_stacks = NULL;
252   tmp = group->windows;
253   while (tmp != NULL)
254     {
255       MetaWindow *window = tmp->data;
256 
257       /* we end up freezing the same stack a lot of times,
258        * but doesn't hurt anything. have to handle
259        * groups that span 2 screens.
260        */
261       meta_stack_freeze (window->screen->stack);
262       frozen_stacks = g_slist_prepend (frozen_stacks, window->screen->stack);
263 
264       meta_stack_update_layer (window->screen->stack,
265                                window);
266 
267       tmp = tmp->next;
268     }
269 
270   tmp = frozen_stacks;
271   while (tmp != NULL)
272     {
273       meta_stack_thaw (tmp->data);
274       tmp = tmp->next;
275     }
276 
277   g_slist_free (frozen_stacks);
278 }
279 
280 const char*
meta_group_get_startup_id(MetaGroup * group)281 meta_group_get_startup_id (MetaGroup *group)
282 {
283   return group->startup_id;
284 }
285 
286 /**
287  * meta_group_property_notify: (skip)
288  *
289  */
290 gboolean
meta_group_property_notify(MetaGroup * group,XEvent * event)291 meta_group_property_notify (MetaGroup  *group,
292                             XEvent     *event)
293 {
294   meta_group_reload_property (group,
295                               event->xproperty.atom);
296 
297   return TRUE;
298 
299 }
300 
301 int
meta_group_get_size(MetaGroup * group)302 meta_group_get_size (MetaGroup *group)
303 {
304   if (!group)
305     return 0;
306 
307   return group->refcount;
308 }
309 
310