1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright (C) 2001 Havoc Pennington
5 * Copyright (C) 2002, 2003, 2004 Red Hat, Inc.
6 * Copyright (C) 2003, 2004 Rob Adams
7 * Copyright (C) 2004-2006 Elijah Newren
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "config.h"
24
25 #include "core/meta-workspace-manager-private.h"
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "core/window-private.h"
32 #include "core/workspace-private.h"
33 #include "meta/meta-enum-types.h"
34 #include "meta/prefs.h"
35 #include "meta/util.h"
36
37 G_DEFINE_TYPE (MetaWorkspaceManager, meta_workspace_manager, G_TYPE_OBJECT)
38
39 enum
40 {
41 WORKSPACE_ADDED,
42 WORKSPACE_REMOVED,
43 WORKSPACE_SWITCHED,
44 WORKSPACES_REORDERED,
45 ACTIVE_WORKSPACE_CHANGED,
46 SHOWING_DESKTOP_CHANGED,
47 LAST_SIGNAL
48 };
49
50 enum
51 {
52 PROP_0,
53
54 PROP_LAYOUT_COLUMNS,
55 PROP_LAYOUT_ROWS,
56
57 PROP_N_WORKSPACES
58 };
59
60 static guint workspace_manager_signals [LAST_SIGNAL] = { 0 };
61
62 static void prefs_changed_callback (MetaPreference pref,
63 gpointer data);
64
65 static void
meta_workspace_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)66 meta_workspace_manager_get_property (GObject *object,
67 guint prop_id,
68 GValue *value,
69 GParamSpec *pspec)
70 {
71 MetaWorkspaceManager *workspace_manager = META_WORKSPACE_MANAGER (object);
72
73 switch (prop_id)
74 {
75 case PROP_LAYOUT_COLUMNS:
76 g_value_set_int (value, workspace_manager->columns_of_workspaces);
77 break;
78 case PROP_LAYOUT_ROWS:
79 g_value_set_int (value, workspace_manager->rows_of_workspaces);
80 break;
81 case PROP_N_WORKSPACES:
82 g_value_set_int (value, meta_workspace_manager_get_n_workspaces (workspace_manager));
83 break;
84 default:
85 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
86 break;
87 }
88 }
89
90 static void
meta_workspace_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)91 meta_workspace_manager_set_property (GObject *object,
92 guint prop_id,
93 const GValue *value,
94 GParamSpec *pspec)
95 {
96 switch (prop_id)
97 {
98 default:
99 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
100 break;
101 }
102 }
103
104 static void
meta_workspace_manager_finalize(GObject * object)105 meta_workspace_manager_finalize (GObject *object)
106 {
107 MetaWorkspaceManager *workspace_manager = META_WORKSPACE_MANAGER (object);
108
109 meta_prefs_remove_listener (prefs_changed_callback, workspace_manager);
110
111 G_OBJECT_CLASS (meta_workspace_manager_parent_class)->finalize (object);
112 }
113
114 static void
meta_workspace_manager_class_init(MetaWorkspaceManagerClass * klass)115 meta_workspace_manager_class_init (MetaWorkspaceManagerClass *klass)
116 {
117 GObjectClass *object_class = G_OBJECT_CLASS (klass);
118
119 object_class->get_property = meta_workspace_manager_get_property;
120 object_class->set_property = meta_workspace_manager_set_property;
121
122 object_class->finalize = meta_workspace_manager_finalize;
123
124 workspace_manager_signals[WORKSPACE_ADDED] =
125 g_signal_new ("workspace-added",
126 G_TYPE_FROM_CLASS (klass),
127 G_SIGNAL_RUN_LAST,
128 0, NULL, NULL, NULL,
129 G_TYPE_NONE,
130 1,
131 G_TYPE_INT);
132
133 workspace_manager_signals[WORKSPACE_REMOVED] =
134 g_signal_new ("workspace-removed",
135 G_TYPE_FROM_CLASS (klass),
136 G_SIGNAL_RUN_LAST,
137 0, NULL, NULL, NULL,
138 G_TYPE_NONE,
139 1,
140 G_TYPE_INT);
141
142 workspace_manager_signals[WORKSPACE_SWITCHED] =
143 g_signal_new ("workspace-switched",
144 G_TYPE_FROM_CLASS (klass),
145 G_SIGNAL_RUN_LAST,
146 0, NULL, NULL, NULL,
147 G_TYPE_NONE,
148 3,
149 G_TYPE_INT,
150 G_TYPE_INT,
151 META_TYPE_MOTION_DIRECTION);
152
153 /* Emitted when calling meta_workspace_manager_reorder_workspace.
154 *
155 * This signal is emitted when a workspace has been reordered to
156 * a different index. Note that other workspaces can change
157 * their index too when reordering happens.
158 */
159 workspace_manager_signals[WORKSPACES_REORDERED] =
160 g_signal_new ("workspaces-reordered",
161 G_TYPE_FROM_CLASS (klass),
162 G_SIGNAL_RUN_LAST,
163 0,
164 NULL, NULL, NULL,
165 G_TYPE_NONE, 0);
166
167 workspace_manager_signals[ACTIVE_WORKSPACE_CHANGED] =
168 g_signal_new ("active-workspace-changed",
169 G_TYPE_FROM_CLASS (klass),
170 G_SIGNAL_RUN_LAST,
171 0, NULL, NULL, NULL,
172 G_TYPE_NONE, 0);
173
174 workspace_manager_signals[SHOWING_DESKTOP_CHANGED] =
175 g_signal_new ("showing-desktop-changed",
176 G_TYPE_FROM_CLASS (klass),
177 G_SIGNAL_RUN_LAST,
178 0, NULL, NULL, NULL,
179 G_TYPE_NONE, 0);
180
181 g_object_class_install_property (object_class,
182 PROP_LAYOUT_COLUMNS,
183 g_param_spec_int ("layout-columns",
184 "Layout columns",
185 "Number of columns in layout",
186 -1, G_MAXINT, 1,
187 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
188
189 g_object_class_install_property (object_class,
190 PROP_LAYOUT_ROWS,
191 g_param_spec_int ("layout-rows",
192 "Layout rows",
193 "Number of rows in layout",
194 -1, G_MAXINT, -1,
195 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
196
197 g_object_class_install_property (object_class,
198 PROP_N_WORKSPACES,
199 g_param_spec_int ("n-workspaces",
200 "N Workspaces",
201 "Number of workspaces",
202 1, G_MAXINT, 1,
203 G_PARAM_READABLE));
204 }
205
206 static void
meta_workspace_manager_init(MetaWorkspaceManager * workspace_manager)207 meta_workspace_manager_init (MetaWorkspaceManager *workspace_manager)
208 {
209 }
210
211 void
meta_workspace_manager_reload_work_areas(MetaWorkspaceManager * workspace_manager)212 meta_workspace_manager_reload_work_areas (MetaWorkspaceManager *workspace_manager)
213 {
214 GList *l;
215
216 for (l = workspace_manager->workspaces; l; l = l->next)
217 {
218 MetaWorkspace *workspace = l->data;
219
220 meta_workspace_invalidate_work_area (workspace);
221 }
222 }
223
224 MetaWorkspaceManager *
meta_workspace_manager_new(MetaDisplay * display)225 meta_workspace_manager_new (MetaDisplay *display)
226 {
227 MetaWorkspaceManager *workspace_manager;
228
229 workspace_manager = g_object_new (META_TYPE_WORKSPACE_MANAGER, NULL);
230
231 workspace_manager->display = display;
232 workspace_manager->active_workspace = NULL;
233 workspace_manager->workspaces = NULL;
234 workspace_manager->rows_of_workspaces = 1;
235 workspace_manager->columns_of_workspaces = -1;
236 workspace_manager->vertical_workspaces = FALSE;
237 workspace_manager->starting_corner = META_DISPLAY_TOPLEFT;
238
239 /* This is the default layout extracted from default
240 * variable values in update_num_workspaces ()
241 * This can be overridden using _NET_DESKTOP_LAYOUT in
242 * meta_x11_display_new (), if it's specified */
243 meta_workspace_manager_update_workspace_layout (workspace_manager,
244 META_DISPLAY_TOPLEFT,
245 FALSE,
246 1,
247 -1);
248
249 /* There must be at least one workspace at all times,
250 * so create that required workspace.
251 */
252 meta_workspace_new (workspace_manager);
253
254 meta_workspace_manager_init_workspaces (workspace_manager);
255
256 meta_prefs_add_listener (prefs_changed_callback, workspace_manager);
257
258 return workspace_manager;
259 }
260
261 void
meta_workspace_manager_init_workspaces(MetaWorkspaceManager * workspace_manager)262 meta_workspace_manager_init_workspaces (MetaWorkspaceManager *workspace_manager)
263 {
264 int num;
265
266 g_return_if_fail (META_IS_WORKSPACE_MANAGER (workspace_manager));
267
268 if (meta_prefs_get_dynamic_workspaces ())
269 /* This will be properly updated using _NET_NUMBER_OF_DESKTOPS
270 * (if set) in meta_x11_display_new () */
271 num = 1;
272 else
273 num = meta_prefs_get_num_workspaces ();
274
275 meta_workspace_manager_update_num_workspaces (workspace_manager, META_CURRENT_TIME, num);
276
277 meta_workspace_activate (workspace_manager->workspaces->data, META_CURRENT_TIME);
278
279 meta_workspace_manager_reload_work_areas (workspace_manager);
280 }
281
282 int
meta_workspace_manager_get_n_workspaces(MetaWorkspaceManager * workspace_manager)283 meta_workspace_manager_get_n_workspaces (MetaWorkspaceManager *workspace_manager)
284 {
285 return g_list_length (workspace_manager->workspaces);
286 }
287
288 /**
289 * meta_workspace_manager_get_workspace_by_index:
290 * @workspace_manager: a #MetaWorkspaceManager
291 * @index: index of one of the display's workspaces
292 *
293 * Gets the workspace object for one of a workspace manager's workspaces given the workspace
294 * index. It's valid to call this function with an out-of-range index and it
295 * will robustly return %NULL.
296 *
297 * Return value: (transfer none) (nullable): the workspace object with specified
298 * index, or %NULL if the index is out of range.
299 */
300 MetaWorkspace *
meta_workspace_manager_get_workspace_by_index(MetaWorkspaceManager * workspace_manager,int idx)301 meta_workspace_manager_get_workspace_by_index (MetaWorkspaceManager *workspace_manager,
302 int idx)
303 {
304 return g_list_nth_data (workspace_manager->workspaces, idx);
305 }
306
307 void
meta_workspace_manager_remove_workspace(MetaWorkspaceManager * workspace_manager,MetaWorkspace * workspace,guint32 timestamp)308 meta_workspace_manager_remove_workspace (MetaWorkspaceManager *workspace_manager,
309 MetaWorkspace *workspace,
310 guint32 timestamp)
311 {
312 GList *l;
313 GList *next;
314 MetaWorkspace *neighbour = NULL;
315 int index;
316 int active_index;
317 gboolean active_index_changed;
318 int new_num;
319
320 l = g_list_find (workspace_manager->workspaces, workspace);
321 if (!l)
322 return;
323
324 next = l->next;
325
326 if (l->prev)
327 neighbour = l->prev->data;
328 else if (l->next)
329 neighbour = l->next->data;
330 else
331 {
332 /* Cannot remove the only workspace! */
333 return;
334 }
335
336 meta_workspace_relocate_windows (workspace, neighbour);
337
338 if (workspace == workspace_manager->active_workspace)
339 meta_workspace_activate (neighbour, timestamp);
340
341 /* To emit the signal after removing the workspace */
342 index = meta_workspace_index (workspace);
343 active_index = meta_workspace_manager_get_active_workspace_index (workspace_manager);
344 active_index_changed = index < active_index;
345
346 /* This also removes the workspace from the displays list */
347 meta_workspace_remove (workspace);
348
349 new_num = g_list_length (workspace_manager->workspaces);
350
351 if (!meta_prefs_get_dynamic_workspaces ())
352 meta_prefs_set_num_workspaces (new_num);
353
354 /* If deleting a workspace before the current workspace, the active
355 * workspace index changes, so we need to update that hint */
356 if (active_index_changed)
357 g_signal_emit (workspace_manager,
358 workspace_manager_signals[ACTIVE_WORKSPACE_CHANGED],
359 0, NULL);
360
361 for (l = next; l; l = l->next)
362 {
363 MetaWorkspace *w = l->data;
364 meta_workspace_index_changed (w);
365 }
366
367 meta_display_queue_workarea_recalc (workspace_manager->display);
368
369 g_signal_emit (workspace_manager,
370 workspace_manager_signals[WORKSPACE_REMOVED],
371 0, index);
372 g_object_notify (G_OBJECT (workspace_manager), "n-workspaces");
373 }
374
375 /**
376 * meta_workspace_manager_append_new_workspace:
377 * @workspace_manager: a #MetaWorkspaceManager
378 * @activate: %TRUE if the workspace should be switched to after creation
379 * @timestamp: if switching to a new workspace, timestamp to be used when
380 * focusing a window on the new workspace. (Doesn't hurt to pass a valid
381 * timestamp when available even if not switching workspaces.)
382 *
383 * Append a new workspace to the workspace manager and (optionally) switch to that
384 * display.
385 *
386 * Return value: (transfer none): the newly appended workspace.
387 */
388 MetaWorkspace *
meta_workspace_manager_append_new_workspace(MetaWorkspaceManager * workspace_manager,gboolean activate,guint32 timestamp)389 meta_workspace_manager_append_new_workspace (MetaWorkspaceManager *workspace_manager,
390 gboolean activate,
391 guint32 timestamp)
392 {
393 MetaWorkspace *w;
394 int new_num;
395
396 /* This also adds the workspace to the workspace manager list */
397 w = meta_workspace_new (workspace_manager);
398
399 if (!w)
400 return NULL;
401
402 if (activate)
403 meta_workspace_activate (w, timestamp);
404
405 new_num = g_list_length (workspace_manager->workspaces);
406
407 if (!meta_prefs_get_dynamic_workspaces ())
408 meta_prefs_set_num_workspaces (new_num);
409
410 meta_display_queue_workarea_recalc (workspace_manager->display);
411
412 g_signal_emit (workspace_manager, workspace_manager_signals[WORKSPACE_ADDED],
413 0, meta_workspace_index (w));
414 g_object_notify (G_OBJECT (workspace_manager), "n-workspaces");
415
416 return w;
417 }
418
419 void
meta_workspace_manager_update_num_workspaces(MetaWorkspaceManager * workspace_manager,guint32 timestamp,int new_num)420 meta_workspace_manager_update_num_workspaces (MetaWorkspaceManager *workspace_manager,
421 guint32 timestamp,
422 int new_num)
423 {
424 int old_num;
425 GList *l;
426 int i = 0;
427 GList *extras = NULL;
428 MetaWorkspace *last_remaining = NULL;
429 gboolean need_change_space = FALSE;
430
431 g_assert (new_num > 0);
432
433 if (g_list_length (workspace_manager->workspaces) == (guint) new_num)
434 return;
435
436 for (l = workspace_manager->workspaces; l; l = l->next)
437 {
438 MetaWorkspace *w = l->data;
439
440 if (i >= new_num)
441 extras = g_list_prepend (extras, w);
442 else
443 last_remaining = w;
444
445 ++i;
446 }
447 old_num = i;
448
449 g_assert (last_remaining);
450
451 /* Get rid of the extra workspaces by moving all their windows
452 * to last_remaining, then activating last_remaining if
453 * one of the removed workspaces was active. This will be a bit
454 * wacky if the config tool for changing number of workspaces
455 * is on a removed workspace ;-)
456 */
457 for (l = extras; l; l = l->next)
458 {
459 MetaWorkspace *w = l->data;
460
461 meta_workspace_relocate_windows (w, last_remaining);
462
463 if (w == workspace_manager->active_workspace)
464 need_change_space = TRUE;
465 }
466
467 if (need_change_space)
468 meta_workspace_activate (last_remaining, timestamp);
469
470 /* Should now be safe to free the workspaces */
471 for (l = extras; l; l = l->next)
472 {
473 MetaWorkspace *w = l->data;
474
475 meta_workspace_remove (w);
476 }
477
478 g_list_free (extras);
479
480 for (i = old_num; i < new_num; i++)
481 meta_workspace_new (workspace_manager);
482
483 meta_display_queue_workarea_recalc (workspace_manager->display);
484
485 for (i = old_num; i < new_num; i++)
486 g_signal_emit (workspace_manager,
487 workspace_manager_signals[WORKSPACE_ADDED],
488 0, i);
489
490 g_object_notify (G_OBJECT (workspace_manager), "n-workspaces");
491 }
492
493 /**
494 * meta_workspace_manager_reorder_workspace:
495 * @workspace_manager: a #MetaWorkspaceManager
496 * @workspace: a #MetaWorkspace to reorder
497 * @new_index: the new index of the passed workspace
498 *
499 * Reorder a workspace to a new index. If the workspace is currently active
500 * the "active-workspace-changed" signal will be emitted.
501 * If the workspace's index is the same as @new_index or the workspace
502 * will not be found in the list, this function will return.
503 *
504 * Calling this function will also emit the "workspaces-reordered" signal.
505 */
506 void
meta_workspace_manager_reorder_workspace(MetaWorkspaceManager * workspace_manager,MetaWorkspace * workspace,int new_index)507 meta_workspace_manager_reorder_workspace (MetaWorkspaceManager *workspace_manager,
508 MetaWorkspace *workspace,
509 int new_index)
510 {
511 GList *l;
512 GList *from, *to;
513 int index;
514 int active_index, new_active_index;
515
516 g_return_if_fail (META_IS_WORKSPACE_MANAGER (workspace_manager));
517 g_return_if_fail (new_index >= 0 &&
518 new_index < g_list_length (workspace_manager->workspaces));
519
520 l = g_list_find (workspace_manager->workspaces, workspace);
521 g_return_if_fail (l);
522
523 index = meta_workspace_index (workspace);
524
525 if (new_index == index)
526 return;
527
528 active_index =
529 meta_workspace_manager_get_active_workspace_index (workspace_manager);
530
531 workspace_manager->workspaces =
532 g_list_remove_link (workspace_manager->workspaces, l);
533
534 workspace_manager->workspaces =
535 g_list_insert (workspace_manager->workspaces, l->data, new_index);
536
537 g_list_free (l);
538
539 new_active_index =
540 meta_workspace_manager_get_active_workspace_index (workspace_manager);
541
542 if (active_index != new_active_index)
543 g_signal_emit (workspace_manager,
544 workspace_manager_signals[ACTIVE_WORKSPACE_CHANGED],
545 0, NULL);
546
547 from = g_list_nth (workspace_manager->workspaces, MIN (new_index, index));
548 to = g_list_nth (workspace_manager->workspaces, MAX (new_index, index));
549 for (l = from; l != to->next; l = l->next)
550 {
551 MetaWorkspace *w = l->data;
552
553 meta_workspace_index_changed (w);
554 }
555
556 meta_display_queue_workarea_recalc (workspace_manager->display);
557 g_signal_emit (workspace_manager,
558 workspace_manager_signals[WORKSPACES_REORDERED], 0, NULL);
559 }
560
561 void
meta_workspace_manager_update_workspace_layout(MetaWorkspaceManager * workspace_manager,MetaDisplayCorner starting_corner,gboolean vertical_layout,int n_rows,int n_columns)562 meta_workspace_manager_update_workspace_layout (MetaWorkspaceManager *workspace_manager,
563 MetaDisplayCorner starting_corner,
564 gboolean vertical_layout,
565 int n_rows,
566 int n_columns)
567 {
568 g_return_if_fail (META_IS_WORKSPACE_MANAGER (workspace_manager));
569 g_return_if_fail (n_rows > 0 || n_columns > 0);
570 g_return_if_fail (n_rows != 0 && n_columns != 0);
571
572 if (workspace_manager->workspace_layout_overridden)
573 return;
574
575 workspace_manager->vertical_workspaces = vertical_layout != FALSE;
576 workspace_manager->starting_corner = starting_corner;
577 workspace_manager->rows_of_workspaces = n_rows;
578 workspace_manager->columns_of_workspaces = n_columns;
579
580 meta_verbose ("Workspace layout rows = %d cols = %d orientation = %d starting corner = %u",
581 workspace_manager->rows_of_workspaces,
582 workspace_manager->columns_of_workspaces,
583 workspace_manager->vertical_workspaces,
584 workspace_manager->starting_corner);
585 g_object_notify (G_OBJECT (workspace_manager), "layout-columns");
586 g_object_notify (G_OBJECT (workspace_manager), "layout-rows");
587 }
588
589 /**
590 * meta_workspace_manager_override_workspace_layout:
591 * @workspace_manager: a #MetaWorkspaceManager
592 * @starting_corner: the corner at which the first workspace is found
593 * @vertical_layout: if %TRUE the workspaces are laid out in columns rather than rows
594 * @n_rows: number of rows of workspaces, or -1 to determine the number of rows from
595 * @n_columns and the total number of workspaces
596 * @n_columns: number of columns of workspaces, or -1 to determine the number of columns from
597 * @n_rows and the total number of workspaces
598 *
599 * Explicitly set the layout of workspaces. Once this has been called, the contents of the
600 * _NET_DESKTOP_LAYOUT property on the root window are completely ignored.
601 */
602 void
meta_workspace_manager_override_workspace_layout(MetaWorkspaceManager * workspace_manager,MetaDisplayCorner starting_corner,gboolean vertical_layout,int n_rows,int n_columns)603 meta_workspace_manager_override_workspace_layout (MetaWorkspaceManager *workspace_manager,
604 MetaDisplayCorner starting_corner,
605 gboolean vertical_layout,
606 int n_rows,
607 int n_columns)
608 {
609 workspace_manager->workspace_layout_overridden = FALSE;
610
611 meta_workspace_manager_update_workspace_layout (workspace_manager,
612 starting_corner,
613 vertical_layout,
614 n_rows,
615 n_columns);
616
617 workspace_manager->workspace_layout_overridden = TRUE;
618 }
619
620 #ifdef WITH_VERBOSE_MODE
621 static const char *
meta_workspace_manager_corner_to_string(MetaDisplayCorner corner)622 meta_workspace_manager_corner_to_string (MetaDisplayCorner corner)
623 {
624 switch (corner)
625 {
626 case META_DISPLAY_TOPLEFT:
627 return "TopLeft";
628 case META_DISPLAY_TOPRIGHT:
629 return "TopRight";
630 case META_DISPLAY_BOTTOMLEFT:
631 return "BottomLeft";
632 case META_DISPLAY_BOTTOMRIGHT:
633 return "BottomRight";
634 }
635
636 return "Unknown";
637 }
638 #endif /* WITH_VERBOSE_MODE */
639
640 void
meta_workspace_manager_calc_workspace_layout(MetaWorkspaceManager * workspace_manager,int num_workspaces,int current_space,MetaWorkspaceLayout * layout)641 meta_workspace_manager_calc_workspace_layout (MetaWorkspaceManager *workspace_manager,
642 int num_workspaces,
643 int current_space,
644 MetaWorkspaceLayout *layout)
645 {
646 int rows, cols;
647 int grid_area;
648 int *grid;
649 int i, r, c;
650 int current_row, current_col;
651
652 rows = workspace_manager->rows_of_workspaces;
653 cols = workspace_manager->columns_of_workspaces;
654 if (rows <= 0 && cols <= 0)
655 cols = num_workspaces;
656
657 if (rows <= 0)
658 rows = num_workspaces / cols + ((num_workspaces % cols) > 0 ? 1 : 0);
659 if (cols <= 0)
660 cols = num_workspaces / rows + ((num_workspaces % rows) > 0 ? 1 : 0);
661
662 /* paranoia */
663 if (rows < 1)
664 rows = 1;
665 if (cols < 1)
666 cols = 1;
667
668 g_assert (rows != 0 && cols != 0);
669
670 grid_area = rows * cols;
671
672 meta_verbose ("Getting layout rows = %d cols = %d current = %d "
673 "num_spaces = %d vertical = %s corner = %s",
674 rows, cols, current_space, num_workspaces,
675 workspace_manager->vertical_workspaces ? "(true)" : "(false)",
676 meta_workspace_manager_corner_to_string (workspace_manager->starting_corner));
677
678 /* ok, we want to setup the distances in the workspace array to go
679 * in each direction. Remember, there are many ways that a workspace
680 * array can be setup.
681 * see http://www.freedesktop.org/standards/wm-spec/1.2/html/x109.html
682 * and look at the _NET_DESKTOP_LAYOUT section for details.
683 * For instance:
684 */
685 /* starting_corner = META_DISPLAY_TOPLEFT
686 * vertical_workspaces = 0 vertical_workspaces=1
687 * 1234 1357
688 * 5678 2468
689 *
690 * starting_corner = META_DISPLAY_TOPRIGHT
691 * vertical_workspaces = 0 vertical_workspaces=1
692 * 4321 7531
693 * 8765 8642
694 *
695 * starting_corner = META_DISPLAY_BOTTOMLEFT
696 * vertical_workspaces = 0 vertical_workspaces=1
697 * 5678 2468
698 * 1234 1357
699 *
700 * starting_corner = META_DISPLAY_BOTTOMRIGHT
701 * vertical_workspaces = 0 vertical_workspaces=1
702 * 8765 8642
703 * 4321 7531
704 *
705 */
706 /* keep in mind that we could have a ragged layout, e.g. the "8"
707 * in the above grids could be missing
708 */
709
710
711 grid = g_new (int, grid_area);
712
713 i = 0;
714
715 switch (workspace_manager->starting_corner)
716 {
717 case META_DISPLAY_TOPLEFT:
718 if (workspace_manager->vertical_workspaces)
719 {
720 c = 0;
721 while (c < cols)
722 {
723 r = 0;
724 while (r < rows)
725 {
726 grid[r*cols+c] = i;
727 ++i;
728 ++r;
729 }
730 ++c;
731 }
732 }
733 else
734 {
735 r = 0;
736 while (r < rows)
737 {
738 c = 0;
739 while (c < cols)
740 {
741 grid[r*cols+c] = i;
742 ++i;
743 ++c;
744 }
745 ++r;
746 }
747 }
748 break;
749 case META_DISPLAY_TOPRIGHT:
750 if (workspace_manager->vertical_workspaces)
751 {
752 c = cols - 1;
753 while (c >= 0)
754 {
755 r = 0;
756 while (r < rows)
757 {
758 grid[r*cols+c] = i;
759 ++i;
760 ++r;
761 }
762 --c;
763 }
764 }
765 else
766 {
767 r = 0;
768 while (r < rows)
769 {
770 c = cols - 1;
771 while (c >= 0)
772 {
773 grid[r*cols+c] = i;
774 ++i;
775 --c;
776 }
777 ++r;
778 }
779 }
780 break;
781 case META_DISPLAY_BOTTOMLEFT:
782 if (workspace_manager->vertical_workspaces)
783 {
784 c = 0;
785 while (c < cols)
786 {
787 r = rows - 1;
788 while (r >= 0)
789 {
790 grid[r*cols+c] = i;
791 ++i;
792 --r;
793 }
794 ++c;
795 }
796 }
797 else
798 {
799 r = rows - 1;
800 while (r >= 0)
801 {
802 c = 0;
803 while (c < cols)
804 {
805 grid[r*cols+c] = i;
806 ++i;
807 ++c;
808 }
809 --r;
810 }
811 }
812 break;
813 case META_DISPLAY_BOTTOMRIGHT:
814 if (workspace_manager->vertical_workspaces)
815 {
816 c = cols - 1;
817 while (c >= 0)
818 {
819 r = rows - 1;
820 while (r >= 0)
821 {
822 grid[r*cols+c] = i;
823 ++i;
824 --r;
825 }
826 --c;
827 }
828 }
829 else
830 {
831 r = rows - 1;
832 while (r >= 0)
833 {
834 c = cols - 1;
835 while (c >= 0)
836 {
837 grid[r*cols+c] = i;
838 ++i;
839 --c;
840 }
841 --r;
842 }
843 }
844 break;
845 }
846
847 if (i != grid_area)
848 meta_bug ("did not fill in the whole workspace grid in %s (%d filled)",
849 G_STRFUNC, i);
850
851 current_row = 0;
852 current_col = 0;
853 r = 0;
854 while (r < rows)
855 {
856 c = 0;
857 while (c < cols)
858 {
859 if (grid[r*cols+c] == current_space)
860 {
861 current_row = r;
862 current_col = c;
863 }
864 else if (grid[r*cols+c] >= num_workspaces)
865 {
866 /* flag nonexistent spaces with -1 */
867 grid[r*cols+c] = -1;
868 }
869 ++c;
870 }
871 ++r;
872 }
873
874 layout->rows = rows;
875 layout->cols = cols;
876 layout->grid = grid;
877 layout->grid_area = grid_area;
878 layout->current_row = current_row;
879 layout->current_col = current_col;
880
881 #ifdef WITH_VERBOSE_MODE
882 if (meta_is_verbose ())
883 {
884 r = 0;
885 while (r < layout->rows)
886 {
887 meta_verbose (" ");
888 meta_push_no_msg_prefix ();
889 c = 0;
890 while (c < layout->cols)
891 {
892 if (r == layout->current_row &&
893 c == layout->current_col)
894 meta_verbose ("*%2d ", layout->grid[r*layout->cols+c]);
895 else
896 meta_verbose ("%3d ", layout->grid[r*layout->cols+c]);
897 ++c;
898 }
899 meta_pop_no_msg_prefix ();
900 ++r;
901 }
902 }
903 #endif /* WITH_VERBOSE_MODE */
904 }
905
906 void
meta_workspace_manager_free_workspace_layout(MetaWorkspaceLayout * layout)907 meta_workspace_manager_free_workspace_layout (MetaWorkspaceLayout *layout)
908 {
909 g_free (layout->grid);
910 }
911
912 static void
queue_windows_showing(MetaWorkspaceManager * workspace_manager)913 queue_windows_showing (MetaWorkspaceManager *workspace_manager)
914 {
915 GSList *windows, *l;
916
917 /* Must operate on all windows on display instead of just on the
918 * active_workspace's window list, because the active_workspace's
919 * window list may not contain the on_all_workspace windows.
920 */
921 windows = meta_display_list_windows (workspace_manager->display, META_LIST_DEFAULT);
922
923 for (l = windows; l; l = l->next)
924 {
925 MetaWindow *w = l->data;
926
927 meta_window_queue (w, META_QUEUE_CALC_SHOWING);
928 }
929
930 g_slist_free (windows);
931 }
932
933 void
meta_workspace_manager_minimize_all_on_active_workspace_except(MetaWorkspaceManager * workspace_manager,MetaWindow * keep)934 meta_workspace_manager_minimize_all_on_active_workspace_except (MetaWorkspaceManager *workspace_manager,
935 MetaWindow *keep)
936 {
937 GList *l;
938
939 for (l = workspace_manager->active_workspace->windows; l; l = l->next)
940 {
941 MetaWindow *w = l->data;
942
943 if (w->has_minimize_func && w != keep)
944 meta_window_minimize (w);
945 }
946 }
947
948 void
meta_workspace_manager_show_desktop(MetaWorkspaceManager * workspace_manager,guint32 timestamp)949 meta_workspace_manager_show_desktop (MetaWorkspaceManager *workspace_manager,
950 guint32 timestamp)
951 {
952 GList *l;
953
954 if (workspace_manager->active_workspace->showing_desktop)
955 return;
956
957 workspace_manager->active_workspace->showing_desktop = TRUE;
958
959 queue_windows_showing (workspace_manager);
960
961 /* Focus the most recently used META_WINDOW_DESKTOP window, if there is one;
962 * see bug 159257.
963 */
964 for (l = workspace_manager->active_workspace->mru_list; l; l = l->next)
965 {
966 MetaWindow *w = l->data;
967
968 if (w->type == META_WINDOW_DESKTOP)
969 {
970 meta_window_focus (w, timestamp);
971 break;
972 }
973 }
974
975 g_signal_emit (workspace_manager,
976 workspace_manager_signals[SHOWING_DESKTOP_CHANGED],
977 0, NULL);
978 }
979
980 void
meta_workspace_manager_unshow_desktop(MetaWorkspaceManager * workspace_manager)981 meta_workspace_manager_unshow_desktop (MetaWorkspaceManager *workspace_manager)
982 {
983 if (!workspace_manager->active_workspace->showing_desktop)
984 return;
985
986 workspace_manager->active_workspace->showing_desktop = FALSE;
987
988 queue_windows_showing (workspace_manager);
989
990 g_signal_emit (workspace_manager,
991 workspace_manager_signals[SHOWING_DESKTOP_CHANGED],
992 0, NULL);
993 }
994
995 /**
996 * meta_workspace_manager_get_workspaces: (skip)
997 * @workspace_manager: a #MetaWorkspaceManager
998 *
999 * Returns: (transfer none) (element-type Meta.Workspace): The workspaces for @display
1000 */
1001 GList *
meta_workspace_manager_get_workspaces(MetaWorkspaceManager * workspace_manager)1002 meta_workspace_manager_get_workspaces (MetaWorkspaceManager *workspace_manager)
1003 {
1004 return workspace_manager->workspaces;
1005 }
1006
1007 int
meta_workspace_manager_get_active_workspace_index(MetaWorkspaceManager * workspace_manager)1008 meta_workspace_manager_get_active_workspace_index (MetaWorkspaceManager *workspace_manager)
1009 {
1010 MetaWorkspace *active = workspace_manager->active_workspace;
1011
1012 if (!active)
1013 return -1;
1014
1015 return meta_workspace_index (active);
1016 }
1017
1018 /**
1019 * meta_workspace_manager_get_active_workspace:
1020 * @workspace_manager: A #MetaWorkspaceManager
1021 *
1022 * Returns: (transfer none): The current workspace
1023 */
1024 MetaWorkspace *
meta_workspace_manager_get_active_workspace(MetaWorkspaceManager * workspace_manager)1025 meta_workspace_manager_get_active_workspace (MetaWorkspaceManager *workspace_manager)
1026 {
1027 return workspace_manager->active_workspace;
1028 }
1029
1030 void
meta_workspace_manager_workspace_switched(MetaWorkspaceManager * workspace_manager,int from,int to,MetaMotionDirection direction)1031 meta_workspace_manager_workspace_switched (MetaWorkspaceManager *workspace_manager,
1032 int from,
1033 int to,
1034 MetaMotionDirection direction)
1035 {
1036 g_signal_emit (workspace_manager,
1037 workspace_manager_signals[WORKSPACE_SWITCHED], 0,
1038 from, to, direction);
1039 }
1040
1041 static void
prefs_changed_callback(MetaPreference pref,gpointer data)1042 prefs_changed_callback (MetaPreference pref,
1043 gpointer data)
1044 {
1045 MetaWorkspaceManager *workspace_manager = data;
1046
1047 if ((pref == META_PREF_NUM_WORKSPACES ||
1048 pref == META_PREF_DYNAMIC_WORKSPACES) &&
1049 !meta_prefs_get_dynamic_workspaces ())
1050 {
1051 guint32 timestamp;
1052 int new_num;
1053
1054 timestamp =
1055 meta_display_get_current_time_roundtrip (workspace_manager->display);
1056 new_num = meta_prefs_get_num_workspaces ();
1057 meta_workspace_manager_update_num_workspaces (workspace_manager,
1058 timestamp, new_num);
1059 }
1060 }
1061