1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* Marco Workspaces */
4
5 /*
6 * Copyright (C) 2001 Havoc Pennington
7 * Copyright (C) 2003 Rob Adams
8 * Copyright (C) 2004, 2005 Elijah Newren
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301, USA.
24 */
25
26 #include <config.h>
27 #include "workspace.h"
28 #include "errors.h"
29 #include "prefs.h"
30 #include <X11/Xatom.h>
31 #include <string.h>
32 #include <canberra-gtk.h>
33
34 void meta_workspace_queue_calc_showing (MetaWorkspace *workspace);
35 static void set_active_space_hint (MetaScreen *screen);
36 static void focus_ancestor_or_top_window (MetaWorkspace *workspace,
37 MetaWindow *not_this_one,
38 guint32 timestamp);
39 static void free_this (gpointer candidate,
40 gpointer dummy);
41 static void workspace_free_struts (MetaWorkspace *workspace);
42
43 static void
maybe_add_to_list(MetaScreen * screen,MetaWindow * window,gpointer data)44 maybe_add_to_list (MetaScreen *screen, MetaWindow *window, gpointer data)
45 {
46 GList **mru_list = data;
47
48 if (window->on_all_workspaces)
49 *mru_list = g_list_prepend (*mru_list, window);
50 }
51
52 MetaWorkspace*
meta_workspace_new(MetaScreen * screen)53 meta_workspace_new (MetaScreen *screen)
54 {
55 MetaWorkspace *workspace;
56
57 workspace = g_new (MetaWorkspace, 1);
58
59 workspace->screen = screen;
60 workspace->screen->workspaces =
61 g_list_append (workspace->screen->workspaces, workspace);
62 workspace->windows = NULL;
63 workspace->mru_list = NULL;
64 meta_screen_foreach_window (screen, maybe_add_to_list, &workspace->mru_list);
65
66 workspace->work_areas_invalid = TRUE;
67 workspace->work_area_xinerama = NULL;
68 workspace->work_area_screen.x = 0;
69 workspace->work_area_screen.y = 0;
70 workspace->work_area_screen.width = 0;
71 workspace->work_area_screen.height = 0;
72
73 workspace->screen_region = NULL;
74 workspace->xinerama_region = NULL;
75 workspace->screen_edges = NULL;
76 workspace->xinerama_edges = NULL;
77 workspace->list_containing_self = g_list_prepend (NULL, workspace);
78
79 workspace->all_struts = NULL;
80
81 workspace->showing_desktop = FALSE;
82
83 return workspace;
84 }
85
86 /** Foreach function for workspace_free_struts() */
87 static void
free_this(gpointer candidate,gpointer dummy)88 free_this (gpointer candidate, gpointer dummy)
89 {
90 g_free (candidate);
91 }
92
93 /**
94 * Frees the struts list of a workspace.
95 *
96 * \param workspace The workspace.
97 */
98 static void
workspace_free_struts(MetaWorkspace * workspace)99 workspace_free_struts (MetaWorkspace *workspace)
100 {
101 if (workspace->all_struts == NULL)
102 return;
103
104 g_slist_foreach (workspace->all_struts, free_this, NULL);
105 g_slist_free (workspace->all_struts);
106 workspace->all_struts = NULL;
107 }
108
109 void
meta_workspace_free(MetaWorkspace * workspace)110 meta_workspace_free (MetaWorkspace *workspace)
111 {
112 GList *tmp;
113 MetaScreen *screen;
114 int i;
115
116 g_return_if_fail (workspace != workspace->screen->active_workspace);
117
118 /* Here we assume all the windows are already on another workspace
119 * as well, so they won't be "orphaned"
120 */
121
122 tmp = workspace->windows;
123 while (tmp != NULL)
124 {
125 GList *next;
126 MetaWindow *window = tmp->data;
127 next = tmp->next;
128
129 /* pop front of list we're iterating over */
130 meta_workspace_remove_window (workspace, window);
131 g_assert (window->workspace != NULL);
132
133 tmp = next;
134 }
135
136 g_assert (workspace->windows == NULL);
137
138 screen = workspace->screen;
139
140 workspace->screen->workspaces =
141 g_list_remove (workspace->screen->workspaces, workspace);
142
143 g_free (workspace->work_area_xinerama);
144
145 g_list_free (workspace->mru_list);
146 g_list_free (workspace->list_containing_self);
147
148 /* screen.c:update_num_workspaces(), which calls us, removes windows from
149 * workspaces first, which can cause the workareas on the workspace to be
150 * invalidated (and hence for struts/regions/edges to be freed).
151 * So, no point trying to double free it; that causes a crash
152 * anyway. #361804.
153 */
154
155 if (!workspace->work_areas_invalid)
156 {
157 workspace_free_struts (workspace);
158 for (i = 0; i < screen->n_xinerama_infos; i++)
159 g_list_free_full (workspace->xinerama_region[i], g_free);
160 g_free (workspace->xinerama_region);
161 g_list_free_full (workspace->screen_region, g_free);
162 g_list_free_full (workspace->screen_edges, g_free);
163 g_list_free_full (workspace->xinerama_edges, g_free);
164 }
165
166 g_free (workspace);
167
168 /* don't bother to reset names, pagers can just ignore
169 * extra ones
170 */
171 }
172
173 void
meta_workspace_add_window(MetaWorkspace * workspace,MetaWindow * window)174 meta_workspace_add_window (MetaWorkspace *workspace,
175 MetaWindow *window)
176 {
177 g_return_if_fail (window->workspace == NULL);
178
179 /* If the window is on all workspaces, we want to add it to all mru
180 * lists, otherwise just add it to this workspaces mru list
181 */
182 if (window->on_all_workspaces)
183 {
184 if (window->workspace == NULL)
185 {
186 GList* tmp = window->screen->workspaces;
187 while (tmp)
188 {
189 MetaWorkspace* work = (MetaWorkspace*) tmp->data;
190 if (!g_list_find (work->mru_list, window))
191 work->mru_list = g_list_prepend (work->mru_list, window);
192
193 tmp = tmp->next;
194 }
195 }
196 }
197 else
198 {
199 g_assert (g_list_find (workspace->mru_list, window) == NULL);
200 workspace->mru_list = g_list_prepend (workspace->mru_list, window);
201 }
202
203 workspace->windows = g_list_prepend (workspace->windows, window);
204 window->workspace = workspace;
205
206 meta_window_set_current_workspace_hint (window);
207
208 if (window->struts)
209 {
210 meta_topic (META_DEBUG_WORKAREA,
211 "Invalidating work area of workspace %d since we're adding window %s to it\n",
212 meta_workspace_index (workspace), window->desc);
213 meta_workspace_invalidate_work_area (workspace);
214 }
215
216 /* queue a move_resize since changing workspaces may change
217 * the relevant struts
218 */
219 meta_window_queue (window, META_QUEUE_CALC_SHOWING|META_QUEUE_MOVE_RESIZE);
220 }
221
222 void
meta_workspace_remove_window(MetaWorkspace * workspace,MetaWindow * window)223 meta_workspace_remove_window (MetaWorkspace *workspace,
224 MetaWindow *window)
225 {
226 g_return_if_fail (window->workspace == workspace);
227
228 workspace->windows = g_list_remove (workspace->windows, window);
229 window->workspace = NULL;
230
231 /* If the window is on all workspaces, we don't want to remove it
232 * from the MRU list unless this causes it to be removed from all
233 * workspaces
234 */
235 if (window->on_all_workspaces)
236 {
237 GList* tmp = window->screen->workspaces;
238 while (tmp)
239 {
240 MetaWorkspace* work = (MetaWorkspace*) tmp->data;
241 work->mru_list = g_list_remove (work->mru_list, window);
242
243 tmp = tmp->next;
244 }
245 }
246 else
247 {
248 workspace->mru_list = g_list_remove (workspace->mru_list, window);
249 g_assert (g_list_find (workspace->mru_list, window) == NULL);
250 }
251
252 meta_window_set_current_workspace_hint (window);
253
254 if (window->struts)
255 {
256 meta_topic (META_DEBUG_WORKAREA,
257 "Invalidating work area of workspace %d since we're removing window %s from it\n",
258 meta_workspace_index (workspace), window->desc);
259 meta_workspace_invalidate_work_area (workspace);
260 }
261
262 /* queue a move_resize since changing workspaces may change
263 * the relevant struts
264 */
265 meta_window_queue (window, META_QUEUE_CALC_SHOWING|META_QUEUE_MOVE_RESIZE);
266 }
267
268 void
meta_workspace_relocate_windows(MetaWorkspace * workspace,MetaWorkspace * new_home)269 meta_workspace_relocate_windows (MetaWorkspace *workspace,
270 MetaWorkspace *new_home)
271 {
272 GList *tmp;
273 GList *copy;
274
275 g_return_if_fail (workspace != new_home);
276
277 /* can't modify list we're iterating over */
278 copy = g_list_copy (workspace->windows);
279
280 tmp = copy;
281 while (tmp != NULL)
282 {
283 MetaWindow *window = tmp->data;
284
285 meta_workspace_remove_window (workspace, window);
286 meta_workspace_add_window (new_home, window);
287
288 tmp = tmp->next;
289 }
290
291 g_list_free (copy);
292
293 g_assert (workspace->windows == NULL);
294 }
295
296 void
meta_workspace_queue_calc_showing(MetaWorkspace * workspace)297 meta_workspace_queue_calc_showing (MetaWorkspace *workspace)
298 {
299 GList *tmp;
300
301 tmp = workspace->windows;
302 while (tmp != NULL)
303 {
304 meta_window_queue (tmp->data, META_QUEUE_CALC_SHOWING);
305
306 tmp = tmp->next;
307 }
308 }
309
workspace_switch_sound(MetaWorkspace * from,MetaWorkspace * to)310 static void workspace_switch_sound(MetaWorkspace *from,
311 MetaWorkspace *to) {
312
313 MetaWorkspaceLayout layout;
314 int i, nw, x, y, fi, ti;
315 const char *e;
316
317 nw = meta_screen_get_n_workspaces(from->screen);
318 fi = meta_workspace_index(from);
319 ti = meta_workspace_index(to);
320
321 meta_screen_calc_workspace_layout(from->screen,
322 nw,
323 fi,
324 &layout);
325
326 for (i = 0; i < nw; i++)
327 if (layout.grid[i] == ti)
328 break;
329
330 if (i >= nw) {
331 meta_bug("Failed to find destination workspace in layout\n");
332 goto finish;
333 }
334
335 y = i / layout.cols;
336 x = i % layout.cols;
337
338 /* We priorize horizontal over vertical movements here. The
339 rationale for this is that horizontal movements are probably more
340 interesting for sound effects because speakers are usually
341 positioned on a horizontal and not a vertical axis. i.e. your
342 spatial "Woosh!" effects will easily be able to encode horizontal
343 movement but not such much vertical movement. */
344
345 if (x < layout.current_col)
346 e = "desktop-switch-left";
347 else if (x > layout.current_col)
348 e = "desktop-switch-right";
349 else if (y < layout.current_row)
350 e = "desktop-switch-up";
351 else if (y > layout.current_row)
352 e = "desktop-switch-down";
353 else {
354 meta_bug("Uh, origin and destination workspace at same logic position!\n");
355 goto finish;
356 }
357
358 ca_context_play(ca_gtk_context_get(), 1,
359 CA_PROP_EVENT_ID, e,
360 CA_PROP_EVENT_DESCRIPTION, "Desktop switched",
361 CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
362 NULL);
363
364 finish:
365 meta_screen_free_workspace_layout (&layout);
366 }
367
368 void
meta_workspace_activate_with_focus(MetaWorkspace * workspace,MetaWindow * focus_this,guint32 timestamp)369 meta_workspace_activate_with_focus (MetaWorkspace *workspace,
370 MetaWindow *focus_this,
371 guint32 timestamp)
372 {
373 MetaWorkspace *old;
374 MetaWindow *move_window;
375
376 meta_verbose ("Activating workspace %d\n",
377 meta_workspace_index (workspace));
378
379 if (workspace->screen->active_workspace == workspace)
380 return;
381
382 if (workspace->screen->active_workspace)
383 workspace_switch_sound(workspace->screen->active_workspace, workspace);
384
385 /* Free any cached pointers to the workspaces's edges from
386 * a current resize or move operation */
387 meta_display_cleanup_edges (workspace->screen->display);
388
389 /* Note that old can be NULL; e.g. when starting up */
390 old = workspace->screen->active_workspace;
391
392 /* Save old workspace, to be able to switch back. */
393 workspace->screen->prev_workspace = old;
394
395 workspace->screen->active_workspace = workspace;
396
397 set_active_space_hint (workspace->screen);
398
399 /* If the "show desktop" mode is active for either the old workspace
400 * or the new one *but not both*, then update the
401 * _net_showing_desktop hint
402 */
403 if (old && (old->showing_desktop ^ workspace->showing_desktop))
404 meta_screen_update_showing_desktop_hint (workspace->screen);
405
406 if (old == NULL)
407 return;
408
409 move_window = NULL;
410 if (workspace->screen->display->grab_op == META_GRAB_OP_MOVING ||
411 workspace->screen->display->grab_op == META_GRAB_OP_KEYBOARD_MOVING)
412 move_window = workspace->screen->display->grab_window;
413
414 if (move_window != NULL)
415 {
416 if (move_window->on_all_workspaces)
417 move_window = NULL; /* don't move it after all */
418
419 /* We put the window on the new workspace, flip spaces,
420 * then remove from old workspace, so the window
421 * never gets unmapped and we maintain the button grab
422 * on it.
423 *
424 * \bug This comment appears to be the reverse of what happens
425 */
426 if (move_window && (move_window->workspace != workspace))
427 {
428 meta_workspace_remove_window (old, move_window);
429 meta_workspace_add_window (workspace, move_window);
430 }
431 }
432
433 meta_workspace_queue_calc_showing (old);
434 meta_workspace_queue_calc_showing (workspace);
435
436 /* FIXME: Why do we need this?!? Isn't it handled in the lines above? */
437 if (move_window)
438 /* Removes window from other spaces */
439 meta_window_change_workspace (move_window, workspace);
440
441 if (focus_this)
442 {
443 meta_window_focus (focus_this, timestamp);
444 meta_window_raise (focus_this);
445 }
446 else if (move_window)
447 {
448 meta_window_raise (move_window);
449 }
450 else
451 {
452 meta_topic (META_DEBUG_FOCUS, "Focusing default window on new workspace\n");
453 meta_workspace_focus_default_window (workspace, NULL, timestamp);
454 }
455 }
456
457 void
meta_workspace_activate(MetaWorkspace * workspace,guint32 timestamp)458 meta_workspace_activate (MetaWorkspace *workspace,
459 guint32 timestamp)
460 {
461 meta_workspace_activate_with_focus (workspace, NULL, timestamp);
462 }
463
464 int
meta_workspace_index(MetaWorkspace * workspace)465 meta_workspace_index (MetaWorkspace *workspace)
466 {
467 int ret;
468
469 ret = g_list_index (workspace->screen->workspaces, workspace);
470
471 if (ret < 0)
472 meta_bug ("Workspace does not exist to index!\n");
473
474 return ret;
475 }
476
477 /* get windows contained on workspace, including workspace->windows
478 * and also sticky windows.
479 */
480 GList*
meta_workspace_list_windows(MetaWorkspace * workspace)481 meta_workspace_list_windows (MetaWorkspace *workspace)
482 {
483 GSList *display_windows;
484 GSList *tmp;
485 GList *workspace_windows;
486
487 display_windows = meta_display_list_windows (workspace->screen->display);
488
489 workspace_windows = NULL;
490 tmp = display_windows;
491 while (tmp != NULL)
492 {
493 MetaWindow *window = tmp->data;
494
495 if (meta_window_located_on_workspace (window, workspace))
496 workspace_windows = g_list_prepend (workspace_windows,
497 window);
498
499 tmp = tmp->next;
500 }
501
502 g_slist_free (display_windows);
503
504 return workspace_windows;
505 }
506
507 static void
set_active_space_hint(MetaScreen * screen)508 set_active_space_hint (MetaScreen *screen)
509 {
510 unsigned long data[1];
511
512 /* this is because we destroy the spaces in order,
513 * so we always end up setting a current desktop of
514 * 0 when closing a screen, so lose the current desktop
515 * on restart. By doing this we keep the current
516 * desktop on restart.
517 */
518 if (screen->closing > 0)
519 return;
520
521 data[0] = meta_workspace_index (screen->active_workspace);
522
523 meta_verbose ("Setting _NET_CURRENT_DESKTOP to %lu\n", data[0]);
524
525 meta_error_trap_push (screen->display);
526 XChangeProperty (screen->display->xdisplay, screen->xroot,
527 screen->display->atom__NET_CURRENT_DESKTOP,
528 XA_CARDINAL,
529 32, PropModeReplace, (guchar*) data, 1);
530 meta_error_trap_pop (screen->display, FALSE);
531 }
532
533 void
meta_workspace_invalidate_work_area(MetaWorkspace * workspace)534 meta_workspace_invalidate_work_area (MetaWorkspace *workspace)
535 {
536 GList *tmp;
537 GList *windows;
538 int i;
539
540 if (workspace->work_areas_invalid)
541 {
542 meta_topic (META_DEBUG_WORKAREA,
543 "Work area for workspace %d is already invalid\n",
544 meta_workspace_index (workspace));
545 return;
546 }
547
548 meta_topic (META_DEBUG_WORKAREA,
549 "Invalidating work area for workspace %d\n",
550 meta_workspace_index (workspace));
551
552 /* If we are in the middle of a resize or move operation, we
553 * might have cached pointers to the workspace's edges */
554 if (workspace == workspace->screen->active_workspace)
555 meta_display_cleanup_edges (workspace->screen->display);
556
557 g_free (workspace->work_area_xinerama);
558 workspace->work_area_xinerama = NULL;
559
560 workspace_free_struts (workspace);
561
562 for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
563 g_list_free_full (workspace->xinerama_region[i], g_free);
564 g_free (workspace->xinerama_region);
565 g_list_free_full (workspace->screen_region, g_free);
566 g_list_free_full (workspace->screen_edges, g_free);
567 g_list_free_full (workspace->xinerama_edges, g_free);
568 workspace->xinerama_region = NULL;
569 workspace->screen_region = NULL;
570 workspace->screen_edges = NULL;
571 workspace->xinerama_edges = NULL;
572
573 workspace->work_areas_invalid = TRUE;
574
575 /* redo the size/position constraints on all windows */
576 windows = meta_workspace_list_windows (workspace);
577 tmp = windows;
578 while (tmp != NULL)
579 {
580 MetaWindow *w = tmp->data;
581
582 meta_window_queue (w, META_QUEUE_MOVE_RESIZE);
583
584 tmp = tmp->next;
585 }
586
587 g_list_free (windows);
588
589 meta_screen_queue_workarea_recalc (workspace->screen);
590 }
591
592 static void
ensure_work_areas_validated(MetaWorkspace * workspace)593 ensure_work_areas_validated (MetaWorkspace *workspace)
594 {
595 GList *windows;
596 GList *tmp;
597 MetaRectangle work_area;
598 int i; /* C89 absolutely sucks... */
599
600 if (!workspace->work_areas_invalid)
601 return;
602
603 g_assert (workspace->all_struts == NULL);
604 g_assert (workspace->xinerama_region == NULL);
605 g_assert (workspace->screen_region == NULL);
606 g_assert (workspace->screen_edges == NULL);
607 g_assert (workspace->xinerama_edges == NULL);
608
609 /* STEP 1: Get the list of struts */
610 windows = meta_workspace_list_windows (workspace);
611 for (tmp = windows; tmp != NULL; tmp = tmp->next)
612 {
613 MetaWindow *win = tmp->data;
614 GSList *s_iter;
615
616 for (s_iter = win->struts; s_iter != NULL; s_iter = s_iter->next) {
617 MetaStrut *cpy = g_new (MetaStrut, 1);
618 *cpy = *((MetaStrut *)s_iter->data);
619 workspace->all_struts = g_slist_prepend (workspace->all_struts,
620 cpy);
621 }
622 }
623 g_list_free (windows);
624
625 /* STEP 2: Get the maximal/spanning rects for the onscreen and
626 * on-single-xinerama regions
627 */
628 g_assert (workspace->xinerama_region == NULL);
629 g_assert (workspace->screen_region == NULL);
630
631 workspace->xinerama_region = g_new (GList*,
632 workspace->screen->n_xinerama_infos);
633 for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
634 {
635 workspace->xinerama_region[i] =
636 meta_rectangle_get_minimal_spanning_set_for_region (
637 &workspace->screen->xinerama_infos[i].rect,
638 workspace->all_struts,
639 FALSE);
640 }
641 workspace->screen_region =
642 meta_rectangle_get_minimal_spanning_set_for_region (
643 &workspace->screen->rect,
644 workspace->all_struts,
645 TRUE);
646
647 /* STEP 3: Get the work areas (region-to-maximize-to) for the screen and
648 * xineramas.
649 */
650 work_area = workspace->screen->rect; /* start with the screen */
651 if (workspace->screen_region == NULL)
652 work_area = meta_rect (0, 0, -1, -1);
653 else
654 meta_rectangle_clip_to_region (workspace->screen_region,
655 FIXED_DIRECTION_NONE,
656 &work_area);
657
658 /* Lots of paranoia checks, forcing work_area_screen to be sane */
659 #define MIN_SANE_AREA 100
660 if (work_area.width < MIN_SANE_AREA)
661 {
662 meta_warning ("struts occupy an unusually large percentage of the screen; "
663 "available remaining width = %d < %d",
664 work_area.width, MIN_SANE_AREA);
665 if (work_area.width < 1)
666 {
667 work_area.x = (workspace->screen->rect.width - MIN_SANE_AREA)/2;
668 work_area.width = MIN_SANE_AREA;
669 }
670 else
671 {
672 int amount = (MIN_SANE_AREA - work_area.width)/2;
673 work_area.x -= amount;
674 work_area.width += 2*amount;
675 }
676 }
677 if (work_area.height < MIN_SANE_AREA)
678 {
679 meta_warning ("struts occupy an unusually large percentage of the screen; "
680 "available remaining height = %d < %d",
681 work_area.height, MIN_SANE_AREA);
682 if (work_area.height < 1)
683 {
684 work_area.y = (workspace->screen->rect.height - MIN_SANE_AREA)/2;
685 work_area.height = MIN_SANE_AREA;
686 }
687 else
688 {
689 int amount = (MIN_SANE_AREA - work_area.height)/2;
690 work_area.y -= amount;
691 work_area.height += 2*amount;
692 }
693 }
694 workspace->work_area_screen = work_area;
695 meta_topic (META_DEBUG_WORKAREA,
696 "Computed work area for workspace %d: %d,%d %d x %d\n",
697 meta_workspace_index (workspace),
698 workspace->work_area_screen.x,
699 workspace->work_area_screen.y,
700 workspace->work_area_screen.width,
701 workspace->work_area_screen.height);
702
703 /* Now find the work areas for each xinerama */
704 g_free (workspace->work_area_xinerama);
705 workspace->work_area_xinerama = g_new (MetaRectangle,
706 workspace->screen->n_xinerama_infos);
707
708 for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
709 {
710 work_area = workspace->screen->xinerama_infos[i].rect;
711
712 if (workspace->xinerama_region[i] == NULL)
713 /* FIXME: constraints.c untested with this, but it might be nice for
714 * a screen reader or magnifier.
715 */
716 work_area = meta_rect (work_area.x, work_area.y, -1, -1);
717 else
718 meta_rectangle_clip_to_region (workspace->xinerama_region[i],
719 FIXED_DIRECTION_NONE,
720 &work_area);
721
722 workspace->work_area_xinerama[i] = work_area;
723 meta_topic (META_DEBUG_WORKAREA,
724 "Computed work area for workspace %d "
725 "xinerama %d: %d,%d %d x %d\n",
726 meta_workspace_index (workspace),
727 i,
728 workspace->work_area_xinerama[i].x,
729 workspace->work_area_xinerama[i].y,
730 workspace->work_area_xinerama[i].width,
731 workspace->work_area_xinerama[i].height);
732 }
733
734 /* STEP 4: Make sure the screen_region is nonempty (separate from step 2
735 * since it relies on step 3).
736 */
737 if (workspace->screen_region == NULL)
738 {
739 MetaRectangle *nonempty_region;
740 nonempty_region = g_new (MetaRectangle, 1);
741 *nonempty_region = workspace->work_area_screen;
742 workspace->screen_region = g_list_prepend (NULL, nonempty_region);
743 }
744
745 /* STEP 5: Cache screen and xinerama edges for edge resistance and snapping */
746 g_assert (workspace->screen_edges == NULL);
747 g_assert (workspace->xinerama_edges == NULL);
748 workspace->screen_edges =
749 meta_rectangle_find_onscreen_edges (&workspace->screen->rect,
750 workspace->all_struts);
751 tmp = NULL;
752 for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
753 tmp = g_list_prepend (tmp, &workspace->screen->xinerama_infos[i].rect);
754 workspace->xinerama_edges =
755 meta_rectangle_find_nonintersected_xinerama_edges (&workspace->screen->rect, tmp, workspace->all_struts);
756 g_list_free (tmp);
757
758 /* We're all done, YAAY! Record that everything has been validated. */
759 workspace->work_areas_invalid = FALSE;
760 }
761
762 void
meta_workspace_get_work_area_for_xinerama(MetaWorkspace * workspace,int which_xinerama,MetaRectangle * area)763 meta_workspace_get_work_area_for_xinerama (MetaWorkspace *workspace,
764 int which_xinerama,
765 MetaRectangle *area)
766 {
767 g_assert (which_xinerama >= 0);
768
769 ensure_work_areas_validated (workspace);
770 g_assert (which_xinerama < workspace->screen->n_xinerama_infos);
771
772 *area = workspace->work_area_xinerama[which_xinerama];
773 }
774
775 void
meta_workspace_get_work_area_all_xineramas(MetaWorkspace * workspace,MetaRectangle * area)776 meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace,
777 MetaRectangle *area)
778 {
779 ensure_work_areas_validated (workspace);
780
781 *area = workspace->work_area_screen;
782 }
783
784 GList*
meta_workspace_get_onscreen_region(MetaWorkspace * workspace)785 meta_workspace_get_onscreen_region (MetaWorkspace *workspace)
786 {
787 ensure_work_areas_validated (workspace);
788
789 return workspace->screen_region;
790 }
791
792 GList*
meta_workspace_get_onxinerama_region(MetaWorkspace * workspace,int which_xinerama)793 meta_workspace_get_onxinerama_region (MetaWorkspace *workspace,
794 int which_xinerama)
795 {
796 ensure_work_areas_validated (workspace);
797
798 return workspace->xinerama_region[which_xinerama];
799 }
800
801 #ifdef WITH_VERBOSE_MODE
802 static char *
meta_motion_direction_to_string(MetaMotionDirection direction)803 meta_motion_direction_to_string (MetaMotionDirection direction)
804 {
805 switch (direction)
806 {
807 case META_MOTION_UP:
808 return "Up";
809 case META_MOTION_DOWN:
810 return "Down";
811 case META_MOTION_LEFT:
812 return "Left";
813 case META_MOTION_RIGHT:
814 return "Right";
815 case META_MOTION_PREV:
816 return "Previous";
817 }
818
819 return "Unknown";
820 }
821 #endif /* WITH_VERBOSE_MODE */
822
823 MetaWorkspace*
meta_workspace_get_neighbor(MetaWorkspace * workspace,MetaMotionDirection direction)824 meta_workspace_get_neighbor (MetaWorkspace *workspace,
825 MetaMotionDirection direction)
826 {
827 MetaWorkspaceLayout layout;
828 int i, current_space, num_workspaces;
829 gboolean ltr;
830 MetaWrapStyle wrap;
831
832 current_space = meta_workspace_index (workspace);
833 num_workspaces = meta_screen_get_n_workspaces (workspace->screen);
834 meta_screen_calc_workspace_layout (workspace->screen, num_workspaces,
835 current_space, &layout);
836 wrap = meta_prefs_get_wrap_style();
837
838 meta_verbose ("Getting neighbor of %d in direction %s\n",
839 current_space, meta_motion_direction_to_string (direction));
840
841 ltr = meta_ui_get_direction() == META_UI_DIRECTION_LTR;
842
843 switch (direction)
844 {
845 case META_MOTION_LEFT:
846 layout.current_col -= ltr ? 1 : -1;
847 break;
848 case META_MOTION_RIGHT:
849 layout.current_col += ltr ? 1 : -1;
850 break;
851 case META_MOTION_UP:
852 layout.current_row -= 1;
853 break;
854 case META_MOTION_DOWN:
855 layout.current_row += 1;
856 break;
857 case META_MOTION_PREV:
858 break;
859 }
860
861 /* LEFT */
862 if (layout.current_col < 0)
863 switch (wrap)
864 {
865 case META_WRAP_NONE:
866 layout.current_col = 0;
867 break;
868 case META_WRAP_CLASSIC:
869 layout.current_row = layout.current_row > 0 ? layout.current_row - 1 : layout.rows - 1;
870 /* fall through */
871 case META_WRAP_TOROIDAL:
872 layout.current_col = layout.cols - 1;
873 }
874 /* RIGHT */
875 if (layout.current_col >= layout.cols)
876 switch (wrap)
877 {
878 case META_WRAP_NONE:
879 layout.current_col = layout.cols - 1;
880 break;
881 case META_WRAP_CLASSIC:
882 layout.current_row = layout.current_row < layout.rows - 1 ? layout.current_row + 1 : 0;
883 /* fall through */
884 case META_WRAP_TOROIDAL:
885 layout.current_col = 0;
886 }
887 /* UP */
888 if (layout.current_row < 0)
889 switch (wrap)
890 {
891 case META_WRAP_NONE:
892 layout.current_row = 0;
893 break;
894 case META_WRAP_CLASSIC:
895 layout.current_col = layout.current_col > 0 ? layout.current_col - 1 : layout.cols - 1;
896 /* fall through */
897 case META_WRAP_TOROIDAL:
898 layout.current_row = layout.rows - 1;
899 }
900 /* DOWN */
901 if (layout.current_row >= layout.rows)
902 switch (wrap)
903 {
904 case META_WRAP_NONE:
905 layout.current_row = layout.rows - 1;
906 break;
907 case META_WRAP_CLASSIC:
908 layout.current_col = layout.current_col < layout.cols - 1 ? layout.current_col + 1 : 0;
909 /* fall through */
910 case META_WRAP_TOROIDAL:
911 layout.current_row = 0;
912 }
913
914 /* If we have an uneven arrangement of workspaces, (layout.cols - n, layout.rows - 1) may be an invalid workspace
915 e.g. we have 7 workspaces on a 3x3 pane */
916 if (wrap != META_WRAP_NONE && (layout.current_row * layout.cols + layout.current_col >= num_workspaces))
917 switch (direction)
918 {
919 case META_MOTION_LEFT:
920 layout.current_col = num_workspaces - (layout.current_row * layout.cols + 1);
921 break;
922 case META_MOTION_RIGHT:
923 layout.current_col = 0;
924 if (wrap == META_WRAP_CLASSIC)
925 layout.current_row = 0;
926 break;
927 case META_MOTION_UP:
928 layout.current_row -= 1;
929 break;
930 case META_MOTION_DOWN:
931 layout.current_row = 0;
932 if (wrap == META_WRAP_CLASSIC)
933 layout.current_col = layout.current_col < layout.cols - 1 ? layout.current_col + 1 : 0;
934 break;
935 case META_MOTION_PREV:
936 break;
937 }
938
939 i = layout.grid[layout.current_row * layout.cols + layout.current_col];
940
941 if (i < 0)
942 i = current_space;
943
944 if (i >= num_workspaces)
945 meta_bug ("calc_workspace_layout left an invalid (too-high) workspace number %d in the grid\n",
946 i);
947
948 meta_verbose ("Neighbor workspace is %d at row %d col %d\n",
949 i, layout.current_row, layout.current_col);
950
951 meta_screen_free_workspace_layout (&layout);
952
953 return meta_screen_get_workspace_by_index (workspace->screen, i);
954 }
955
956 const char*
meta_workspace_get_name(MetaWorkspace * workspace)957 meta_workspace_get_name (MetaWorkspace *workspace)
958 {
959 return meta_prefs_get_workspace_name (meta_workspace_index (workspace));
960 }
961
962 void
meta_workspace_focus_default_window(MetaWorkspace * workspace,MetaWindow * not_this_one,guint32 timestamp)963 meta_workspace_focus_default_window (MetaWorkspace *workspace,
964 MetaWindow *not_this_one,
965 guint32 timestamp)
966 {
967 if (timestamp == CurrentTime)
968 {
969 meta_warning ("CurrentTime used to choose focus window; "
970 "focus window may not be correct.\n");
971 }
972
973 if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK ||
974 !workspace->screen->display->mouse_mode)
975 focus_ancestor_or_top_window (workspace, not_this_one, timestamp);
976 else
977 {
978 MetaWindow * window;
979 window = meta_screen_get_mouse_window (workspace->screen, not_this_one);
980 if (window &&
981 window->type != META_WINDOW_DOCK &&
982 window->type != META_WINDOW_DESKTOP)
983 {
984 if (timestamp == CurrentTime)
985 {
986
987 /* We would like for this to never happen. However, if
988 * it does happen then we kludge since using CurrentTime
989 * can mean ugly race conditions--and we can avoid these
990 * by allowing EnterNotify events (which come with
991 * timestamps) to handle focus.
992 */
993
994 meta_topic (META_DEBUG_FOCUS,
995 "Not focusing mouse window %s because EnterNotify events should handle that\n", window->desc);
996 }
997 else
998 {
999 meta_topic (META_DEBUG_FOCUS,
1000 "Focusing mouse window %s\n", window->desc);
1001 meta_window_focus (window, timestamp);
1002 }
1003
1004 if (workspace->screen->display->autoraise_window != window &&
1005 meta_prefs_get_auto_raise ())
1006 {
1007 meta_display_queue_autoraise_callback (workspace->screen->display,
1008 window);
1009 }
1010 }
1011 else if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_SLOPPY)
1012 focus_ancestor_or_top_window (workspace, not_this_one, timestamp);
1013 else if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_MOUSE)
1014 {
1015 meta_topic (META_DEBUG_FOCUS,
1016 "Setting focus to no_focus_window, since no valid "
1017 "window to focus found.\n");
1018 meta_display_focus_the_no_focus_window (workspace->screen->display,
1019 workspace->screen,
1020 timestamp);
1021 }
1022 }
1023 }
1024
1025 static gboolean
record_ancestor(MetaWindow * window,void * data)1026 record_ancestor (MetaWindow *window,
1027 void *data)
1028 {
1029 MetaWindow **result = data;
1030
1031 *result = window;
1032 return FALSE; /* quit with the first ancestor we find */
1033 }
1034
1035 /* Focus ancestor of not_this_one if there is one, otherwise focus the MRU
1036 * window on active workspace
1037 */
1038 static void
focus_ancestor_or_top_window(MetaWorkspace * workspace,MetaWindow * not_this_one,guint32 timestamp)1039 focus_ancestor_or_top_window (MetaWorkspace *workspace,
1040 MetaWindow *not_this_one,
1041 guint32 timestamp)
1042 {
1043 MetaWindow *window = NULL;
1044
1045 if (not_this_one)
1046 meta_topic (META_DEBUG_FOCUS,
1047 "Focusing MRU window excluding %s\n", not_this_one->desc);
1048 else
1049 meta_topic (META_DEBUG_FOCUS,
1050 "Focusing MRU window\n");
1051
1052 /* First, check to see if we need to focus an ancestor of a window */
1053 if (not_this_one)
1054 {
1055 MetaWindow *ancestor;
1056 ancestor = NULL;
1057 meta_window_foreach_ancestor (not_this_one, record_ancestor, &ancestor);
1058 if (ancestor != NULL)
1059 {
1060 meta_topic (META_DEBUG_FOCUS,
1061 "Focusing %s, ancestor of %s\n",
1062 ancestor->desc, not_this_one->desc);
1063
1064 meta_window_focus (ancestor, timestamp);
1065
1066 /* Also raise the window if in click-to-focus */
1067 if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
1068 meta_window_raise (ancestor);
1069
1070 return;
1071 }
1072 }
1073
1074 window = meta_stack_get_default_focus_window (workspace->screen->stack,
1075 workspace, NULL);
1076
1077 if (window)
1078 {
1079 meta_topic (META_DEBUG_FOCUS,
1080 "Focusing workspace MRU window %s\n", window->desc);
1081
1082 meta_window_focus (window, timestamp);
1083
1084 /* Also raise the window if in click-to-focus */
1085 if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
1086 meta_window_raise (window);
1087 }
1088 else
1089 {
1090 meta_topic (META_DEBUG_FOCUS, "No MRU window to focus found; focusing no_focus_window.\n");
1091 meta_display_focus_the_no_focus_window (workspace->screen->display,
1092 workspace->screen,
1093 timestamp);
1094 }
1095 }
1096