1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /* Metacity X screen handler */
4 
5 /*
6  * Copyright (C) 2001, 2002 Havoc Pennington
7  * Copyright (C) 2002, 2003 Red Hat Inc.
8  * Some ICCCM manager selection code derived from fvwm2,
9  * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team
10  * Copyright (C) 2003 Rob Adams
11  * Copyright (C) 2004-2006 Elijah Newren
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License as
15  * published by the Free Software Foundation; either version 2 of the
16  * License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, see <http://www.gnu.org/licenses/>.
25  */
26 
27 #include "config.h"
28 
29 #include "screen-private.h"
30 #include "util.h"
31 #include "errors.h"
32 #include "window-private.h"
33 #include "frame-private.h"
34 #include "prefs.h"
35 #include "workspace.h"
36 #include "keybindings.h"
37 #include "stack.h"
38 #include "xprops.h"
39 #include "meta-compositor.h"
40 
41 #ifdef HAVE_XINERAMA
42 #include <X11/extensions/Xinerama.h>
43 #endif
44 
45 #include <X11/Xatom.h>
46 #include <locale.h>
47 #include <string.h>
48 #include <stdio.h>
49 
50 static char* get_screen_name (MetaDisplay *display,
51                               int          number);
52 
53 static void update_num_workspaces  (MetaScreen *screen,
54                                     guint32     timestamp);
55 static void update_focus_mode      (MetaScreen *screen);
56 static void set_workspace_names    (MetaScreen *screen);
57 static void prefs_changed_callback (MetaPreference pref,
58                                     gpointer       data);
59 
60 static void set_desktop_geometry_hint (MetaScreen *screen);
61 static void set_desktop_viewport_hint (MetaScreen *screen);
62 
63 #ifdef HAVE_STARTUP_NOTIFICATION
64 static void meta_screen_sn_event   (SnMonitorEvent *event,
65                                     void           *user_data);
66 #endif
67 
68 static int
set_wm_check_hint(MetaScreen * screen)69 set_wm_check_hint (MetaScreen *screen)
70 {
71   unsigned long data[1];
72 
73   g_return_val_if_fail (screen->display->leader_window != None, 0);
74 
75   data[0] = screen->display->leader_window;
76 
77   XChangeProperty (screen->display->xdisplay, screen->xroot,
78                    screen->display->atom__NET_SUPPORTING_WM_CHECK,
79                    XA_WINDOW,
80                    32, PropModeReplace, (guchar*) data, 1);
81 
82   return Success;
83 }
84 
85 static void
unset_wm_check_hint(MetaScreen * screen)86 unset_wm_check_hint (MetaScreen *screen)
87 {
88   XDeleteProperty (screen->display->xdisplay, screen->xroot,
89                    screen->display->atom__NET_SUPPORTING_WM_CHECK);
90 }
91 
92 static int
set_supported_hint(MetaScreen * screen)93 set_supported_hint (MetaScreen *screen)
94 {
95   Atom atoms[] = {
96 #define EWMH_ATOMS_ONLY
97 #define item(x)  screen->display->atom_##x,
98 #include "atomnames.h"
99 #undef item
100 #undef EWMH_ATOMS_ONLY
101 
102     screen->display->atom__GTK_FRAME_EXTENTS,
103     screen->display->atom__GTK_SHOW_WINDOW_MENU,
104     screen->display->atom__GTK_WORKAREAS
105   };
106 
107   XChangeProperty (screen->display->xdisplay, screen->xroot,
108                    screen->display->atom__NET_SUPPORTED,
109                    XA_ATOM,
110                    32, PropModeReplace,
111                    (guchar*) atoms, G_N_ELEMENTS(atoms));
112 
113   return Success;
114 }
115 
116 static int
set_wm_icon_size_hint(MetaScreen * screen)117 set_wm_icon_size_hint (MetaScreen *screen)
118 {
119 #define N_VALS 6
120   gulong vals[N_VALS];
121 
122   /* We've bumped the real icon size up to 96x96, but
123    * we really should not add these sorts of constraints
124    * on clients still using the legacy WM_HINTS interface.
125    */
126 #define LEGACY_ICON_SIZE 32
127 
128   /* min width, min height, max w, max h, width inc, height inc */
129   vals[0] = LEGACY_ICON_SIZE;
130   vals[1] = LEGACY_ICON_SIZE;
131   vals[2] = LEGACY_ICON_SIZE;
132   vals[3] = LEGACY_ICON_SIZE;
133   vals[4] = 0;
134   vals[5] = 0;
135 #undef LEGACY_ICON_SIZE
136 
137   XChangeProperty (screen->display->xdisplay, screen->xroot,
138                    screen->display->atom_WM_ICON_SIZE,
139                    XA_CARDINAL,
140                    32, PropModeReplace, (guchar*) vals, N_VALS);
141 
142   return Success;
143 #undef N_VALS
144 }
145 
146 static void
reload_monitor_infos(MetaScreen * screen)147 reload_monitor_infos (MetaScreen *screen)
148 {
149   {
150     GList *tmp;
151 
152     tmp = screen->workspaces;
153     while (tmp != NULL)
154       {
155         MetaWorkspace *space = tmp->data;
156 
157         meta_workspace_invalidate_work_area (space);
158 
159         tmp = tmp->next;
160       }
161   }
162 
163   if (screen->monitor_infos)
164     g_free (screen->monitor_infos);
165 
166   screen->monitor_infos = NULL;
167   screen->n_monitor_infos = 0;
168   screen->last_monitor_index = 0;
169 
170   screen->display->monitor_cache_invalidated = TRUE;
171 
172 #ifdef HAVE_XINERAMA
173   if (XineramaIsActive (screen->display->xdisplay))
174     {
175       XineramaScreenInfo *infos;
176       int n_infos;
177       int i;
178 
179       n_infos = 0;
180       infos = XineramaQueryScreens (screen->display->xdisplay, &n_infos);
181 
182       meta_topic (META_DEBUG_XINERAMA,
183                   "Found %d monitors on display %s\n",
184                   n_infos, screen->display->name);
185 
186       if (n_infos > 0)
187         {
188           screen->monitor_infos = g_new (MetaMonitorInfo, n_infos);
189           screen->n_monitor_infos = n_infos;
190 
191           i = 0;
192           while (i < n_infos)
193             {
194               screen->monitor_infos[i].number = infos[i].screen_number;
195               screen->monitor_infos[i].rect.x = infos[i].x_org;
196               screen->monitor_infos[i].rect.y = infos[i].y_org;
197               screen->monitor_infos[i].rect.width = infos[i].width;
198               screen->monitor_infos[i].rect.height = infos[i].height;
199 
200               meta_topic (META_DEBUG_XINERAMA,
201                           "Monitor %d is %d,%d %d x %d\n",
202                           screen->monitor_infos[i].number,
203                           screen->monitor_infos[i].rect.x,
204                           screen->monitor_infos[i].rect.y,
205                           screen->monitor_infos[i].rect.width,
206                           screen->monitor_infos[i].rect.height);
207 
208               ++i;
209             }
210         }
211 
212       meta_XFree (infos);
213     }
214   else
215     {
216       meta_topic (META_DEBUG_XINERAMA,
217                   "No Xinerama extension or Xinerama inactive on display %s\n",
218                   screen->display->name);
219     }
220 #else
221   meta_topic (META_DEBUG_XINERAMA,
222               "Metacity compiled without Xinerama support\n");
223 #endif /* HAVE_XINERAMA */
224 
225   /* If no Xinerama, fill in the single screen info so
226    * we can use the field unconditionally
227    */
228   if (screen->n_monitor_infos == 0)
229     {
230       if (g_getenv ("METACITY_DEBUG_XINERAMA"))
231         {
232           meta_topic (META_DEBUG_XINERAMA,
233                       "Pretending a single screen has two monitors\n");
234 
235           screen->monitor_infos = g_new (MetaMonitorInfo, 2);
236           screen->n_monitor_infos = 2;
237 
238           screen->monitor_infos[0].number = 0;
239           screen->monitor_infos[0].rect = screen->rect;
240           screen->monitor_infos[0].rect.width = screen->rect.width / 2;
241 
242           screen->monitor_infos[1].number = 1;
243           screen->monitor_infos[1].rect = screen->rect;
244           screen->monitor_infos[1].rect.x = screen->rect.width / 2;
245           screen->monitor_infos[1].rect.width = screen->rect.width / 2;
246         }
247       else
248         {
249           meta_topic (META_DEBUG_XINERAMA,
250                       "No monitors, using default screen info\n");
251 
252           screen->monitor_infos = g_new (MetaMonitorInfo, 1);
253           screen->n_monitor_infos = 1;
254 
255           screen->monitor_infos[0].number = 0;
256           screen->monitor_infos[0].rect = screen->rect;
257         }
258     }
259 
260   g_assert (screen->n_monitor_infos > 0);
261   g_assert (screen->monitor_infos != NULL);
262 }
263 
264 MetaScreen*
meta_screen_new(MetaDisplay * display,int number,guint32 timestamp)265 meta_screen_new (MetaDisplay *display,
266                  int          number,
267                  guint32      timestamp)
268 {
269   MetaScreen *screen;
270   Window xroot;
271   Display *xdisplay;
272   XWindowAttributes attr;
273   Window new_wm_sn_owner;
274   Window current_wm_sn_owner;
275   gboolean replace_current_wm;
276   Atom wm_sn_atom;
277   char buf[128];
278   guint32 manager_timestamp;
279   gulong current_workspace;
280 
281   replace_current_wm = meta_get_replace_current_wm ();
282 
283   /* Only display->name, display->xdisplay, and display->error_traps
284    * can really be used in this function, since normally screens are
285    * created from the MetaDisplay constructor
286    */
287 
288   xdisplay = display->xdisplay;
289 
290   meta_verbose ("Trying screen %d on display '%s'\n",
291                 number, display->name);
292 
293   xroot = RootWindow (xdisplay, number);
294 
295   /* FVWM checks for None here, I don't know if this
296    * ever actually happens
297    */
298   if (xroot == None)
299     {
300       g_warning ("Screen %d on display '%s' is invalid", number, display->name);
301       return NULL;
302     }
303 
304   sprintf (buf, "WM_S%d", number);
305   wm_sn_atom = XInternAtom (xdisplay, buf, False);
306 
307   current_wm_sn_owner = XGetSelectionOwner (xdisplay, wm_sn_atom);
308 
309   if (current_wm_sn_owner != None)
310     {
311       XSetWindowAttributes attrs;
312 
313       if (!replace_current_wm)
314         {
315           g_warning ("Screen %d on display \"%s\" already has a window "
316                      "manager; try using the --replace option to replace the "
317                      "current window manager.", number, display->name);
318 
319           return NULL;
320         }
321 
322       /* We want to find out when the current selection owner dies */
323       meta_error_trap_push (display);
324       attrs.event_mask = StructureNotifyMask;
325       XChangeWindowAttributes (xdisplay,
326                                current_wm_sn_owner, CWEventMask, &attrs);
327       if (meta_error_trap_pop_with_return (display) != Success)
328         current_wm_sn_owner = None; /* don't wait for it to die later on */
329     }
330 
331   /* We need SelectionClear and SelectionRequest events on the new_wm_sn_owner,
332    * but those cannot be masked, so we only need NoEventMask.
333    */
334   new_wm_sn_owner = meta_create_offscreen_window (xdisplay, xroot, NoEventMask);
335 
336   manager_timestamp = timestamp;
337 
338   XSetSelectionOwner (xdisplay, wm_sn_atom, new_wm_sn_owner,
339                       manager_timestamp);
340 
341   if (XGetSelectionOwner (xdisplay, wm_sn_atom) != new_wm_sn_owner)
342     {
343       g_warning ("Could not acquire window manager selection on "
344                  "screen %d display \"%s\"", number, display->name);
345 
346       XDestroyWindow (xdisplay, new_wm_sn_owner);
347 
348       return NULL;
349     }
350 
351   {
352     /* Send client message indicating that we are now the WM */
353     XClientMessageEvent ev;
354 
355     ev.type = ClientMessage;
356     ev.window = xroot;
357     ev.message_type = display->atom_MANAGER;
358     ev.format = 32;
359     ev.data.l[0] = manager_timestamp;
360     ev.data.l[1] = wm_sn_atom;
361 
362     XSendEvent (xdisplay, xroot, False, StructureNotifyMask, (XEvent*)&ev);
363   }
364 
365   /* Wait for old window manager to go away */
366   if (current_wm_sn_owner != None)
367     {
368       XEvent event;
369 
370       /* We sort of block infinitely here which is probably lame. */
371 
372       meta_verbose ("Waiting for old window manager to exit\n");
373       do
374         {
375           XWindowEvent (xdisplay, current_wm_sn_owner,
376                         StructureNotifyMask, &event);
377         }
378       while (event.type != DestroyNotify);
379     }
380 
381   /* select our root window events */
382   meta_error_trap_push (display);
383 
384   /* We need to or with the existing event mask since
385    * gtk+ may be interested in other events.
386    */
387   XGetWindowAttributes (xdisplay, xroot, &attr);
388   XSelectInput (xdisplay,
389                 xroot,
390                 SubstructureRedirectMask | SubstructureNotifyMask |
391                 ColormapChangeMask | PropertyChangeMask |
392                 LeaveWindowMask | EnterWindowMask |
393                 KeyPressMask | KeyReleaseMask |
394                 FocusChangeMask | StructureNotifyMask |
395                 ExposureMask |
396                 attr.your_event_mask);
397   if (meta_error_trap_pop_with_return (display) != Success)
398     {
399       g_warning ("Screen %d on display \"%s\" already has a window manager",
400                  number, display->name);
401 
402       XDestroyWindow (xdisplay, new_wm_sn_owner);
403 
404       return NULL;
405     }
406 
407   screen = g_new (MetaScreen, 1);
408   screen->closing = 0;
409 
410   screen->display = display;
411   screen->number = number;
412   screen->screen_name = get_screen_name (display, number);
413   screen->xscreen = ScreenOfDisplay (xdisplay, number);
414   screen->xroot = xroot;
415   screen->rect.x = screen->rect.y = 0;
416   screen->rect.width = WidthOfScreen (screen->xscreen);
417   screen->rect.height = HeightOfScreen (screen->xscreen);
418   screen->current_cursor = -1; /* invalid/unset */
419   screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
420   screen->default_depth = DefaultDepthOfScreen (screen->xscreen);
421   screen->flash_window = None;
422 
423   screen->wm_sn_selection_window = new_wm_sn_owner;
424   screen->wm_sn_atom = wm_sn_atom;
425   screen->wm_sn_timestamp = manager_timestamp;
426 
427   screen->work_area_idle = 0;
428 
429   screen->active_workspace = NULL;
430   screen->workspaces = NULL;
431   screen->rows_of_workspaces = 1;
432   screen->columns_of_workspaces = -1;
433   screen->vertical_workspaces = FALSE;
434   screen->starting_corner = META_SCREEN_TOPLEFT;
435 
436   screen->monitor_infos = NULL;
437   screen->n_monitor_infos = 0;
438   screen->last_monitor_index = 0;
439 
440   reload_monitor_infos (screen);
441 
442   meta_screen_set_cursor (screen, META_CURSOR_DEFAULT);
443 
444   /* Handle creating a no_focus_window for this screen */
445   screen->no_focus_window =
446     meta_create_offscreen_window (display->xdisplay,
447                                   screen->xroot,
448                                   FocusChangeMask|KeyPressMask|KeyReleaseMask);
449   XMapWindow (display->xdisplay, screen->no_focus_window);
450   /* Done with no_focus_window stuff */
451 
452   set_wm_icon_size_hint (screen);
453 
454   set_supported_hint (screen);
455 
456   set_wm_check_hint (screen);
457 
458   set_desktop_viewport_hint (screen);
459 
460   set_desktop_geometry_hint (screen);
461 
462   meta_screen_update_workspace_layout (screen);
463 
464   /* Get current workspace */
465   current_workspace = 0;
466   if (meta_prop_get_cardinal (screen->display,
467                               screen->xroot,
468                               screen->display->atom__NET_CURRENT_DESKTOP,
469                               &current_workspace))
470     meta_verbose ("Read existing _NET_CURRENT_DESKTOP = %d\n",
471                   (int) current_workspace);
472   else
473     meta_verbose ("No _NET_CURRENT_DESKTOP present\n");
474 
475   /* Screens must have at least one workspace at all times,
476    * so create that required workspace.
477    */
478   meta_workspace_activate (meta_workspace_new (screen), timestamp);
479   update_num_workspaces (screen, timestamp);
480 
481   set_workspace_names (screen);
482 
483   screen->all_keys_grabbed = FALSE;
484   screen->keys_grabbed = FALSE;
485   meta_screen_grab_keys (screen);
486 
487   screen->ui = meta_ui_new (screen->display->xdisplay, FALSE);
488 
489   screen->tab_popup = NULL;
490   screen->tile_preview = NULL;
491 
492   screen->tile_preview_timeout_id = 0;
493 
494   screen->stack = meta_stack_new (screen);
495   screen->stack_tracker = meta_stack_tracker_new (screen);
496 
497   meta_prefs_add_listener (prefs_changed_callback, screen);
498 
499 #ifdef HAVE_STARTUP_NOTIFICATION
500   screen->sn_context =
501     sn_monitor_context_new (screen->display->sn_display,
502                             screen->number,
503                             meta_screen_sn_event,
504                             screen,
505                             NULL);
506   screen->startup_sequences = NULL;
507   screen->startup_sequence_timeout = 0;
508 #endif
509 
510   /* Switch to the _NET_CURRENT_DESKTOP workspace */
511   {
512     MetaWorkspace *space;
513 
514     space = meta_screen_get_workspace_by_index (screen,
515                                                 current_workspace);
516 
517     if (space != NULL)
518       meta_workspace_activate (space, timestamp);
519   }
520 
521   meta_verbose ("Added screen %d ('%s') root 0x%lx\n",
522                 screen->number, screen->screen_name, screen->xroot);
523 
524   return screen;
525 }
526 
527 void
meta_screen_free(MetaScreen * screen,guint32 timestamp)528 meta_screen_free (MetaScreen *screen,
529                   guint32     timestamp)
530 {
531   screen->closing += 1;
532 
533   meta_prefs_remove_listener (prefs_changed_callback, screen);
534 
535   meta_screen_ungrab_keys (screen);
536 
537 #ifdef HAVE_STARTUP_NOTIFICATION
538   g_slist_free_full (screen->startup_sequences, (GDestroyNotify) sn_startup_sequence_unref);
539   screen->startup_sequences = NULL;
540 
541   if (screen->startup_sequence_timeout != 0)
542     {
543       g_source_remove (screen->startup_sequence_timeout);
544       screen->startup_sequence_timeout = 0;
545     }
546   if (screen->sn_context)
547     {
548       sn_monitor_context_unref (screen->sn_context);
549       screen->sn_context = NULL;
550     }
551 #endif
552 
553   meta_ui_free (screen->ui);
554 
555   meta_stack_free (screen->stack);
556   meta_stack_tracker_free (screen->stack_tracker);
557 
558   meta_error_trap_push (screen->display);
559   XSelectInput (screen->display->xdisplay, screen->xroot, 0);
560   if (meta_error_trap_pop_with_return (screen->display) != Success)
561     {
562       g_warning ("Could not release screen %d on display \"%s\"",
563                  screen->number, screen->display->name);
564     }
565 
566   unset_wm_check_hint (screen);
567 
568   XDestroyWindow (screen->display->xdisplay,
569                   screen->wm_sn_selection_window);
570 
571   if (screen->work_area_idle != 0)
572     g_source_remove (screen->work_area_idle);
573 
574   if (screen->monitor_infos)
575     g_free (screen->monitor_infos);
576 
577   if (screen->tile_preview_timeout_id)
578     g_source_remove (screen->tile_preview_timeout_id);
579 
580   if (screen->tile_preview)
581     meta_tile_preview_free (screen->tile_preview);
582 
583   g_free (screen->screen_name);
584   g_free (screen);
585 }
586 
587 void
meta_screen_manage_all_windows(MetaScreen * screen)588 meta_screen_manage_all_windows (MetaScreen *screen)
589 {
590   Window *windows;
591   int n_windows;
592   Window *xwindows;
593   int i;
594 
595   meta_stack_freeze (screen->stack);
596   meta_stack_tracker_get_stack (screen->stack_tracker, &windows, &n_windows);
597 
598   /* Copy the stack as it will be modified as part of the loop */
599   xwindows = g_memdup2 (windows, sizeof (Window) * n_windows);
600 
601   for (i = 0; i < n_windows; i++)
602     {
603       meta_window_new (screen->display, xwindows[i], TRUE,
604                        META_EFFECT_TYPE_NONE);
605     }
606 
607   g_free (xwindows);
608 
609   meta_stack_thaw (screen->stack);
610 }
611 
612 void
meta_screen_composite_all_windows(MetaScreen * screen)613 meta_screen_composite_all_windows (MetaScreen *screen)
614 {
615   MetaDisplay *display;
616   GSList *windows, *list;
617 
618   display = screen->display;
619   windows = meta_display_list_windows (display, META_LIST_INCLUDE_OVERRIDE_REDIRECT);
620 
621   for (list = windows; list != NULL; list = list->next)
622     {
623       MetaWindow *window;
624 
625       window = list->data;
626 
627       meta_compositor_add_window (display->compositor, window);
628     }
629 
630   g_slist_free (windows);
631 
632   /* initialize the compositor's view of the stacking order */
633   meta_stack_tracker_sync_stack (screen->stack_tracker);
634 }
635 
636 MetaScreen*
meta_screen_for_x_screen(Screen * xscreen)637 meta_screen_for_x_screen (Screen *xscreen)
638 {
639   MetaDisplay *display;
640 
641   display = meta_display_for_x_display (DisplayOfScreen (xscreen));
642 
643   if (display == NULL || display->screen->xscreen != xscreen)
644     return NULL;
645 
646   return display->screen;
647 }
648 
649 static void
prefs_changed_callback(MetaPreference pref,gpointer data)650 prefs_changed_callback (MetaPreference pref,
651                         gpointer       data)
652 {
653   MetaScreen *screen = data;
654 
655   if (pref == META_PREF_NUM_WORKSPACES)
656     {
657       /* GSettings doesn't provide timestamps, but luckily update_num_workspaces
658        * often doesn't need it...
659        */
660       guint32 timestamp =
661         meta_display_get_current_time_roundtrip (screen->display);
662       update_num_workspaces (screen, timestamp);
663     }
664   else if (pref == META_PREF_FOCUS_MODE)
665     {
666       update_focus_mode (screen);
667     }
668   else if (pref == META_PREF_WORKSPACE_NAMES)
669     {
670       set_workspace_names (screen);
671     }
672 }
673 
674 
675 static char*
get_screen_name(MetaDisplay * display,int number)676 get_screen_name (MetaDisplay *display,
677                  int          number)
678 {
679   char *p;
680   char *dname;
681   char *scr;
682 
683   /* DisplayString gives us a sort of canonical display,
684    * vs. the user-entered name from XDisplayName()
685    */
686   dname = g_strdup (DisplayString (display->xdisplay));
687 
688   /* Change display name to specify this screen.
689    */
690   p = strrchr (dname, ':');
691   if (p)
692     {
693       p = strchr (p, '.');
694       if (p)
695         *p = '\0';
696     }
697 
698   scr = g_strdup_printf ("%s.%d", dname, number);
699 
700   g_free (dname);
701 
702   return scr;
703 }
704 
705 static gint
ptrcmp(gconstpointer a,gconstpointer b)706 ptrcmp (gconstpointer a, gconstpointer b)
707 {
708   if (a < b)
709     return -1;
710   else if (a > b)
711     return 1;
712   else
713     return 0;
714 }
715 
716 static void
listify_func(gpointer key,gpointer value,gpointer data)717 listify_func (gpointer key, gpointer value, gpointer data)
718 {
719   GSList **listp;
720 
721   listp = data;
722 
723   *listp = g_slist_prepend (*listp, value);
724 }
725 
726 void
meta_screen_foreach_window(MetaScreen * screen,MetaScreenWindowFunc func,gpointer data)727 meta_screen_foreach_window (MetaScreen *screen,
728                             MetaScreenWindowFunc func,
729                             gpointer data)
730 {
731   GSList *winlist;
732   GSList *tmp;
733 
734   /* If we end up doing this often, just keeping a list
735    * of windows might be sensible.
736    */
737 
738   winlist = NULL;
739   g_hash_table_foreach (screen->display->window_ids,
740                         listify_func,
741                         &winlist);
742 
743   winlist = g_slist_sort (winlist, ptrcmp);
744 
745   tmp = winlist;
746   while (tmp != NULL)
747     {
748       /* If the next node doesn't contain this window
749        * a second time, delete the window.
750        */
751       if (tmp->next == NULL ||
752           (tmp->next && tmp->next->data != tmp->data))
753         {
754           MetaWindow *window = tmp->data;
755 
756           if (window->screen == screen && !window->override_redirect)
757             (* func) (screen, window, data);
758         }
759 
760       tmp = tmp->next;
761     }
762   g_slist_free (winlist);
763 }
764 
765 static void
queue_draw(MetaScreen * screen,MetaWindow * window,gpointer data)766 queue_draw (MetaScreen *screen, MetaWindow *window, gpointer data)
767 {
768   if (window->frame)
769     meta_frame_queue_draw (window->frame);
770 }
771 
772 void
meta_screen_queue_frame_redraws(MetaScreen * screen)773 meta_screen_queue_frame_redraws (MetaScreen *screen)
774 {
775   meta_screen_foreach_window (screen, queue_draw, NULL);
776 }
777 
778 static void
queue_resize(MetaScreen * screen,MetaWindow * window,gpointer data)779 queue_resize (MetaScreen *screen, MetaWindow *window, gpointer data)
780 {
781   meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
782 }
783 
784 void
meta_screen_queue_window_resizes(MetaScreen * screen)785 meta_screen_queue_window_resizes (MetaScreen *screen)
786 {
787   meta_screen_foreach_window (screen, queue_resize, NULL);
788 }
789 
790 int
meta_screen_get_n_workspaces(MetaScreen * screen)791 meta_screen_get_n_workspaces (MetaScreen *screen)
792 {
793   return g_list_length (screen->workspaces);
794 }
795 
796 MetaWorkspace*
meta_screen_get_workspace_by_index(MetaScreen * screen,int idx)797 meta_screen_get_workspace_by_index (MetaScreen  *screen,
798                                     int          idx)
799 {
800   GList *tmp;
801   int i;
802 
803   /* should be robust, idx is maybe from an app */
804   if (idx < 0)
805     return NULL;
806 
807   i = 0;
808   tmp = screen->workspaces;
809   while (tmp != NULL)
810     {
811       MetaWorkspace *w = tmp->data;
812 
813       if (i == idx)
814         return w;
815 
816       ++i;
817       tmp = tmp->next;
818     }
819 
820   return NULL;
821 }
822 
823 static void
set_number_of_spaces_hint(MetaScreen * screen,int n_spaces)824 set_number_of_spaces_hint (MetaScreen *screen,
825                            int         n_spaces)
826 {
827   unsigned long data[1];
828 
829   if (screen->closing > 0)
830     return;
831 
832   data[0] = n_spaces;
833 
834   meta_verbose ("Setting _NET_NUMBER_OF_DESKTOPS to %lu\n", data[0]);
835 
836   meta_error_trap_push (screen->display);
837   XChangeProperty (screen->display->xdisplay, screen->xroot,
838                    screen->display->atom__NET_NUMBER_OF_DESKTOPS,
839                    XA_CARDINAL,
840                    32, PropModeReplace, (guchar*) data, 1);
841   meta_error_trap_pop (screen->display);
842 }
843 
844 static void
set_desktop_geometry_hint(MetaScreen * screen)845 set_desktop_geometry_hint (MetaScreen *screen)
846 {
847   unsigned long data[2];
848 
849   if (screen->closing > 0)
850     return;
851 
852   data[0] = screen->rect.width;
853   data[1] = screen->rect.height;
854 
855   meta_verbose ("Setting _NET_DESKTOP_GEOMETRY to %lu, %lu\n", data[0], data[1]);
856 
857   meta_error_trap_push (screen->display);
858   XChangeProperty (screen->display->xdisplay, screen->xroot,
859                    screen->display->atom__NET_DESKTOP_GEOMETRY,
860                    XA_CARDINAL,
861                    32, PropModeReplace, (guchar*) data, 2);
862   meta_error_trap_pop (screen->display);
863 }
864 
865 static void
set_desktop_viewport_hint(MetaScreen * screen)866 set_desktop_viewport_hint (MetaScreen *screen)
867 {
868   unsigned long data[2];
869 
870   if (screen->closing > 0)
871     return;
872 
873   /*
874    * Metacity does not implement viewports, so this is a fixed 0,0
875    */
876   data[0] = 0;
877   data[1] = 0;
878 
879   meta_verbose ("Setting _NET_DESKTOP_VIEWPORT to 0, 0\n");
880 
881   meta_error_trap_push (screen->display);
882   XChangeProperty (screen->display->xdisplay, screen->xroot,
883                    screen->display->atom__NET_DESKTOP_VIEWPORT,
884                    XA_CARDINAL,
885                    32, PropModeReplace, (guchar*) data, 2);
886   meta_error_trap_pop (screen->display);
887 }
888 
889 static void
update_num_workspaces(MetaScreen * screen,guint32 timestamp)890 update_num_workspaces (MetaScreen *screen,
891                        guint32     timestamp)
892 {
893   int new_num;
894   GList *tmp;
895   int i;
896   GList *extras;
897   MetaWorkspace *last_remaining;
898   gboolean need_change_space;
899 
900   new_num = meta_prefs_get_num_workspaces ();
901 
902   g_assert (new_num > 0);
903 
904   last_remaining = NULL;
905   extras = NULL;
906   i = 0;
907   tmp = screen->workspaces;
908   while (tmp != NULL)
909     {
910       MetaWorkspace *w = tmp->data;
911 
912       if (i >= new_num)
913         extras = g_list_prepend (extras, w);
914       else
915         last_remaining = w;
916 
917       ++i;
918       tmp = tmp->next;
919     }
920 
921   g_assert (last_remaining);
922 
923   /* Get rid of the extra workspaces by moving all their windows
924    * to last_remaining, then activating last_remaining if
925    * one of the removed workspaces was active. This will be a bit
926    * wacky if the config tool for changing number of workspaces
927    * is on a removed workspace ;-)
928    */
929   need_change_space = FALSE;
930   tmp = extras;
931   while (tmp != NULL)
932     {
933       MetaWorkspace *w = tmp->data;
934 
935       meta_workspace_relocate_windows (w, last_remaining);
936 
937       if (w == screen->active_workspace)
938         need_change_space = TRUE;
939 
940       tmp = tmp->next;
941     }
942 
943   if (need_change_space)
944     meta_workspace_activate (last_remaining, timestamp);
945 
946   /* Should now be safe to free the workspaces */
947   tmp = extras;
948   while (tmp != NULL)
949     {
950       MetaWorkspace *w = tmp->data;
951 
952       g_assert (w->windows == NULL);
953       meta_workspace_free (w);
954 
955       tmp = tmp->next;
956     }
957 
958   g_list_free (extras);
959 
960   while (i < new_num)
961     {
962       meta_workspace_new (screen);
963       ++i;
964     }
965 
966   set_number_of_spaces_hint (screen, new_num);
967 
968   meta_screen_queue_workarea_recalc (screen);
969 }
970 
971 static void
update_focus_mode(MetaScreen * screen)972 update_focus_mode (MetaScreen *screen)
973 {
974   /* nothing to do anymore */ ;
975 }
976 
977 void
meta_screen_set_cursor(MetaScreen * screen,MetaCursor cursor)978 meta_screen_set_cursor (MetaScreen *screen,
979                         MetaCursor  cursor)
980 {
981   Cursor xcursor;
982 
983   if (cursor == screen->current_cursor)
984     return;
985 
986   screen->current_cursor = cursor;
987 
988   xcursor = meta_display_create_x_cursor (screen->display, cursor);
989   XDefineCursor (screen->display->xdisplay, screen->xroot, xcursor);
990   XFlush (screen->display->xdisplay);
991   XFreeCursor (screen->display->xdisplay, xcursor);
992 }
993 
994 void
meta_screen_update_cursor(MetaScreen * screen)995 meta_screen_update_cursor (MetaScreen *screen)
996 {
997   Cursor xcursor;
998 
999   xcursor = meta_display_create_x_cursor (screen->display,
1000                                           screen->current_cursor);
1001   XDefineCursor (screen->display->xdisplay, screen->xroot, xcursor);
1002   XFlush (screen->display->xdisplay);
1003   XFreeCursor (screen->display->xdisplay, xcursor);
1004 }
1005 
1006 #define MAX_PREVIEW_SIZE 150.0
1007 
1008 static GdkPixbuf *
get_window_pixbuf(MetaWindow * window,int * width,int * height)1009 get_window_pixbuf (MetaWindow *window,
1010                    int        *width,
1011                    int        *height)
1012 {
1013   MetaDisplay *display;
1014   cairo_surface_t *surface;
1015   GdkPixbuf *pixbuf, *scaled;
1016   double ratio;
1017 
1018   display = window->display;
1019   surface = meta_compositor_get_window_surface (display->compositor, window);
1020   if (surface == NULL)
1021     return NULL;
1022 
1023   meta_error_trap_push (display);
1024 
1025   pixbuf = meta_ui_get_pixbuf_from_surface (surface);
1026   cairo_surface_destroy (surface);
1027 
1028   if (meta_error_trap_pop_with_return (display) != Success)
1029     g_clear_object (&pixbuf);
1030 
1031   if (pixbuf == NULL)
1032     return NULL;
1033 
1034   *width = gdk_pixbuf_get_width (pixbuf);
1035   *height = gdk_pixbuf_get_height (pixbuf);
1036 
1037   /* Scale pixbuf to max dimension MAX_PREVIEW_SIZE */
1038   if (*width > *height)
1039     {
1040       ratio = ((double) *width) / MAX_PREVIEW_SIZE;
1041       *width = (int) MAX_PREVIEW_SIZE;
1042       *height = (int) (((double) *height) / ratio);
1043     }
1044   else
1045     {
1046       ratio = ((double) *height) / MAX_PREVIEW_SIZE;
1047       *height = (int) MAX_PREVIEW_SIZE;
1048       *width = (int) (((double) *width) / ratio);
1049     }
1050 
1051   scaled = gdk_pixbuf_scale_simple (pixbuf, *width, *height,
1052                                     GDK_INTERP_BILINEAR);
1053   g_object_unref (pixbuf);
1054   return scaled;
1055 }
1056 
1057 void
meta_screen_ensure_tab_popup(MetaScreen * screen,MetaTabList list_type,MetaTabShowType show_type)1058 meta_screen_ensure_tab_popup (MetaScreen      *screen,
1059                               MetaTabList      list_type,
1060                               MetaTabShowType  show_type)
1061 {
1062   MetaTabEntry *entries;
1063   GList *tab_list;
1064   GList *tmp;
1065   int len;
1066   int i;
1067 
1068   if (screen->tab_popup)
1069     return;
1070 
1071   tab_list = meta_display_get_tab_list (screen->display,
1072                                         list_type,
1073                                         screen,
1074                                         screen->active_workspace);
1075 
1076   len = g_list_length (tab_list);
1077 
1078   entries = g_new (MetaTabEntry, len + 1);
1079   entries[len].key = NULL;
1080   entries[len].title = NULL;
1081   entries[len].icon = NULL;
1082 
1083   i = 0;
1084   tmp = tab_list;
1085   while (i < len)
1086     {
1087       MetaWindow *window;
1088       MetaRectangle r;
1089       GdkPixbuf *win_pixbuf;
1090       int width, height;
1091 
1092       window = tmp->data;
1093 
1094       entries[i].key = (MetaTabEntryKey) window->xwindow;
1095       entries[i].title = window->title;
1096 
1097       win_pixbuf = NULL;
1098       if (meta_prefs_get_alt_tab_thumbnails ())
1099         win_pixbuf = get_window_pixbuf (window, &width, &height);
1100 
1101       if (win_pixbuf == NULL)
1102         entries[i].icon = g_object_ref (window->icon);
1103       else
1104         {
1105           GdkPixbuf *scaled;
1106           int icon_width, icon_height, t_width, t_height;
1107 
1108 #define ICON_SIZE 32
1109 #define ICON_OFFSET 6
1110 
1111           scaled = gdk_pixbuf_scale_simple (window->icon,
1112                                             ICON_SIZE, ICON_SIZE,
1113                                             GDK_INTERP_BILINEAR);
1114 
1115           icon_width = gdk_pixbuf_get_width (scaled);
1116           icon_height = gdk_pixbuf_get_height (scaled);
1117 
1118           t_width = width + ICON_OFFSET;
1119           t_height = height + ICON_OFFSET;
1120 
1121           entries[i].icon = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
1122                                             t_width, t_height);
1123           gdk_pixbuf_fill (entries[i].icon, 0x00000000);
1124           gdk_pixbuf_copy_area (win_pixbuf, 0, 0, width, height,
1125                                 entries[i].icon, 0, 0);
1126           g_object_unref (win_pixbuf);
1127           gdk_pixbuf_composite (scaled, entries[i].icon,
1128                                 t_width - icon_width, t_height - icon_height,
1129                                 icon_width, icon_height,
1130                                 t_width - icon_width, t_height - icon_height,
1131                                 1.0, 1.0, GDK_INTERP_BILINEAR, 255);
1132 
1133           g_object_unref (scaled);
1134         }
1135 
1136       entries[i].blank = FALSE;
1137       entries[i].hidden = !meta_window_showing_on_its_workspace (window);
1138       entries[i].demands_attention = window->wm_state_demands_attention;
1139 
1140       if (show_type == META_TAB_SHOW_INSTANTLY ||
1141           !entries[i].hidden                   ||
1142           !meta_window_get_icon_geometry (window, &r))
1143         meta_window_get_outer_rect (window, &r);
1144 
1145       entries[i].rect = r;
1146 
1147       /* Find inside of highlight rectangle to be used when window is
1148        * outlined for tabbing.  This should be the size of the
1149        * east/west frame, and the size of the south frame, on those
1150        * sides.  On the top it should be the size of the south frame
1151        * edge.
1152        */
1153 #define OUTLINE_WIDTH 5
1154       /* Top side */
1155       entries[i].inner_rect.y = OUTLINE_WIDTH;
1156 
1157       /* Bottom side */
1158       entries[i].inner_rect.height = r.height - entries[i].inner_rect.y - OUTLINE_WIDTH;
1159 
1160       /* Left side */
1161       entries[i].inner_rect.x = OUTLINE_WIDTH;
1162 
1163       /* Right side */
1164         entries[i].inner_rect.width = r.width - entries[i].inner_rect.x - OUTLINE_WIDTH;
1165 
1166       ++i;
1167       tmp = tmp->next;
1168     }
1169 
1170   screen->tab_popup = meta_ui_tab_popup_new (entries,
1171                                              len,
1172                                              5, /* FIXME */
1173                                              TRUE);
1174 
1175   for (i = 0; i < len; i++)
1176     g_object_unref (entries[i].icon);
1177 
1178   g_free (entries);
1179 
1180   g_list_free (tab_list);
1181 
1182   /* don't show tab popup, since proper window isn't selected yet */
1183 }
1184 
1185 void
meta_screen_ensure_workspace_popup(MetaScreen * screen)1186 meta_screen_ensure_workspace_popup (MetaScreen *screen)
1187 {
1188   MetaTabEntry *entries;
1189   int len;
1190   int i;
1191   MetaWorkspaceLayout layout;
1192   int n_workspaces;
1193   int current_workspace;
1194 
1195   if (screen->tab_popup)
1196     return;
1197 
1198   current_workspace = meta_workspace_index (screen->active_workspace);
1199   n_workspaces = meta_screen_get_n_workspaces (screen);
1200 
1201   meta_screen_calc_workspace_layout (screen, n_workspaces,
1202                                      current_workspace, &layout);
1203 
1204   len = layout.grid_area;
1205 
1206   entries = g_new (MetaTabEntry, len + 1);
1207   entries[len].key = NULL;
1208   entries[len].title = NULL;
1209   entries[len].icon = NULL;
1210 
1211   i = 0;
1212   while (i < len)
1213     {
1214       if (layout.grid[i] >= 0)
1215         {
1216           MetaWorkspace *workspace;
1217 
1218           workspace = meta_screen_get_workspace_by_index (screen,
1219                                                           layout.grid[i]);
1220 
1221           entries[i].key = (MetaTabEntryKey) workspace;
1222           entries[i].title = meta_workspace_get_name (workspace);
1223           entries[i].icon = NULL;
1224           entries[i].blank = FALSE;
1225 
1226           g_assert (entries[i].title != NULL);
1227         }
1228       else
1229         {
1230           entries[i].key = NULL;
1231           entries[i].title = NULL;
1232           entries[i].icon = NULL;
1233           entries[i].blank = TRUE;
1234         }
1235       entries[i].hidden = FALSE;
1236       entries[i].demands_attention = FALSE;
1237 
1238       ++i;
1239     }
1240 
1241   screen->tab_popup = meta_ui_tab_popup_new (entries,
1242                                              len,
1243                                              layout.cols,
1244                                              FALSE);
1245 
1246   g_free (entries);
1247   meta_screen_free_workspace_layout (&layout);
1248 
1249   /* don't show tab popup, since proper space isn't selected yet */
1250 }
1251 
1252 static gboolean
meta_screen_tile_preview_update_timeout(gpointer data)1253 meta_screen_tile_preview_update_timeout (gpointer data)
1254 {
1255   MetaScreen *screen = data;
1256   MetaWindow *window = screen->display->grab_window;
1257   gboolean needs_preview = FALSE;
1258 
1259   screen->tile_preview_timeout_id = 0;
1260 
1261   if (!screen->tile_preview)
1262     screen->tile_preview = meta_tile_preview_new ();
1263 
1264   if (window)
1265     {
1266       switch (window->tile_mode)
1267         {
1268           case META_TILE_LEFT:
1269           case META_TILE_RIGHT:
1270               if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
1271                 needs_preview = TRUE;
1272               break;
1273 
1274           case META_TILE_MAXIMIZED:
1275               if (!META_WINDOW_MAXIMIZED (window))
1276                 needs_preview = TRUE;
1277               break;
1278 
1279           default:
1280               needs_preview = FALSE;
1281               break;
1282         }
1283     }
1284 
1285   if (needs_preview)
1286     {
1287       MetaRectangle tile_rect;
1288 
1289       meta_window_get_current_tile_area (window, &tile_rect);
1290       meta_tile_preview_show (screen->tile_preview, &tile_rect);
1291     }
1292   else
1293     meta_tile_preview_hide (screen->tile_preview);
1294 
1295   return FALSE;
1296 }
1297 
1298 #define TILE_PREVIEW_TIMEOUT_MS 200
1299 
1300 void
meta_screen_tile_preview_update(MetaScreen * screen,gboolean delay)1301 meta_screen_tile_preview_update (MetaScreen *screen,
1302                                  gboolean delay)
1303 {
1304   if (delay)
1305     {
1306       if (screen->tile_preview_timeout_id > 0)
1307         return;
1308 
1309       screen->tile_preview_timeout_id =
1310         g_timeout_add (TILE_PREVIEW_TIMEOUT_MS,
1311                        meta_screen_tile_preview_update_timeout,
1312                        screen);
1313     }
1314   else
1315     {
1316       if (screen->tile_preview_timeout_id > 0)
1317         g_source_remove (screen->tile_preview_timeout_id);
1318 
1319       meta_screen_tile_preview_update_timeout ((gpointer)screen);
1320     }
1321 }
1322 
1323 void
meta_screen_tile_preview_hide(MetaScreen * screen)1324 meta_screen_tile_preview_hide (MetaScreen *screen)
1325 {
1326   if (screen->tile_preview_timeout_id > 0)
1327     g_source_remove (screen->tile_preview_timeout_id);
1328   screen->tile_preview_timeout_id = 0;
1329 
1330   if (screen->tile_preview)
1331     meta_tile_preview_hide (screen->tile_preview);
1332 }
1333 
1334 MetaWindow*
meta_screen_get_mouse_window(MetaScreen * screen,MetaWindow * not_this_one)1335 meta_screen_get_mouse_window (MetaScreen  *screen,
1336                               MetaWindow  *not_this_one)
1337 {
1338   MetaWindow *window;
1339   Window root_return, child_return;
1340   int root_x_return, root_y_return;
1341   int win_x_return, win_y_return;
1342   unsigned int mask_return;
1343 
1344   if (not_this_one)
1345     meta_topic (META_DEBUG_FOCUS,
1346                 "Focusing mouse window excluding %s\n", not_this_one->desc);
1347 
1348   meta_error_trap_push (screen->display);
1349   XQueryPointer (screen->display->xdisplay,
1350                  screen->xroot,
1351                  &root_return,
1352                  &child_return,
1353                  &root_x_return,
1354                  &root_y_return,
1355                  &win_x_return,
1356                  &win_y_return,
1357                  &mask_return);
1358   meta_error_trap_pop (screen->display);
1359 
1360   window = meta_stack_get_default_focus_window_at_point (screen->stack,
1361                                                          screen->active_workspace,
1362                                                          not_this_one,
1363                                                          root_x_return,
1364                                                          root_y_return);
1365 
1366   return window;
1367 }
1368 
1369 const MetaMonitorInfo *
meta_screen_get_monitor_for_rect(MetaScreen * screen,MetaRectangle * rect)1370 meta_screen_get_monitor_for_rect (MetaScreen    *screen,
1371                                   MetaRectangle *rect)
1372 {
1373   int i;
1374   int best_monitor, monitor_score;
1375 
1376   if (screen->n_monitor_infos == 1)
1377     return &screen->monitor_infos[0];
1378 
1379   best_monitor = 0;
1380   monitor_score = 0;
1381 
1382   for (i = 0; i < screen->n_monitor_infos; i++)
1383     {
1384       MetaRectangle dest;
1385       if (meta_rectangle_intersect (&screen->monitor_infos[i].rect,
1386                                     rect,
1387                                     &dest))
1388         {
1389           int cur = meta_rectangle_area (&dest);
1390           if (cur > monitor_score)
1391             {
1392               monitor_score = cur;
1393               best_monitor = i;
1394             }
1395         }
1396     }
1397 
1398   return &screen->monitor_infos[best_monitor];
1399 }
1400 
1401 const MetaMonitorInfo *
meta_screen_get_monitor_for_window(MetaScreen * screen,MetaWindow * window)1402 meta_screen_get_monitor_for_window (MetaScreen *screen,
1403                                     MetaWindow *window)
1404 {
1405   MetaRectangle window_rect;
1406 
1407   meta_window_get_outer_rect (window, &window_rect);
1408 
1409   return meta_screen_get_monitor_for_rect (screen, &window_rect);
1410 }
1411 
1412 const MetaMonitorInfo *
meta_screen_get_monitor_neighbor(MetaScreen * screen,int which_monitor,MetaScreenDirection direction)1413 meta_screen_get_monitor_neighbor (MetaScreen          *screen,
1414                                   int                  which_monitor,
1415                                   MetaScreenDirection  direction)
1416 {
1417   MetaMonitorInfo *input = screen->monitor_infos + which_monitor;
1418   MetaMonitorInfo *current;
1419   int i;
1420 
1421   for (i = 0; i < screen->n_monitor_infos; i++)
1422     {
1423       current = screen->monitor_infos + i;
1424 
1425       if ((direction == META_SCREEN_RIGHT &&
1426            current->rect.x == input->rect.x + input->rect.width &&
1427            meta_rectangle_vert_overlap(&current->rect, &input->rect)) ||
1428           (direction == META_SCREEN_LEFT &&
1429            input->rect.x == current->rect.x + current->rect.width &&
1430            meta_rectangle_vert_overlap(&current->rect, &input->rect)) ||
1431           (direction == META_SCREEN_UP &&
1432            input->rect.y == current->rect.y + current->rect.height &&
1433            meta_rectangle_horiz_overlap(&current->rect, &input->rect)) ||
1434           (direction == META_SCREEN_DOWN &&
1435            current->rect.y == input->rect.y + input->rect.height &&
1436            meta_rectangle_horiz_overlap(&current->rect, &input->rect)))
1437         {
1438           return current;
1439         }
1440     }
1441 
1442   return NULL;
1443 }
1444 
1445 void
meta_screen_get_natural_monitor_list(MetaScreen * screen,int ** monitors_list,int * n_monitors)1446 meta_screen_get_natural_monitor_list (MetaScreen *screen,
1447                                       int**       monitors_list,
1448                                       int*        n_monitors)
1449 {
1450   const MetaMonitorInfo *current;
1451   const MetaMonitorInfo *tmp;
1452   GQueue* monitor_queue;
1453   int* visited;
1454   int cur = 0;
1455   int i;
1456 
1457   *n_monitors = screen->n_monitor_infos;
1458   *monitors_list = g_new (int, screen->n_monitor_infos);
1459 
1460   /* we calculate a natural ordering by which to choose monitors for
1461    * window placement. We start at the current monitor, and perform
1462    * a breadth-first search of the monitors starting from that monitor.
1463    * We choose preferentially left, then right, then down, then up.
1464    * The visitation order produced by this traversal is the natural
1465    * monitor ordering.
1466    */
1467 
1468   visited = g_new (int, screen->n_monitor_infos);
1469   for (i = 0; i < screen->n_monitor_infos; i++)
1470     {
1471       visited[i] = FALSE;
1472     }
1473 
1474   current = meta_screen_get_current_monitor (screen);
1475   monitor_queue = g_queue_new ();
1476   g_queue_push_tail (monitor_queue, (gpointer) current);
1477   visited[current->number] = TRUE;
1478 
1479   while (!g_queue_is_empty (monitor_queue))
1480     {
1481       current = (const MetaMonitorInfo *) g_queue_pop_head (monitor_queue);
1482 
1483       (*monitors_list)[cur++] = current->number;
1484 
1485       /* enqueue each of the directions */
1486       tmp = meta_screen_get_monitor_neighbor (screen,
1487                                               current->number,
1488                                               META_SCREEN_LEFT);
1489 
1490       if (tmp && !visited[tmp->number])
1491         {
1492           g_queue_push_tail (monitor_queue, (MetaMonitorInfo *) tmp);
1493           visited[tmp->number] = TRUE;
1494         }
1495 
1496       tmp = meta_screen_get_monitor_neighbor (screen,
1497                                               current->number,
1498                                               META_SCREEN_RIGHT);
1499 
1500       if (tmp && !visited[tmp->number])
1501         {
1502           g_queue_push_tail (monitor_queue, (MetaMonitorInfo *) tmp);
1503           visited[tmp->number] = TRUE;
1504         }
1505 
1506       tmp = meta_screen_get_monitor_neighbor (screen,
1507                                               current->number,
1508                                               META_SCREEN_UP);
1509 
1510       if (tmp && !visited[tmp->number])
1511         {
1512           g_queue_push_tail (monitor_queue, (MetaMonitorInfo *) tmp);
1513           visited[tmp->number] = TRUE;
1514         }
1515 
1516       tmp = meta_screen_get_monitor_neighbor (screen,
1517                                               current->number,
1518                                               META_SCREEN_DOWN);
1519 
1520       if (tmp && !visited[tmp->number])
1521         {
1522           g_queue_push_tail (monitor_queue, (MetaMonitorInfo *) tmp);
1523           visited[tmp->number] = TRUE;
1524         }
1525     }
1526 
1527   /* in case we somehow missed some set of monitors, go through the
1528    * visited list and add in any monitors that were missed
1529    */
1530   for (i = 0; i < screen->n_monitor_infos; i++)
1531     {
1532       if (visited[i] == FALSE)
1533         {
1534           (*monitors_list)[cur++] = i;
1535         }
1536     }
1537 
1538   g_free (visited);
1539   g_queue_free (monitor_queue);
1540 }
1541 
1542 const MetaMonitorInfo *
meta_screen_get_current_monitor(MetaScreen * screen)1543 meta_screen_get_current_monitor (MetaScreen *screen)
1544 {
1545   if (screen->n_monitor_infos == 1)
1546     return &screen->monitor_infos[0];
1547 
1548   /* Sadly, we have to do it this way. Yuck.
1549    */
1550 
1551   if (screen->display->monitor_cache_invalidated)
1552     {
1553       Window root_return, child_return;
1554       int win_x_return, win_y_return;
1555       unsigned int mask_return;
1556       int i;
1557       MetaRectangle pointer_position;
1558 
1559       screen->display->monitor_cache_invalidated = FALSE;
1560 
1561       pointer_position.width = pointer_position.height = 1;
1562       XQueryPointer (screen->display->xdisplay,
1563                      screen->xroot,
1564                      &root_return,
1565                      &child_return,
1566                      &pointer_position.x,
1567                      &pointer_position.y,
1568                      &win_x_return,
1569                      &win_y_return,
1570                      &mask_return);
1571 
1572       screen->last_monitor_index = 0;
1573       for (i = 0; i < screen->n_monitor_infos; i++)
1574         {
1575           if (meta_rectangle_contains_rect (&screen->monitor_infos[i].rect,
1576                                             &pointer_position))
1577             {
1578               screen->last_monitor_index = i;
1579               break;
1580             }
1581         }
1582 
1583       meta_topic (META_DEBUG_XINERAMA,
1584                   "Rechecked current monitor, now %d\n",
1585                   screen->last_monitor_index);
1586     }
1587 
1588   return &screen->monitor_infos[screen->last_monitor_index];
1589 }
1590 
1591 #define _NET_WM_ORIENTATION_HORZ 0
1592 #define _NET_WM_ORIENTATION_VERT 1
1593 
1594 #define _NET_WM_TOPLEFT     0
1595 #define _NET_WM_TOPRIGHT    1
1596 #define _NET_WM_BOTTOMRIGHT 2
1597 #define _NET_WM_BOTTOMLEFT  3
1598 
1599 void
meta_screen_update_workspace_layout(MetaScreen * screen)1600 meta_screen_update_workspace_layout (MetaScreen *screen)
1601 {
1602   gulong *list;
1603   int n_items;
1604 
1605   list = NULL;
1606   n_items = 0;
1607 
1608   if (meta_prop_get_cardinal_list (screen->display,
1609                                    screen->xroot,
1610                                    screen->display->atom__NET_DESKTOP_LAYOUT,
1611                                    &list, &n_items))
1612     {
1613       if (n_items == 3 || n_items == 4)
1614         {
1615           int cols, rows;
1616 
1617           switch (list[0])
1618             {
1619             case _NET_WM_ORIENTATION_HORZ:
1620               screen->vertical_workspaces = FALSE;
1621               break;
1622             case _NET_WM_ORIENTATION_VERT:
1623               screen->vertical_workspaces = TRUE;
1624               break;
1625             default:
1626               g_warning ("Someone set a weird orientation in _NET_DESKTOP_LAYOUT");
1627               break;
1628             }
1629 
1630           cols = list[1];
1631           rows = list[2];
1632 
1633           if (rows <= 0 && cols <= 0)
1634             {
1635               g_warning ("Columns = %d rows = %d in _NET_DESKTOP_LAYOUT makes no sense",
1636                          rows, cols);
1637             }
1638           else
1639             {
1640               if (rows > 0)
1641                 screen->rows_of_workspaces = rows;
1642               else
1643                 screen->rows_of_workspaces = -1;
1644 
1645               if (cols > 0)
1646                 screen->columns_of_workspaces = cols;
1647               else
1648                 screen->columns_of_workspaces = -1;
1649             }
1650 
1651           if (n_items == 4)
1652             {
1653               switch (list[3])
1654                 {
1655                   case _NET_WM_TOPLEFT:
1656                     screen->starting_corner = META_SCREEN_TOPLEFT;
1657                     break;
1658                   case _NET_WM_TOPRIGHT:
1659                     screen->starting_corner = META_SCREEN_TOPRIGHT;
1660                     break;
1661                   case _NET_WM_BOTTOMRIGHT:
1662                     screen->starting_corner = META_SCREEN_BOTTOMRIGHT;
1663                     break;
1664                   case _NET_WM_BOTTOMLEFT:
1665                     screen->starting_corner = META_SCREEN_BOTTOMLEFT;
1666                     break;
1667                   default:
1668                     g_warning ("Someone set a weird starting corner in _NET_DESKTOP_LAYOUT");
1669                     break;
1670                 }
1671             }
1672           else
1673             screen->starting_corner = META_SCREEN_TOPLEFT;
1674         }
1675       else
1676         {
1677           g_warning ("Someone set _NET_DESKTOP_LAYOUT to %d integers instead of 4 "
1678                      "(3 is accepted for backwards compat)", n_items);
1679         }
1680 
1681       meta_XFree (list);
1682     }
1683 
1684   meta_verbose ("Workspace layout rows = %d cols = %d orientation = %d starting corner = %u\n",
1685                 screen->rows_of_workspaces,
1686                 screen->columns_of_workspaces,
1687                 screen->vertical_workspaces,
1688                 screen->starting_corner);
1689 }
1690 
1691 static void
set_workspace_names(MetaScreen * screen)1692 set_workspace_names (MetaScreen *screen)
1693 {
1694   /* This updates names on root window when the pref changes,
1695    * note we only get prefs change notify if things have
1696    * really changed.
1697    */
1698   GString *flattened;
1699   int i;
1700   int n_spaces;
1701 
1702   /* flatten to nul-separated list */
1703   n_spaces = meta_screen_get_n_workspaces (screen);
1704   flattened = g_string_new ("");
1705   i = 0;
1706   while (i < n_spaces)
1707     {
1708       const char *name;
1709 
1710       name = meta_prefs_get_workspace_name (i);
1711 
1712       if (name)
1713         g_string_append_len (flattened, name,
1714                              strlen (name) + 1);
1715       else
1716         g_string_append_len (flattened, "", 1);
1717 
1718       ++i;
1719     }
1720 
1721   meta_error_trap_push (screen->display);
1722   XChangeProperty (screen->display->xdisplay,
1723                    screen->xroot,
1724                    screen->display->atom__NET_DESKTOP_NAMES,
1725                    screen->display->atom_UTF8_STRING,
1726                    8, PropModeReplace,
1727                    (unsigned char *)flattened->str, flattened->len);
1728   meta_error_trap_pop (screen->display);
1729 
1730   g_string_free (flattened, TRUE);
1731 }
1732 
1733 void
meta_screen_update_workspace_names(MetaScreen * screen)1734 meta_screen_update_workspace_names (MetaScreen *screen)
1735 {
1736   char **names;
1737   int n_names;
1738   int i;
1739 
1740   /* this updates names in prefs when the root window property changes,
1741    * iff the new property contents don't match what's already in prefs
1742    */
1743 
1744   names = NULL;
1745   n_names = 0;
1746   if (!meta_prop_get_utf8_list (screen->display,
1747                                 screen->xroot,
1748                                 screen->display->atom__NET_DESKTOP_NAMES,
1749                                 &names, &n_names))
1750     {
1751       meta_verbose ("Failed to get workspace names from root window %d\n",
1752                     screen->number);
1753       return;
1754     }
1755 
1756   i = 0;
1757   while (i < n_names)
1758     {
1759       meta_topic (META_DEBUG_PREFS,
1760                   "Setting workspace %d name to \"%s\" due to _NET_DESKTOP_NAMES change\n",
1761                   i, names[i] ? names[i] : "null");
1762       meta_prefs_change_workspace_name (i, names[i]);
1763 
1764       ++i;
1765     }
1766 
1767   g_strfreev (names);
1768 }
1769 
1770 Window
meta_create_offscreen_window(Display * xdisplay,Window parent,long valuemask)1771 meta_create_offscreen_window (Display *xdisplay,
1772                               Window   parent,
1773                               long     valuemask)
1774 {
1775   XSetWindowAttributes attrs;
1776 
1777   /* we want to be override redirect because sometimes we
1778    * create a window on a screen we aren't managing.
1779    * (but on a display we are managing at least one screen for)
1780    */
1781   attrs.override_redirect = True;
1782   attrs.event_mask = valuemask;
1783 
1784   return XCreateWindow (xdisplay,
1785                         parent,
1786                         -100, -100, 1, 1,
1787                         0,
1788                         CopyFromParent,
1789                         CopyFromParent,
1790                         (Visual *)CopyFromParent,
1791                         CWOverrideRedirect | CWEventMask,
1792                         &attrs);
1793 }
1794 
1795 static void
set_workspace_work_area_hint(MetaWorkspace * workspace,MetaScreen * screen)1796 set_workspace_work_area_hint (MetaWorkspace *workspace,
1797                               MetaScreen    *screen)
1798 {
1799   unsigned long *data;
1800   unsigned long *tmp;
1801   int i;
1802   gchar *workarea_name;
1803   Atom workarea_atom;
1804 
1805   data = g_new (unsigned long, screen->n_monitor_infos * 4);
1806   tmp = data;
1807 
1808   for (i = 0; i < screen->n_monitor_infos; i++)
1809     {
1810       MetaRectangle area;
1811 
1812       meta_workspace_get_work_area_for_monitor (workspace, i, &area);
1813 
1814       tmp[0] = area.x;
1815       tmp[1] = area.y;
1816       tmp[2] = area.width;
1817       tmp[3] = area.height;
1818 
1819       tmp += 4;
1820     }
1821 
1822   workarea_name = g_strdup_printf ("_GTK_WORKAREAS_D%d",
1823                                    meta_workspace_index (workspace));
1824 
1825   workarea_atom = XInternAtom (screen->display->xdisplay, workarea_name, False);
1826   g_free (workarea_name);
1827 
1828   meta_error_trap_push (screen->display);
1829   XChangeProperty (screen->display->xdisplay, screen->xroot, workarea_atom,
1830                    XA_CARDINAL, 32, PropModeReplace,
1831                    (guchar*) data, screen->n_monitor_infos * 4);
1832   meta_error_trap_pop (screen->display);
1833 
1834   g_free (data);
1835 }
1836 
1837 static void
set_work_area_hint(MetaScreen * screen)1838 set_work_area_hint (MetaScreen *screen)
1839 {
1840   int num_workspaces;
1841   GList *tmp_list;
1842   unsigned long *data, *tmp;
1843   MetaRectangle area;
1844 
1845   num_workspaces = meta_screen_get_n_workspaces (screen);
1846   data = g_new (unsigned long, num_workspaces * 4);
1847   tmp_list = screen->workspaces;
1848   tmp = data;
1849 
1850   while (tmp_list != NULL)
1851     {
1852       MetaWorkspace *workspace = tmp_list->data;
1853 
1854       if (workspace->screen == screen)
1855         {
1856           meta_workspace_get_work_area_all_monitors (workspace, &area);
1857           set_workspace_work_area_hint (workspace, screen);
1858 
1859           tmp[0] = area.x;
1860           tmp[1] = area.y;
1861           tmp[2] = area.width;
1862           tmp[3] = area.height;
1863 
1864           tmp += 4;
1865         }
1866 
1867       tmp_list = tmp_list->next;
1868     }
1869 
1870   meta_error_trap_push (screen->display);
1871   XChangeProperty (screen->display->xdisplay, screen->xroot,
1872                    screen->display->atom__NET_WORKAREA,
1873                    XA_CARDINAL, 32, PropModeReplace,
1874                    (guchar*) data, num_workspaces*4);
1875   g_free (data);
1876   meta_error_trap_pop (screen->display);
1877 }
1878 
1879 static gboolean
set_work_area_idle_func(MetaScreen * screen)1880 set_work_area_idle_func (MetaScreen *screen)
1881 {
1882   meta_topic (META_DEBUG_WORKAREA,
1883               "Running work area idle function\n");
1884 
1885   screen->work_area_idle = 0;
1886 
1887   set_work_area_hint (screen);
1888 
1889   return FALSE;
1890 }
1891 
1892 void
meta_screen_queue_workarea_recalc(MetaScreen * screen)1893 meta_screen_queue_workarea_recalc (MetaScreen *screen)
1894 {
1895   /* Recompute work area in an idle */
1896   if (screen->work_area_idle == 0)
1897     {
1898       meta_topic (META_DEBUG_WORKAREA,
1899                   "Adding work area hint idle function\n");
1900       screen->work_area_idle =
1901         g_idle_add_full (META_PRIORITY_BEFORE_REDRAW,
1902                          (GSourceFunc) set_work_area_idle_func,
1903                          screen,
1904                          NULL);
1905     }
1906 }
1907 
1908 static const gchar *
meta_screen_corner_to_string(MetaScreenCorner corner)1909 meta_screen_corner_to_string (MetaScreenCorner corner)
1910 {
1911   switch (corner)
1912     {
1913     case META_SCREEN_TOPLEFT:
1914       return "TopLeft";
1915     case META_SCREEN_TOPRIGHT:
1916       return "TopRight";
1917     case META_SCREEN_BOTTOMLEFT:
1918       return "BottomLeft";
1919     case META_SCREEN_BOTTOMRIGHT:
1920       return "BottomRight";
1921     default:
1922       break;
1923     }
1924 
1925   return "Unknown";
1926 }
1927 
1928 void
meta_screen_calc_workspace_layout(MetaScreen * screen,int num_workspaces,int current_space,MetaWorkspaceLayout * layout)1929 meta_screen_calc_workspace_layout (MetaScreen          *screen,
1930                                    int                  num_workspaces,
1931                                    int                  current_space,
1932                                    MetaWorkspaceLayout *layout)
1933 {
1934   int rows, cols;
1935   int grid_area;
1936   int *grid;
1937   int i, r, c;
1938   int current_row, current_col;
1939 
1940   rows = screen->rows_of_workspaces;
1941   cols = screen->columns_of_workspaces;
1942   if (rows <= 0 && cols <= 0)
1943     cols = num_workspaces;
1944 
1945   if (rows <= 0)
1946     rows = num_workspaces / cols + ((num_workspaces % cols) > 0 ? 1 : 0);
1947   if (cols <= 0)
1948     cols = num_workspaces / rows + ((num_workspaces % rows) > 0 ? 1 : 0);
1949 
1950   /* paranoia */
1951   if (rows < 1)
1952     rows = 1;
1953   if (cols < 1)
1954     cols = 1;
1955 
1956   g_assert (rows != 0 && cols != 0);
1957 
1958   grid_area = rows * cols;
1959 
1960   meta_verbose ("Getting layout rows = %d cols = %d current = %d "
1961                 "num_spaces = %d vertical = %s corner = %s\n",
1962                 rows, cols, current_space, num_workspaces,
1963                 screen->vertical_workspaces ? "(true)" : "(false)",
1964                 meta_screen_corner_to_string (screen->starting_corner));
1965 
1966   /* ok, we want to setup the distances in the workspace array to go
1967    * in each direction. Remember, there are many ways that a workspace
1968    * array can be setup.
1969    * see http://www.freedesktop.org/standards/wm-spec/1.2/html/x109.html
1970    * and look at the _NET_DESKTOP_LAYOUT section for details.
1971    * For instance:
1972    */
1973   /* starting_corner = META_SCREEN_TOPLEFT
1974    *  vertical_workspaces = 0                 vertical_workspaces=1
1975    *       1234                                    1357
1976    *       5678                                    2468
1977    *
1978    * starting_corner = META_SCREEN_TOPRIGHT
1979    *  vertical_workspaces = 0                 vertical_workspaces=1
1980    *       4321                                    7531
1981    *       8765                                    8642
1982    *
1983    * starting_corner = META_SCREEN_BOTTOMLEFT
1984    *  vertical_workspaces = 0                 vertical_workspaces=1
1985    *       5678                                    2468
1986    *       1234                                    1357
1987    *
1988    * starting_corner = META_SCREEN_BOTTOMRIGHT
1989    *  vertical_workspaces = 0                 vertical_workspaces=1
1990    *       8765                                    8642
1991    *       4321                                    7531
1992    *
1993    */
1994   /* keep in mind that we could have a ragged layout, e.g. the "8"
1995    * in the above grids could be missing
1996    */
1997 
1998 
1999   grid = g_new (int, grid_area);
2000 
2001   current_row = -1;
2002   current_col = -1;
2003   i = 0;
2004 
2005   switch (screen->starting_corner)
2006     {
2007     case META_SCREEN_TOPLEFT:
2008       if (screen->vertical_workspaces)
2009         {
2010           c = 0;
2011           while (c < cols)
2012             {
2013               r = 0;
2014               while (r < rows)
2015                 {
2016                   grid[r*cols+c] = i;
2017                   ++i;
2018                   ++r;
2019                 }
2020               ++c;
2021             }
2022         }
2023       else
2024         {
2025           r = 0;
2026           while (r < rows)
2027             {
2028               c = 0;
2029               while (c < cols)
2030                 {
2031                   grid[r*cols+c] = i;
2032                   ++i;
2033                   ++c;
2034                 }
2035               ++r;
2036             }
2037         }
2038       break;
2039     case META_SCREEN_TOPRIGHT:
2040       if (screen->vertical_workspaces)
2041         {
2042           c = cols - 1;
2043           while (c >= 0)
2044             {
2045               r = 0;
2046               while (r < rows)
2047                 {
2048                   grid[r*cols+c] = i;
2049                   ++i;
2050                   ++r;
2051                 }
2052               --c;
2053             }
2054         }
2055       else
2056         {
2057           r = 0;
2058           while (r < rows)
2059             {
2060               c = cols - 1;
2061               while (c >= 0)
2062                 {
2063                   grid[r*cols+c] = i;
2064                   ++i;
2065                   --c;
2066                 }
2067               ++r;
2068             }
2069         }
2070       break;
2071     case META_SCREEN_BOTTOMLEFT:
2072       if (screen->vertical_workspaces)
2073         {
2074           c = 0;
2075           while (c < cols)
2076             {
2077               r = rows - 1;
2078               while (r >= 0)
2079                 {
2080                   grid[r*cols+c] = i;
2081                   ++i;
2082                   --r;
2083                 }
2084               ++c;
2085             }
2086         }
2087       else
2088         {
2089           r = rows - 1;
2090           while (r >= 0)
2091             {
2092               c = 0;
2093               while (c < cols)
2094                 {
2095                   grid[r*cols+c] = i;
2096                   ++i;
2097                   ++c;
2098                 }
2099               --r;
2100             }
2101         }
2102       break;
2103     case META_SCREEN_BOTTOMRIGHT:
2104       if (screen->vertical_workspaces)
2105         {
2106           c = cols - 1;
2107           while (c >= 0)
2108             {
2109               r = rows - 1;
2110               while (r >= 0)
2111                 {
2112                   grid[r*cols+c] = i;
2113                   ++i;
2114                   --r;
2115                 }
2116               --c;
2117             }
2118         }
2119       else
2120         {
2121           r = rows - 1;
2122           while (r >= 0)
2123             {
2124               c = cols - 1;
2125               while (c >= 0)
2126                 {
2127                   grid[r*cols+c] = i;
2128                   ++i;
2129                   --c;
2130                 }
2131               --r;
2132             }
2133         }
2134       break;
2135     default:
2136       break;
2137     }
2138 
2139   if (i != grid_area)
2140     g_error ("did not fill in the whole workspace grid in %s (%d filled)",
2141              G_STRFUNC, i);
2142 
2143   current_row = 0;
2144   current_col = 0;
2145   r = 0;
2146   while (r < rows)
2147     {
2148       c = 0;
2149       while (c < cols)
2150         {
2151           if (grid[r*cols+c] == current_space)
2152             {
2153               current_row = r;
2154               current_col = c;
2155             }
2156           else if (grid[r*cols+c] >= num_workspaces)
2157             {
2158               /* flag nonexistent spaces with -1 */
2159               grid[r*cols+c] = -1;
2160             }
2161           ++c;
2162         }
2163       ++r;
2164     }
2165 
2166   layout->rows = rows;
2167   layout->cols = cols;
2168   layout->grid = grid;
2169   layout->grid_area = grid_area;
2170   layout->current_row = current_row;
2171   layout->current_col = current_col;
2172 
2173   if (meta_check_debug_flags (META_DEBUG_VERBOSE))
2174     {
2175       r = 0;
2176       while (r < layout->rows)
2177         {
2178           meta_verbose (" ");
2179           meta_push_no_msg_prefix ();
2180           c = 0;
2181           while (c < layout->cols)
2182             {
2183               if (r == layout->current_row &&
2184                   c == layout->current_col)
2185                 meta_verbose ("*%2d ", layout->grid[r*layout->cols+c]);
2186               else
2187                 meta_verbose ("%3d ", layout->grid[r*layout->cols+c]);
2188               ++c;
2189             }
2190           meta_verbose ("\n");
2191           meta_pop_no_msg_prefix ();
2192           ++r;
2193         }
2194     }
2195 }
2196 
2197 void
meta_screen_free_workspace_layout(MetaWorkspaceLayout * layout)2198 meta_screen_free_workspace_layout (MetaWorkspaceLayout *layout)
2199 {
2200   g_free (layout->grid);
2201 }
2202 
2203 static void
meta_screen_resize_func(MetaScreen * screen,MetaWindow * window,void * user_data)2204 meta_screen_resize_func (MetaScreen *screen,
2205                          MetaWindow *window,
2206                          void       *user_data)
2207 {
2208   if (window->struts)
2209     {
2210       meta_window_update_struts (window);
2211     }
2212   meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
2213 
2214   meta_window_recalc_features (window);
2215 }
2216 
2217 void
meta_screen_resize(MetaScreen * screen,int width,int height)2218 meta_screen_resize (MetaScreen *screen,
2219                     int         width,
2220                     int         height)
2221 {
2222   screen->rect.width = width;
2223   screen->rect.height = height;
2224 
2225   reload_monitor_infos (screen);
2226   set_desktop_geometry_hint (screen);
2227 
2228   meta_compositor_sync_screen_size (screen->display->compositor);
2229 
2230   /* Queue a resize on all the windows */
2231   meta_screen_foreach_window (screen, meta_screen_resize_func, 0);
2232 }
2233 
2234 void
meta_screen_update_showing_desktop_hint(MetaScreen * screen)2235 meta_screen_update_showing_desktop_hint (MetaScreen *screen)
2236 {
2237   unsigned long data[1];
2238 
2239   data[0] = screen->active_workspace->showing_desktop ? 1 : 0;
2240 
2241   meta_error_trap_push (screen->display);
2242   XChangeProperty (screen->display->xdisplay, screen->xroot,
2243                    screen->display->atom__NET_SHOWING_DESKTOP,
2244                    XA_CARDINAL,
2245                    32, PropModeReplace, (guchar*) data, 1);
2246   meta_error_trap_pop (screen->display);
2247 }
2248 
2249 static void
queue_windows_showing(MetaScreen * screen)2250 queue_windows_showing (MetaScreen *screen)
2251 {
2252   GSList *windows;
2253   GSList *tmp;
2254 
2255   /* Must operate on all windows on display instead of just on the
2256    * active_workspace's window list, because the active_workspace's
2257    * window list may not contain the on_all_workspace windows.
2258    */
2259   windows = meta_display_list_windows (screen->display, META_LIST_DEFAULT);
2260 
2261   tmp = windows;
2262   while (tmp != NULL)
2263     {
2264       MetaWindow *w = tmp->data;
2265 
2266       if (w->screen == screen)
2267         meta_window_queue (w, META_QUEUE_CALC_SHOWING);
2268 
2269       tmp = tmp->next;
2270     }
2271 
2272   g_slist_free (windows);
2273 }
2274 
2275 void
meta_screen_minimize_all_on_active_workspace_except(MetaScreen * screen,MetaWindow * keep)2276 meta_screen_minimize_all_on_active_workspace_except (MetaScreen *screen,
2277                                                      MetaWindow *keep)
2278 {
2279   GList *windows;
2280   GList *tmp;
2281 
2282   windows = screen->active_workspace->windows;
2283 
2284   tmp = windows;
2285   while (tmp != NULL)
2286     {
2287       MetaWindow *w = tmp->data;
2288 
2289       if (w->screen == screen && w->has_minimize_func && w != keep)
2290         meta_window_minimize (w);
2291 
2292       tmp = tmp->next;
2293     }
2294 }
2295 
2296 void
meta_screen_show_desktop(MetaScreen * screen,guint32 timestamp)2297 meta_screen_show_desktop (MetaScreen *screen,
2298                           guint32     timestamp)
2299 {
2300   GList *windows;
2301 
2302   if (screen->active_workspace->showing_desktop)
2303     return;
2304 
2305   screen->active_workspace->showing_desktop = TRUE;
2306 
2307   queue_windows_showing (screen);
2308 
2309   /* Focus the most recently used META_WINDOW_DESKTOP window, if there is one;
2310    * see bug 159257.
2311    */
2312   windows = screen->active_workspace->mru_list;
2313   while (windows != NULL)
2314     {
2315       MetaWindow *w = windows->data;
2316 
2317       if (w->screen == screen  &&
2318           w->type == META_WINDOW_DESKTOP)
2319         {
2320           meta_window_focus (w, timestamp);
2321           break;
2322         }
2323 
2324       windows = windows->next;
2325     }
2326 
2327 
2328   meta_screen_update_showing_desktop_hint (screen);
2329 }
2330 
2331 void
meta_screen_unshow_desktop(MetaScreen * screen)2332 meta_screen_unshow_desktop (MetaScreen *screen)
2333 {
2334   if (!screen->active_workspace->showing_desktop)
2335     return;
2336 
2337   screen->active_workspace->showing_desktop = FALSE;
2338 
2339   queue_windows_showing (screen);
2340 
2341   meta_screen_update_showing_desktop_hint (screen);
2342 }
2343 
2344 
2345 #ifdef HAVE_STARTUP_NOTIFICATION
2346 static gboolean startup_sequence_timeout (void *data);
2347 
2348 static void
update_startup_feedback(MetaScreen * screen)2349 update_startup_feedback (MetaScreen *screen)
2350 {
2351   if (screen->startup_sequences != NULL)
2352     {
2353       meta_topic (META_DEBUG_STARTUP,
2354                   "Setting busy cursor\n");
2355       meta_screen_set_cursor (screen, META_CURSOR_BUSY);
2356     }
2357   else
2358     {
2359       meta_topic (META_DEBUG_STARTUP,
2360                   "Setting default cursor\n");
2361       meta_screen_set_cursor (screen, META_CURSOR_DEFAULT);
2362     }
2363 }
2364 
2365 static void
add_sequence(MetaScreen * screen,SnStartupSequence * sequence)2366 add_sequence (MetaScreen        *screen,
2367               SnStartupSequence *sequence)
2368 {
2369   meta_topic (META_DEBUG_STARTUP,
2370               "Adding sequence %s\n",
2371               sn_startup_sequence_get_id (sequence));
2372   sn_startup_sequence_ref (sequence);
2373   screen->startup_sequences = g_slist_prepend (screen->startup_sequences,
2374                                                sequence);
2375 
2376   /* our timeout just polls every second, instead of bothering
2377    * to compute exactly when we may next time out
2378    */
2379   if (screen->startup_sequence_timeout == 0)
2380     screen->startup_sequence_timeout = g_timeout_add (1000,
2381                                                       startup_sequence_timeout,
2382                                                       screen);
2383 
2384   update_startup_feedback (screen);
2385 }
2386 
2387 static void
remove_sequence(MetaScreen * screen,SnStartupSequence * sequence)2388 remove_sequence (MetaScreen        *screen,
2389                  SnStartupSequence *sequence)
2390 {
2391   meta_topic (META_DEBUG_STARTUP,
2392               "Removing sequence %s\n",
2393               sn_startup_sequence_get_id (sequence));
2394 
2395   screen->startup_sequences = g_slist_remove (screen->startup_sequences,
2396                                               sequence);
2397   sn_startup_sequence_unref (sequence);
2398 
2399   if (screen->startup_sequences == NULL &&
2400       screen->startup_sequence_timeout != 0)
2401     {
2402       g_source_remove (screen->startup_sequence_timeout);
2403       screen->startup_sequence_timeout = 0;
2404     }
2405 
2406   update_startup_feedback (screen);
2407 }
2408 
2409 typedef struct
2410 {
2411   GSList *list;
2412   gint64  now;
2413 } CollectTimedOutData;
2414 
2415 /* This should be fairly long, as it should never be required unless
2416  * apps or .desktop files are buggy, and it's confusing if
2417  * OpenOffice or whatever seems to stop launching - people
2418  * might decide they need to launch it again.
2419  */
2420 #define STARTUP_TIMEOUT 15000
2421 
2422 static void
collect_timed_out_foreach(void * element,void * data)2423 collect_timed_out_foreach (void *element,
2424                            void *data)
2425 {
2426   CollectTimedOutData *ctod = data;
2427   SnStartupSequence *sequence = element;
2428   long tv_sec, tv_usec;
2429   double elapsed;
2430 
2431   sn_startup_sequence_get_last_active_time (sequence, &tv_sec, &tv_usec);
2432 
2433   elapsed = (ctod->now - (tv_sec * G_USEC_PER_SEC + tv_usec)) / 1000.0;
2434 
2435   meta_topic (META_DEBUG_STARTUP,
2436               "Sequence used %g seconds vs. %g max: %s\n",
2437               elapsed, (double) STARTUP_TIMEOUT,
2438               sn_startup_sequence_get_id (sequence));
2439 
2440   if (elapsed > STARTUP_TIMEOUT)
2441     ctod->list = g_slist_prepend (ctod->list, sequence);
2442 }
2443 
2444 static gboolean
startup_sequence_timeout(void * data)2445 startup_sequence_timeout (void *data)
2446 {
2447   MetaScreen *screen = data;
2448   CollectTimedOutData ctod;
2449   GSList *tmp;
2450 
2451   ctod.list = NULL;
2452   ctod.now = g_get_real_time ();
2453   g_slist_foreach (screen->startup_sequences,
2454                    collect_timed_out_foreach,
2455                    &ctod);
2456 
2457   tmp = ctod.list;
2458   while (tmp != NULL)
2459     {
2460       SnStartupSequence *sequence = tmp->data;
2461 
2462       meta_topic (META_DEBUG_STARTUP,
2463                   "Timed out sequence %s\n",
2464                   sn_startup_sequence_get_id (sequence));
2465 
2466       sn_startup_sequence_complete (sequence);
2467 
2468       tmp = tmp->next;
2469     }
2470 
2471   g_slist_free (ctod.list);
2472 
2473   if (screen->startup_sequences != NULL)
2474     {
2475       return TRUE;
2476     }
2477   else
2478     {
2479       /* remove */
2480       screen->startup_sequence_timeout = 0;
2481       return FALSE;
2482     }
2483 }
2484 
2485 static void
meta_screen_sn_event(SnMonitorEvent * event,void * user_data)2486 meta_screen_sn_event (SnMonitorEvent *event,
2487                       void           *user_data)
2488 {
2489   MetaScreen *screen;
2490   SnStartupSequence *sequence;
2491 
2492   screen = user_data;
2493 
2494   sequence = sn_monitor_event_get_startup_sequence (event);
2495 
2496   switch (sn_monitor_event_get_type (event))
2497     {
2498     case SN_MONITOR_EVENT_INITIATED:
2499       {
2500         const char *wmclass;
2501 
2502         wmclass = sn_startup_sequence_get_wmclass (sequence);
2503 
2504         meta_topic (META_DEBUG_STARTUP,
2505                     "Received startup initiated for %s wmclass %s\n",
2506                     sn_startup_sequence_get_id (sequence),
2507                     wmclass ? wmclass : "(unset)");
2508         add_sequence (screen, sequence);
2509       }
2510       break;
2511 
2512     case SN_MONITOR_EVENT_COMPLETED:
2513       {
2514         meta_topic (META_DEBUG_STARTUP,
2515                     "Received startup completed for %s\n",
2516                     sn_startup_sequence_get_id (sequence));
2517         remove_sequence (screen,
2518                          sn_monitor_event_get_startup_sequence (event));
2519       }
2520       break;
2521 
2522     case SN_MONITOR_EVENT_CHANGED:
2523       meta_topic (META_DEBUG_STARTUP,
2524                   "Received startup changed for %s\n",
2525                   sn_startup_sequence_get_id (sequence));
2526       break;
2527 
2528     case SN_MONITOR_EVENT_CANCELED:
2529       meta_topic (META_DEBUG_STARTUP,
2530                   "Received startup canceled for %s\n",
2531                   sn_startup_sequence_get_id (sequence));
2532       break;
2533 
2534     default:
2535       break;
2536     }
2537 }
2538 #endif
2539 
2540 /* Sets the initial_timestamp and initial_workspace properties
2541  * of a window according to information given us by the
2542  * startup-notification library.
2543  *
2544  * Returns TRUE if startup properties have been applied, and
2545  * FALSE if they have not (for example, if they had already
2546  * been applied.)
2547  */
2548 gboolean
meta_screen_apply_startup_properties(MetaScreen * screen,MetaWindow * window)2549 meta_screen_apply_startup_properties (MetaScreen *screen,
2550                                       MetaWindow *window)
2551 {
2552 #ifdef HAVE_STARTUP_NOTIFICATION
2553   const char *startup_id;
2554   GSList *tmp;
2555   SnStartupSequence *sequence;
2556 
2557   /* Does the window have a startup ID stored? */
2558   startup_id = meta_window_get_startup_id (window);
2559 
2560   meta_topic (META_DEBUG_STARTUP,
2561               "Applying startup props to %s id \"%s\"\n",
2562               window->desc,
2563               startup_id ? startup_id : "(none)");
2564 
2565   sequence = NULL;
2566   if (startup_id == NULL)
2567     {
2568       /* No startup ID stored for the window. Let's ask the
2569        * startup-notification library whether there's anything
2570        * stored for the resource name or resource class hints.
2571        */
2572       tmp = screen->startup_sequences;
2573       while (tmp != NULL)
2574         {
2575           const char *wmclass;
2576 
2577           wmclass = sn_startup_sequence_get_wmclass (tmp->data);
2578 
2579           if (wmclass != NULL &&
2580               ((window->res_class &&
2581                 strcmp (wmclass, window->res_class) == 0) ||
2582                (window->res_name &&
2583                 strcmp (wmclass, window->res_name) == 0)))
2584             {
2585               sequence = tmp->data;
2586 
2587               g_assert (window->startup_id == NULL);
2588               window->startup_id = g_strdup (sn_startup_sequence_get_id (sequence));
2589               startup_id = window->startup_id;
2590 
2591               meta_topic (META_DEBUG_STARTUP,
2592                           "Ending legacy sequence %s due to window %s\n",
2593                           sn_startup_sequence_get_id (sequence),
2594                           window->desc);
2595 
2596               sn_startup_sequence_complete (sequence);
2597               break;
2598             }
2599 
2600           tmp = tmp->next;
2601         }
2602     }
2603 
2604   /* Still no startup ID? Bail. */
2605   if (startup_id == NULL)
2606     return FALSE;
2607 
2608   /* We might get this far and not know the sequence ID (if the window
2609    * already had a startup ID stored), so let's look for one if we don't
2610    * already know it.
2611    */
2612   if (sequence == NULL)
2613     {
2614       tmp = screen->startup_sequences;
2615       while (tmp != NULL)
2616         {
2617           const char *id;
2618 
2619           id = sn_startup_sequence_get_id (tmp->data);
2620 
2621           if (strcmp (id, startup_id) == 0)
2622             {
2623               sequence = tmp->data;
2624               break;
2625             }
2626 
2627           tmp = tmp->next;
2628         }
2629     }
2630 
2631   if (sequence != NULL)
2632     {
2633       gboolean changed_something = FALSE;
2634 
2635       meta_topic (META_DEBUG_STARTUP,
2636                   "Found startup sequence for window %s ID \"%s\"\n",
2637                   window->desc, startup_id);
2638 
2639       if (!window->initial_workspace_set)
2640         {
2641           int space = sn_startup_sequence_get_workspace (sequence);
2642           if (space >= 0)
2643             {
2644               meta_topic (META_DEBUG_STARTUP,
2645                           "Setting initial window workspace to %d based on startup info\n",
2646                           space);
2647 
2648               window->initial_workspace_set = TRUE;
2649               window->initial_workspace = space;
2650               changed_something = TRUE;
2651             }
2652         }
2653 
2654       if (!window->initial_timestamp_set)
2655         {
2656           guint32 timestamp = sn_startup_sequence_get_timestamp (sequence);
2657           meta_topic (META_DEBUG_STARTUP,
2658                       "Setting initial window timestamp to %u based on startup info\n",
2659                       timestamp);
2660 
2661           window->initial_timestamp_set = TRUE;
2662           window->initial_timestamp = timestamp;
2663           changed_something = TRUE;
2664         }
2665 
2666       return changed_something;
2667     }
2668   else
2669     {
2670       meta_topic (META_DEBUG_STARTUP,
2671                   "Did not find startup sequence for window %s ID \"%s\"\n",
2672                   window->desc, startup_id);
2673     }
2674 
2675 #endif /* HAVE_STARTUP_NOTIFICATION */
2676 
2677   return FALSE;
2678 }
2679 
2680 int
meta_screen_get_screen_number(MetaScreen * screen)2681 meta_screen_get_screen_number (MetaScreen *screen)
2682 {
2683   return screen->number;
2684 }
2685 
2686 MetaDisplay *
meta_screen_get_display(MetaScreen * screen)2687 meta_screen_get_display (MetaScreen *screen)
2688 {
2689   return screen->display;
2690 }
2691 
2692 Window
meta_screen_get_xroot(MetaScreen * screen)2693 meta_screen_get_xroot (MetaScreen *screen)
2694 {
2695   return screen->xroot;
2696 }
2697 
2698 void
meta_screen_get_size(MetaScreen * screen,int * width,int * height)2699 meta_screen_get_size (MetaScreen *screen,
2700                       int        *width,
2701                       int        *height)
2702 {
2703   *width = screen->rect.width;
2704   *height = screen->rect.height;
2705 }
2706