workspace.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity Workspaces */
00004 
00005 /* 
00006  * Copyright (C) 2001 Havoc Pennington
00007  * Copyright (C) 2003 Rob Adams
00008  * Copyright (C) 2004, 2005 Elijah Newren
00009  * 
00010  * This program is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU General Public License as
00012  * published by the Free Software Foundation; either version 2 of the
00013  * License, or (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * General Public License for more details.
00019  * 
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00023  * 02111-1307, USA.
00024  */
00025 
00026 #include <config.h>
00027 #include "workspace.h"
00028 #include "errors.h"
00029 #include "prefs.h"
00030 #include <X11/Xatom.h>
00031 #include <string.h>
00032 
00033 void meta_workspace_queue_calc_showing   (MetaWorkspace *workspace);
00034 static void set_active_space_hint        (MetaScreen *screen);
00035 static void focus_ancestor_or_mru_window (MetaWorkspace *workspace,
00036                                           MetaWindow    *not_this_one,
00037                                           guint32        timestamp);
00038 
00039 static void
00040 maybe_add_to_list (MetaScreen *screen, MetaWindow *window, gpointer data)
00041 {
00042   GList **mru_list = data;
00043 
00044   if (window->on_all_workspaces)
00045     *mru_list = g_list_prepend (*mru_list, window);
00046 }
00047 
00048 MetaWorkspace*
00049 meta_workspace_new (MetaScreen *screen)
00050 {
00051   MetaWorkspace *workspace;
00052 
00053   workspace = g_new (MetaWorkspace, 1);
00054 
00055   workspace->screen = screen;
00056   workspace->screen->workspaces =
00057     g_list_append (workspace->screen->workspaces, workspace);
00058   workspace->windows = NULL;
00059   workspace->mru_list = NULL;
00060   meta_screen_foreach_window (screen, maybe_add_to_list, &workspace->mru_list);
00061 
00062   workspace->work_areas_invalid = TRUE;
00063   workspace->work_area_xinerama = NULL;
00064   workspace->work_area_screen.x = 0;
00065   workspace->work_area_screen.y = 0;
00066   workspace->work_area_screen.width = 0;
00067   workspace->work_area_screen.height = 0;
00068 
00069   workspace->screen_region = NULL;
00070   workspace->xinerama_region = NULL;
00071   workspace->screen_edges = NULL;
00072   workspace->xinerama_edges = NULL;
00073   workspace->list_containing_self = g_list_prepend (NULL, workspace);
00074 
00075   workspace->all_struts = NULL;
00076 
00077   workspace->showing_desktop = FALSE;
00078   
00079   return workspace;
00080 }
00081 
00082 void
00083 meta_workspace_free (MetaWorkspace *workspace)
00084 {
00085   GList *tmp;
00086   MetaScreen *screen;
00087   int i;
00088 
00089   g_return_if_fail (workspace != workspace->screen->active_workspace);
00090 
00091   /* Here we assume all the windows are already on another workspace
00092    * as well, so they won't be "orphaned"
00093    */
00094   
00095   tmp = workspace->windows;
00096   while (tmp != NULL)
00097     {
00098       GList *next;
00099       MetaWindow *window = tmp->data;
00100       next = tmp->next;
00101 
00102       /* pop front of list we're iterating over */
00103       meta_workspace_remove_window (workspace, window);
00104       g_assert (window->workspace != NULL);
00105 
00106       tmp = next;
00107     }
00108 
00109   g_assert (workspace->windows == NULL);
00110 
00111   screen = workspace->screen;
00112   
00113   workspace->screen->workspaces =
00114     g_list_remove (workspace->screen->workspaces, workspace);
00115   
00116   g_free (workspace->work_area_xinerama);
00117 
00118   g_list_free (workspace->mru_list);
00119   g_list_free (workspace->list_containing_self);
00120 
00121   /* screen.c:update_num_workspaces(), which calls us, removes windows from
00122    * workspaces first, which can cause the workareas on the workspace to be
00123    * invalidated (and hence for struts/regions/edges to be freed).
00124    * So, no point trying to double free it; that causes a crash
00125    * anyway.  #361804.
00126    */
00127 
00128   if (!workspace->work_areas_invalid)
00129     {
00130       g_slist_free (workspace->all_struts);
00131       for (i = 0; i < screen->n_xinerama_infos; i++)
00132         meta_rectangle_free_list_and_elements (workspace->xinerama_region[i]);
00133       g_free (workspace->xinerama_region);
00134       meta_rectangle_free_list_and_elements (workspace->screen_region);
00135       meta_rectangle_free_list_and_elements (workspace->screen_edges);
00136       meta_rectangle_free_list_and_elements (workspace->xinerama_edges);
00137     }
00138 
00139   g_free (workspace);
00140 
00141   /* don't bother to reset names, pagers can just ignore
00142    * extra ones
00143    */
00144 }
00145 
00146 void
00147 meta_workspace_add_window (MetaWorkspace *workspace,
00148                            MetaWindow    *window)
00149 {
00150   g_return_if_fail (window->workspace == NULL);
00151   
00152   /* If the window is on all workspaces, we want to add it to all mru
00153    * lists, otherwise just add it to this workspaces mru list
00154    */
00155   if (window->on_all_workspaces) 
00156     {
00157       if (window->workspace == NULL)
00158         {
00159           GList* tmp = window->screen->workspaces;
00160           while (tmp)
00161             {
00162               MetaWorkspace* work = (MetaWorkspace*) tmp->data;
00163               if (!g_list_find (work->mru_list, window))
00164                 work->mru_list = g_list_prepend (work->mru_list, window);
00165 
00166               tmp = tmp->next;
00167             }
00168         }
00169     }
00170   else
00171     {
00172       g_assert (g_list_find (workspace->mru_list, window) == NULL);
00173       workspace->mru_list = g_list_prepend (workspace->mru_list, window);
00174     }
00175 
00176   workspace->windows = g_list_prepend (workspace->windows, window);
00177   window->workspace = workspace;
00178 
00179   meta_window_set_current_workspace_hint (window);
00180   
00181   if (window->struts)
00182     {
00183       meta_topic (META_DEBUG_WORKAREA,
00184                   "Invalidating work area of workspace %d since we're adding window %s to it\n",
00185                   meta_workspace_index (workspace), window->desc);
00186       meta_workspace_invalidate_work_area (workspace);
00187     }
00188 
00189   /* queue a move_resize since changing workspaces may change
00190    * the relevant struts
00191    */
00192   meta_window_queue (window, META_QUEUE_CALC_SHOWING|META_QUEUE_MOVE_RESIZE);
00193 }
00194 
00195 void
00196 meta_workspace_remove_window (MetaWorkspace *workspace,
00197                               MetaWindow    *window)
00198 {
00199   g_return_if_fail (window->workspace == workspace);
00200 
00201   workspace->windows = g_list_remove (workspace->windows, window);
00202   window->workspace = NULL;
00203 
00204   /* If the window is on all workspaces, we don't want to remove it
00205    * from the MRU list unless this causes it to be removed from all 
00206    * workspaces
00207    */
00208   if (window->on_all_workspaces) 
00209     {
00210       GList* tmp = window->screen->workspaces;
00211       while (tmp)
00212         {
00213           MetaWorkspace* work = (MetaWorkspace*) tmp->data;
00214           work->mru_list = g_list_remove (work->mru_list, window);
00215 
00216           tmp = tmp->next;
00217         }
00218     }
00219   else
00220     {
00221       workspace->mru_list = g_list_remove (workspace->mru_list, window);
00222       g_assert (g_list_find (workspace->mru_list, window) == NULL);
00223     }
00224 
00225   meta_window_set_current_workspace_hint (window);
00226   
00227   if (window->struts)
00228     {
00229       meta_topic (META_DEBUG_WORKAREA,
00230                   "Invalidating work area of workspace %d since we're removing window %s from it\n",
00231                   meta_workspace_index (workspace), window->desc);
00232       meta_workspace_invalidate_work_area (workspace);
00233     }
00234 
00235   /* queue a move_resize since changing workspaces may change
00236    * the relevant struts
00237    */
00238   meta_window_queue (window, META_QUEUE_CALC_SHOWING|META_QUEUE_MOVE_RESIZE);
00239 }
00240 
00241 void
00242 meta_workspace_relocate_windows (MetaWorkspace *workspace,
00243                                  MetaWorkspace *new_home)
00244 {
00245   GList *tmp;
00246   GList *copy;
00247   
00248   g_return_if_fail (workspace != new_home);
00249 
00250   /* can't modify list we're iterating over */
00251   copy = g_list_copy (workspace->windows);
00252   
00253   tmp = copy;
00254   while (tmp != NULL)
00255     {
00256       MetaWindow *window = tmp->data;
00257 
00258       meta_workspace_remove_window (workspace, window);
00259       meta_workspace_add_window (new_home, window);
00260       
00261       tmp = tmp->next;
00262     }
00263 
00264   g_list_free (copy);
00265   
00266   g_assert (workspace->windows == NULL);
00267 }
00268 
00269 void
00270 meta_workspace_queue_calc_showing  (MetaWorkspace *workspace)
00271 {
00272   GList *tmp;
00273 
00274   tmp = workspace->windows;
00275   while (tmp != NULL)
00276     {
00277       meta_window_queue (tmp->data, META_QUEUE_CALC_SHOWING);
00278 
00279       tmp = tmp->next;
00280     }
00281 }
00282 
00283 void
00284 meta_workspace_activate_with_focus (MetaWorkspace *workspace,
00285                                     MetaWindow    *focus_this,
00286                                     guint32        timestamp)
00287 {
00288   MetaWorkspace *old;
00289   MetaWindow *move_window;
00290   
00291   meta_verbose ("Activating workspace %d\n",
00292                 meta_workspace_index (workspace));
00293   
00294   if (workspace->screen->active_workspace == workspace)
00295     return;
00296 
00297   /* Note that old can be NULL; e.g. when starting up */
00298   old = workspace->screen->active_workspace;
00299   
00300   workspace->screen->active_workspace = workspace;
00301 
00302   set_active_space_hint (workspace->screen);
00303 
00304   /* If the "show desktop" mode is active for either the old workspace
00305    * or the new one *but not both*, then update the
00306    * _net_showing_desktop hint
00307    */
00308   if (old && (old->showing_desktop ^ workspace->showing_desktop))
00309     meta_screen_update_showing_desktop_hint (workspace->screen);
00310 
00311   if (old == NULL)
00312     return;
00313 
00314   move_window = NULL;
00315   if (workspace->screen->display->grab_op == META_GRAB_OP_MOVING ||
00316       workspace->screen->display->grab_op == META_GRAB_OP_KEYBOARD_MOVING)
00317     move_window = workspace->screen->display->grab_window;
00318       
00319   if (move_window != NULL)
00320     {
00321       if (move_window->on_all_workspaces)
00322         move_window = NULL; /* don't move it after all */
00323 
00324       /* We put the window on the new workspace, flip spaces,
00325        * then remove from old workspace, so the window
00326        * never gets unmapped and we maintain the button grab
00327        * on it.
00328        *
00329        * \bug  This comment appears to be the reverse of what happens
00330        */
00331       if (move_window && (move_window->workspace != workspace))
00332         {
00333           meta_workspace_remove_window (old, move_window);
00334           meta_workspace_add_window (workspace, move_window);
00335         }
00336     }
00337 
00338   meta_workspace_queue_calc_showing (old);
00339   meta_workspace_queue_calc_showing (workspace);
00340 
00341   /* FIXME: Why do we need this?!?  Isn't it handled in the lines above? */
00342   if (move_window)
00343       /* Removes window from other spaces */
00344       meta_window_change_workspace (move_window, workspace);
00345 
00346   if (focus_this)
00347     {
00348       meta_window_focus (focus_this, timestamp);
00349       meta_window_raise (focus_this);
00350     }
00351   else if (move_window)
00352     {
00353       meta_window_raise (move_window);
00354     }
00355   else
00356     {
00357       meta_topic (META_DEBUG_FOCUS, "Focusing default window on new workspace\n");
00358       meta_workspace_focus_default_window (workspace, NULL, timestamp);
00359     }
00360 }
00361 
00362 void
00363 meta_workspace_activate (MetaWorkspace *workspace,
00364                          guint32        timestamp)
00365 {
00366   meta_workspace_activate_with_focus (workspace, NULL, timestamp);
00367 }
00368 
00369 int
00370 meta_workspace_index (MetaWorkspace *workspace)
00371 {
00372   int ret;
00373 
00374   ret = g_list_index (workspace->screen->workspaces, workspace);
00375 
00376   if (ret < 0)
00377     meta_bug ("Workspace does not exist to index!\n");
00378 
00379   return ret;
00380 }
00381 
00382 /* get windows contained on workspace, including workspace->windows
00383  * and also sticky windows.
00384  */
00385 GList*
00386 meta_workspace_list_windows (MetaWorkspace *workspace)
00387 {
00388   GSList *display_windows;
00389   GSList *tmp;
00390   GList *workspace_windows;
00391   
00392   display_windows = meta_display_list_windows (workspace->screen->display);
00393 
00394   workspace_windows = NULL;
00395   tmp = display_windows;
00396   while (tmp != NULL)
00397     {
00398       MetaWindow *window = tmp->data;
00399 
00400       if (meta_window_located_on_workspace (window, workspace))
00401         workspace_windows = g_list_prepend (workspace_windows,
00402                                             window);
00403 
00404       tmp = tmp->next;
00405     }
00406 
00407   g_slist_free (display_windows);
00408 
00409   return workspace_windows;
00410 }
00411 
00412 static void
00413 set_active_space_hint (MetaScreen *screen)
00414 {
00415   unsigned long data[1];
00416 
00417   /* this is because we destroy the spaces in order,
00418    * so we always end up setting a current desktop of
00419    * 0 when closing a screen, so lose the current desktop
00420    * on restart. By doing this we keep the current
00421    * desktop on restart.
00422    */
00423   if (screen->closing > 0)
00424     return;
00425   
00426   data[0] = meta_workspace_index (screen->active_workspace);
00427 
00428   meta_verbose ("Setting _NET_CURRENT_DESKTOP to %lu\n", data[0]);
00429   
00430   meta_error_trap_push (screen->display);
00431   XChangeProperty (screen->display->xdisplay, screen->xroot,
00432                    screen->display->atom__NET_CURRENT_DESKTOP,
00433                    XA_CARDINAL,
00434                    32, PropModeReplace, (guchar*) data, 1);
00435   meta_error_trap_pop (screen->display, FALSE);
00436 }
00437 
00438 void
00439 meta_workspace_invalidate_work_area (MetaWorkspace *workspace)
00440 {
00441   GList *tmp;
00442   GList *windows;
00443   int i;
00444   
00445   if (workspace->work_areas_invalid)
00446     {
00447       meta_topic (META_DEBUG_WORKAREA,
00448                   "Work area for workspace %d is already invalid\n",
00449                   meta_workspace_index (workspace));
00450       return;
00451     }
00452 
00453   meta_topic (META_DEBUG_WORKAREA,
00454               "Invalidating work area for workspace %d\n",
00455               meta_workspace_index (workspace));
00456 
00457   g_free (workspace->work_area_xinerama);
00458   workspace->work_area_xinerama = NULL;
00459       
00460   g_slist_free (workspace->all_struts);
00461   workspace->all_struts = NULL;
00462 
00463   for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
00464     meta_rectangle_free_list_and_elements (workspace->xinerama_region[i]);
00465   g_free (workspace->xinerama_region);
00466   meta_rectangle_free_list_and_elements (workspace->screen_region);
00467   meta_rectangle_free_list_and_elements (workspace->screen_edges);
00468   meta_rectangle_free_list_and_elements (workspace->xinerama_edges);
00469   workspace->xinerama_region = NULL;
00470   workspace->screen_region = NULL;
00471   workspace->screen_edges = NULL;
00472   workspace->xinerama_edges = NULL;
00473   
00474   workspace->work_areas_invalid = TRUE;
00475 
00476   /* redo the size/position constraints on all windows */
00477   windows = meta_workspace_list_windows (workspace);
00478   tmp = windows;
00479   while (tmp != NULL)
00480     {
00481       MetaWindow *w = tmp->data;
00482 
00483       meta_window_queue (w, META_QUEUE_MOVE_RESIZE);
00484       
00485       tmp = tmp->next;
00486     }
00487 
00488   g_list_free (windows);
00489 
00490   meta_screen_queue_workarea_recalc (workspace->screen);
00491 }
00492 
00493 static void
00494 ensure_work_areas_validated (MetaWorkspace *workspace)
00495 {
00496   GList         *windows;
00497   GList         *tmp;
00498   MetaRectangle  work_area;
00499   int            i;  /* C89 absolutely sucks... */
00500 
00501   if (!workspace->work_areas_invalid)
00502     return;
00503 
00504   g_assert (workspace->all_struts == NULL);
00505   g_assert (workspace->xinerama_region == NULL);
00506   g_assert (workspace->screen_region == NULL);
00507   g_assert (workspace->screen_edges == NULL);
00508   g_assert (workspace->xinerama_edges == NULL);
00509 
00510   /* STEP 1: Get the list of struts */  
00511   windows = meta_workspace_list_windows (workspace);
00512   for (tmp = windows; tmp != NULL; tmp = tmp->next)
00513     {
00514       MetaWindow *win = tmp->data;
00515       GSList *s_iter;
00516 
00517       for (s_iter = win->struts; s_iter != NULL; s_iter = s_iter->next) {
00518         MetaStrut *cpy = g_new (MetaStrut, 1);
00519         *cpy = *((MetaStrut *)s_iter->data);
00520         workspace->all_struts = g_slist_prepend (workspace->all_struts,
00521                                                  cpy);
00522       }
00523     }
00524   g_list_free (windows);
00525 
00526   /* STEP 2: Get the maximal/spanning rects for the onscreen and
00527    *         on-single-xinerama regions
00528    */  
00529   g_assert (workspace->xinerama_region == NULL);
00530   g_assert (workspace->screen_region   == NULL);
00531 
00532   workspace->xinerama_region = g_new (GList*,
00533                                       workspace->screen->n_xinerama_infos);
00534   for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
00535     {
00536       workspace->xinerama_region[i] =
00537         meta_rectangle_get_minimal_spanning_set_for_region (
00538           &workspace->screen->xinerama_infos[i].rect,
00539           workspace->all_struts);
00540     }
00541   workspace->screen_region =
00542     meta_rectangle_get_minimal_spanning_set_for_region (
00543       &workspace->screen->rect,
00544       workspace->all_struts);
00545 
00546   /* STEP 3: Get the work areas (region-to-maximize-to) for the screen and
00547    *         xineramas.
00548    */
00549   work_area = workspace->screen->rect;  /* start with the screen */
00550   if (workspace->screen_region == NULL)
00551     work_area = meta_rect (0, 0, -1, -1);
00552   else
00553     meta_rectangle_clip_to_region (workspace->screen_region,
00554                                    FIXED_DIRECTION_NONE,
00555                                    &work_area);
00556 
00557   /* Lots of paranoia checks, forcing work_area_screen to be sane */
00558 #define MIN_SANE_AREA 100
00559   if (work_area.width < MIN_SANE_AREA)
00560     {
00561       meta_warning ("struts occupy an unusually large percentage of the screen; "
00562                     "available remaining width = %d < %d",
00563                     work_area.width, MIN_SANE_AREA);
00564       if (work_area.width < 1)
00565         {
00566           work_area.x = (workspace->screen->rect.width - MIN_SANE_AREA)/2;
00567           work_area.width = MIN_SANE_AREA;
00568         }
00569       else
00570         {
00571           int amount = (MIN_SANE_AREA - work_area.width)/2;
00572           work_area.x     -=   amount;
00573           work_area.width += 2*amount;
00574         }
00575     }
00576   if (work_area.height < MIN_SANE_AREA)
00577     {
00578       meta_warning ("struts occupy an unusually large percentage of the screen; "
00579                     "available remaining height = %d < %d",
00580                     work_area.height, MIN_SANE_AREA);
00581       if (work_area.height < 1)
00582         {
00583           work_area.y = (workspace->screen->rect.height - MIN_SANE_AREA)/2;
00584           work_area.height = MIN_SANE_AREA;
00585         }
00586       else
00587         {
00588           int amount = (MIN_SANE_AREA - work_area.height)/2;
00589           work_area.y      -=   amount;
00590           work_area.height += 2*amount;
00591         }
00592     }
00593   workspace->work_area_screen = work_area;
00594   meta_topic (META_DEBUG_WORKAREA,
00595               "Computed work area for workspace %d: %d,%d %d x %d\n",
00596               meta_workspace_index (workspace),
00597               workspace->work_area_screen.x,
00598               workspace->work_area_screen.y,
00599               workspace->work_area_screen.width,
00600               workspace->work_area_screen.height);    
00601 
00602   /* Now find the work areas for each xinerama */
00603   g_free (workspace->work_area_xinerama);
00604   workspace->work_area_xinerama = g_new (MetaRectangle,
00605                                          workspace->screen->n_xinerama_infos);
00606 
00607   for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
00608     {
00609       work_area = workspace->screen->xinerama_infos[i].rect;
00610 
00611       if (workspace->xinerama_region[i] == NULL)
00612         /* FIXME: constraints.c untested with this, but it might be nice for
00613          * a screen reader or magnifier.
00614          */
00615         work_area = meta_rect (work_area.x, work_area.y, -1, -1);
00616       else
00617         meta_rectangle_clip_to_region (workspace->xinerama_region[i],
00618                                        FIXED_DIRECTION_NONE,
00619                                        &work_area);
00620 
00621       workspace->work_area_xinerama[i] = work_area;
00622       meta_topic (META_DEBUG_WORKAREA,
00623                   "Computed work area for workspace %d "
00624                   "xinerama %d: %d,%d %d x %d\n",
00625                   meta_workspace_index (workspace),
00626                   i,
00627                   workspace->work_area_xinerama[i].x,
00628                   workspace->work_area_xinerama[i].y,
00629                   workspace->work_area_xinerama[i].width,
00630                   workspace->work_area_xinerama[i].height);
00631     }
00632 
00633   /* STEP 4: Make sure the screen_region is nonempty (separate from step 2
00634    *         since it relies on step 3).
00635    */  
00636   if (workspace->screen_region == NULL)
00637     {
00638       MetaRectangle *nonempty_region;
00639       nonempty_region = g_new (MetaRectangle, 1);
00640       *nonempty_region = workspace->work_area_screen;
00641       workspace->screen_region = g_list_prepend (NULL, nonempty_region);
00642     }
00643 
00644   /* STEP 5: Cache screen and xinerama edges for edge resistance and snapping */
00645   g_assert (workspace->screen_edges    == NULL);
00646   g_assert (workspace->xinerama_edges  == NULL);
00647   workspace->screen_edges =
00648     meta_rectangle_find_onscreen_edges (&workspace->screen->rect,
00649                                         workspace->all_struts);
00650   tmp = NULL;
00651   for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
00652     tmp = g_list_prepend (tmp, &workspace->screen->xinerama_infos[i].rect);
00653   workspace->xinerama_edges =
00654     meta_rectangle_find_nonintersected_xinerama_edges (tmp,
00655                                                        workspace->all_struts);
00656   g_list_free (tmp);
00657 
00658   /* We're all done, YAAY!  Record that everything has been validated. */
00659   workspace->work_areas_invalid = FALSE;
00660 }
00661 
00662 void
00663 meta_workspace_get_work_area_for_xinerama (MetaWorkspace *workspace,
00664                                            int            which_xinerama,
00665                                            MetaRectangle *area)
00666 {
00667   g_assert (which_xinerama >= 0);
00668 
00669   ensure_work_areas_validated (workspace);
00670   g_assert (which_xinerama < workspace->screen->n_xinerama_infos);
00671   
00672   *area = workspace->work_area_xinerama[which_xinerama];
00673 }
00674 
00675 void
00676 meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace,
00677                                             MetaRectangle *area)
00678 {
00679   ensure_work_areas_validated (workspace);
00680   
00681   *area = workspace->work_area_screen;
00682 }
00683 
00684 GList*
00685 meta_workspace_get_onscreen_region (MetaWorkspace *workspace)
00686 {
00687   ensure_work_areas_validated (workspace);
00688 
00689   return workspace->screen_region;
00690 }
00691 
00692 GList*
00693 meta_workspace_get_onxinerama_region (MetaWorkspace *workspace,
00694                                       int            which_xinerama)
00695 {
00696   ensure_work_areas_validated (workspace);
00697 
00698   return workspace->xinerama_region[which_xinerama];
00699 }
00700 
00701 #ifdef WITH_VERBOSE_MODE
00702 static char *
00703 meta_motion_direction_to_string (MetaMotionDirection direction)
00704 {
00705   switch (direction)
00706     {
00707     case META_MOTION_UP:
00708       return "Up";
00709     case META_MOTION_DOWN:
00710       return "Down";
00711     case META_MOTION_LEFT:
00712       return "Left";
00713     case META_MOTION_RIGHT:
00714       return "Right";
00715     }
00716 
00717   return "Unknown";
00718 }
00719 #endif /* WITH_VERBOSE_MODE */
00720 
00721 MetaWorkspace*
00722 meta_workspace_get_neighbor (MetaWorkspace      *workspace,
00723                              MetaMotionDirection direction)
00724 {
00725   MetaWorkspaceLayout layout;  
00726   int i, current_space, num_workspaces;
00727   gboolean ltr;
00728 
00729   current_space = meta_workspace_index (workspace);
00730   num_workspaces = meta_screen_get_n_workspaces (workspace->screen);
00731   meta_screen_calc_workspace_layout (workspace->screen, num_workspaces,
00732                                      current_space, &layout);
00733 
00734   meta_verbose ("Getting neighbor of %d in direction %s\n",
00735                 current_space, meta_motion_direction_to_string (direction));
00736   
00737   ltr = meta_ui_get_direction() == META_UI_DIRECTION_LTR;
00738 
00739   switch (direction) 
00740     {
00741     case META_MOTION_LEFT:
00742       layout.current_col -= ltr ? 1 : -1;
00743       break;
00744     case META_MOTION_RIGHT:
00745       layout.current_col += ltr ? 1 : -1;
00746       break;
00747     case META_MOTION_UP:
00748       layout.current_row -= 1;
00749       break;
00750     case META_MOTION_DOWN:
00751       layout.current_row += 1;
00752       break;
00753     }
00754 
00755   if (layout.current_col < 0)
00756     layout.current_col = 0;
00757   if (layout.current_col >= layout.cols)
00758     layout.current_col = layout.cols - 1;
00759   if (layout.current_row < 0)
00760     layout.current_row = 0;
00761   if (layout.current_row >= layout.rows)
00762     layout.current_row = layout.rows - 1;
00763 
00764   i = layout.grid[layout.current_row * layout.cols + layout.current_col];
00765 
00766   if (i < 0)
00767     i = current_space;
00768 
00769   if (i >= num_workspaces)
00770     meta_bug ("calc_workspace_layout left an invalid (too-high) workspace number %d in the grid\n",
00771               i);
00772     
00773   meta_verbose ("Neighbor workspace is %d at row %d col %d\n",
00774                 i, layout.current_row, layout.current_col);
00775 
00776   meta_screen_free_workspace_layout (&layout);
00777   
00778   return meta_screen_get_workspace_by_index (workspace->screen, i);
00779 }
00780 
00781 const char*
00782 meta_workspace_get_name (MetaWorkspace *workspace)
00783 {
00784   return meta_prefs_get_workspace_name (meta_workspace_index (workspace));
00785 }
00786 
00787 void
00788 meta_workspace_focus_default_window (MetaWorkspace *workspace,
00789                                      MetaWindow    *not_this_one,
00790                                      guint32        timestamp)
00791 {
00792   if (timestamp == CurrentTime)
00793     {
00794       meta_warning ("CurrentTime used to choose focus window; "
00795                     "focus window may not be correct.\n");
00796     }
00797 
00798 
00799   if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK ||
00800       !workspace->screen->display->mouse_mode)
00801     focus_ancestor_or_mru_window (workspace, not_this_one, timestamp);
00802   else
00803     {
00804       MetaWindow * window;
00805       window = meta_screen_get_mouse_window (workspace->screen, not_this_one);
00806       if (window &&
00807           window->type != META_WINDOW_DOCK &&
00808           window->type != META_WINDOW_DESKTOP)
00809         {
00810           if (timestamp == CurrentTime)
00811             {
00812 
00813               /* We would like for this to never happen.  However, if
00814                * it does happen then we kludge since using CurrentTime
00815                * can mean ugly race conditions--and we can avoid these
00816                * by allowing EnterNotify events (which come with
00817                * timestamps) to handle focus.
00818                */
00819 
00820               meta_topic (META_DEBUG_FOCUS,
00821                           "Not focusing mouse window %s because EnterNotify events should handle that\n", window->desc);
00822             }
00823           else
00824             {
00825               meta_topic (META_DEBUG_FOCUS,
00826                           "Focusing mouse window %s\n", window->desc);
00827               meta_window_focus (window, timestamp);
00828             }
00829 
00830           if (workspace->screen->display->autoraise_window != window &&
00831               meta_prefs_get_auto_raise ()) 
00832             {
00833               meta_display_queue_autoraise_callback (workspace->screen->display,
00834                                                      window);
00835             }
00836         }
00837       else if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_SLOPPY)
00838         focus_ancestor_or_mru_window (workspace, not_this_one, timestamp);
00839       else if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_MOUSE)
00840         {
00841           meta_topic (META_DEBUG_FOCUS,
00842                       "Setting focus to no_focus_window, since no valid "
00843                       "window to focus found.\n");
00844           meta_display_focus_the_no_focus_window (workspace->screen->display,
00845                                                   workspace->screen,
00846                                                   timestamp);
00847         }
00848     }
00849 }
00850 
00851 static gboolean
00852 record_ancestor (MetaWindow *window,
00853                  void       *data)
00854 {
00855   MetaWindow **result = data;
00856 
00857   *result = window;
00858   return FALSE; /* quit with the first ancestor we find */
00859 }
00860 
00861 /* Focus ancestor of not_this_one if there is one, otherwise focus the MRU
00862  * window on active workspace
00863  */
00864 static void
00865 focus_ancestor_or_mru_window (MetaWorkspace *workspace,
00866                               MetaWindow    *not_this_one,
00867                               guint32        timestamp)
00868 {
00869   MetaWindow *window = NULL;
00870   MetaWindow *desktop_window = NULL;
00871   GList *tmp;
00872 
00873   if (not_this_one)
00874     meta_topic (META_DEBUG_FOCUS,
00875                 "Focusing MRU window excluding %s\n", not_this_one->desc);
00876   else
00877     meta_topic (META_DEBUG_FOCUS,
00878                 "Focusing MRU window\n");
00879 
00880   /* First, check to see if we need to focus an ancestor of a window */  
00881   if (not_this_one)
00882     {
00883       MetaWindow *ancestor;
00884       ancestor = NULL;
00885       meta_window_foreach_ancestor (not_this_one, record_ancestor, &ancestor);
00886       if (ancestor != NULL)
00887         {
00888           meta_topic (META_DEBUG_FOCUS,
00889                       "Focusing %s, ancestor of %s\n", 
00890                       ancestor->desc, not_this_one->desc);
00891       
00892           meta_window_focus (ancestor, timestamp);
00893 
00894           /* Also raise the window if in click-to-focus */
00895           if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
00896             meta_window_raise (ancestor);
00897 
00898           return;
00899         }
00900     }
00901 
00902   /* No ancestor, look for the MRU window */
00903   tmp = workspace->mru_list;  
00904 
00905   while (tmp)
00906     {
00907       MetaWindow* tmp_window;
00908       tmp_window = ((MetaWindow*) tmp->data);
00909       if (tmp_window != not_this_one           &&
00910           meta_window_showing_on_its_workspace (tmp_window) &&
00911           tmp_window->type != META_WINDOW_DOCK &&
00912           tmp_window->type != META_WINDOW_DESKTOP)
00913         {
00914           window = tmp->data;
00915           break;
00916         }
00917       else if (tmp_window != not_this_one      &&
00918                desktop_window == NULL          &&
00919                meta_window_showing_on_its_workspace (tmp_window) &&
00920                tmp_window->type == META_WINDOW_DESKTOP)
00921         {
00922           /* Found the most recently used desktop window */
00923           desktop_window = tmp_window;
00924         }
00925 
00926       tmp = tmp->next;
00927     }
00928 
00929   /* If no window was found, default to the MRU desktop-window */
00930   if (window == NULL)
00931     window = desktop_window;
00932 
00933   if (window)
00934     {
00935       meta_topic (META_DEBUG_FOCUS,
00936                   "Focusing workspace MRU window %s\n", window->desc);
00937       
00938       meta_window_focus (window, timestamp);
00939 
00940       /* Also raise the window if in click-to-focus */
00941       if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
00942         meta_window_raise (window);
00943     }
00944   else
00945     {
00946       meta_topic (META_DEBUG_FOCUS, "No MRU window to focus found; focusing no_focus_window.\n");
00947       meta_display_focus_the_no_focus_window (workspace->screen->display,
00948                                               workspace->screen,
00949                                               timestamp);
00950     }
00951 }

Generated on Sat Aug 23 22:04:18 2008 for metacity by  doxygen 1.5.5