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