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