1 /*      $Id$
2 
3         This program is free software; you can redistribute it and/or modify
4         it under the terms of the GNU General Public License as published by
5         the Free Software Foundation; either version 2, or (at your option)
6         any later version.
7 
8         This program is distributed in the hope that it will be useful,
9         but WITHOUT ANY WARRANTY; without even the implied warranty of
10         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11         GNU General Public License for more details.
12 
13         You should have received a copy of the GNU General Public License
14         along with this program; if not, write to the Free Software
15         Foundation, Inc., Inc., 51 Franklin Street, Fifth Floor, Boston,
16         MA 02110-1301, USA.
17 
18 
19         xfwm4    - (c) 2002-2011 Olivier Fourdan
20 
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/extensions/shape.h>
30 #include <X11/extensions/Xinerama.h>
31 #include <glib.h>
32 #include <gdk/gdk.h>
33 #include <gdk/gdkx.h>
34 #include <gtk/gtk.h>
35 #include <pango/pango.h>
36 #include <libxfce4util/libxfce4util.h>
37 
38 #ifdef HAVE_RENDER
39 #include <X11/extensions/Xrender.h>
40 #endif
41 
42 #ifdef HAVE_LIBSTARTUP_NOTIFICATION
43 #define SN_API_NOT_YET_FROZEN
44 #include <libsn/sn.h>
45 #endif
46 
47 #include <common/xfwm-common.h>
48 
49 #include "display.h"
50 #include "screen.h"
51 #include "misc.h"
52 #include "mywindow.h"
53 #include "compositor.h"
54 #include "ui_style.h"
55 
56 #ifndef WM_EXITING_TIMEOUT
57 #define WM_EXITING_TIMEOUT 15 /*seconds */
58 #endif
59 
60 gboolean
myScreenCheckWMAtom(ScreenInfo * screen_info,Atom atom)61 myScreenCheckWMAtom (ScreenInfo *screen_info, Atom atom)
62 {
63     gchar selection[32];
64     Atom wm_sn_atom;
65 
66     TRACE ("atom %lu", atom);
67 
68     g_snprintf (selection, sizeof (selection), "WM_S%d", screen_info->screen);
69     wm_sn_atom = XInternAtom (myScreenGetXDisplay (screen_info), selection, FALSE);
70 
71     return (atom == wm_sn_atom);
72 }
73 
74 static gboolean
myScreenSetWMAtom(ScreenInfo * screen_info,gboolean replace_wm)75 myScreenSetWMAtom (ScreenInfo *screen_info, gboolean replace_wm)
76 {
77     const char *wm_name;
78     gchar selection[32];
79     gchar *display_name;
80     gulong wait, timeout;
81     DisplayInfo *display_info;
82     XSetWindowAttributes attrs;
83     Window current_wm;
84     XEvent event;
85     Atom wm_sn_atom;
86 
87 
88     g_return_val_if_fail (screen_info, FALSE);
89     g_return_val_if_fail (screen_info->display_info, FALSE);
90 
91     TRACE ("replace %i", replace_wm);
92 
93     display_info = screen_info->display_info;
94     g_snprintf (selection, sizeof (selection), "WM_S%d", screen_info->screen);
95     wm_sn_atom = XInternAtom (display_info->dpy, selection, FALSE);
96     display_name = xfwm_make_display_name (screen_info->gscr);
97     wm_name = gdk_x11_screen_get_window_manager_name (screen_info->gscr);
98 
99     XSync (display_info->dpy, FALSE);
100     current_wm = XGetSelectionOwner (display_info->dpy, wm_sn_atom);
101     if (current_wm)
102     {
103         if (!replace_wm)
104         {
105             g_print ("Another Window Manager (%s) is already running on screen %s\n", wm_name, display_name);
106             g_print ("To replace the current window manager, try \"--replace\"\n");
107             g_free (display_name);
108 
109             return FALSE;
110         }
111         myDisplayErrorTrapPush (display_info);
112         attrs.event_mask = StructureNotifyMask;
113         XChangeWindowAttributes (display_info->dpy, current_wm, CWEventMask, &attrs);
114         if (myDisplayErrorTrapPop (display_info))
115         {
116             current_wm = None;
117         }
118     }
119 
120     if (!setXAtomManagerOwner (display_info, wm_sn_atom, screen_info->xroot, screen_info->xfwm4_win))
121     {
122         g_warning ("Cannot acquire window manager selection on screen %s", display_name);
123         g_free (display_name);
124 
125         return FALSE;
126     }
127 
128     /* Waiting for previous window manager to exit */
129     if (current_wm)
130     {
131         g_print ("Waiting for current window manager (%s) on screen %s to exit:", wm_name, display_name);
132         wait = 0;
133         timeout = WM_EXITING_TIMEOUT * G_USEC_PER_SEC;
134         while (wait < timeout)
135         {
136             if (XCheckWindowEvent (display_info->dpy, current_wm, StructureNotifyMask, &event) && (event.type == DestroyNotify))
137             {
138                 break;
139             }
140             g_usleep(G_USEC_PER_SEC / 10);
141             wait += G_USEC_PER_SEC / 10;
142             if (wait % G_USEC_PER_SEC == 0)
143             {
144               g_print (".");
145             }
146         }
147 
148         if (wait >= timeout)
149         {
150             g_print(" Failed\n");
151             g_warning("Previous window manager (%s) on screen %s is not exiting", wm_name, display_name);
152             g_free (display_name);
153 
154             return FALSE;
155         }
156         g_print(" Done\n");
157     }
158     g_free (display_name);
159 
160     return TRUE;
161 }
162 
163 ScreenInfo *
myScreenInit(DisplayInfo * display_info,GdkScreen * gscr,unsigned long event_mask,gboolean replace_wm)164 myScreenInit (DisplayInfo *display_info, GdkScreen *gscr, unsigned long event_mask, gboolean replace_wm)
165 {
166 #ifdef ENABLE_KDE_SYSTRAY_PROXY
167     gchar selection[32];
168 #endif
169     ScreenInfo *screen_info;
170     GdkWindow *event_win;
171     PangoLayout *layout;
172     long desktop_visible;
173     int i, j;
174 
175     g_return_val_if_fail (display_info, NULL);
176     g_return_val_if_fail (GDK_IS_SCREEN (gscr), NULL);
177     TRACE ("entering");
178 
179     screen_info = g_new0 (ScreenInfo, 1);
180     screen_info->params = g_new0 (XfwmParams, 1);
181 
182     screen_info->display_info = display_info;
183     screen_info->gscr = gscr;
184     desktop_visible = 0;
185     layout = NULL;
186 
187     /* Create a GTK window so that we are just like any other GTK application */
188     screen_info->gtk_win = gtk_window_new (GTK_WINDOW_POPUP);
189     gtk_window_set_screen (GTK_WINDOW (screen_info->gtk_win), gscr);
190     gtk_window_resize (GTK_WINDOW (screen_info->gtk_win), 5, 5);
191     gtk_window_move (GTK_WINDOW (screen_info->gtk_win), -1000, -1000);
192     gtk_widget_set_name (screen_info->gtk_win, "xfwm");
193     gtk_widget_show_now (screen_info->gtk_win);
194 
195     /*
196      * The first time the first Gtk application on a display uses pango,
197      * pango grabs the XServer while it creates the font cache window.
198      * Therefore, force the cache window to be created now instead of
199      * trying to do it while we have another grab and deadlocking the server.
200      */
201     layout = gtk_widget_create_pango_layout (screen_info->gtk_win, "-");
202     pango_layout_get_pixel_extents (layout, NULL, NULL);
203     g_object_unref (G_OBJECT (layout));
204 
205     screen_info->xscreen = gdk_x11_screen_get_xscreen (gscr);
206     screen_info->xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (gscr));
207     screen_info->screen = gdk_x11_screen_get_screen_number (gscr);
208     screen_info->cmap = DefaultColormapOfScreen (screen_info->xscreen);
209     screen_info->depth = DefaultDepth (display_info->dpy, screen_info->screen);
210     screen_info->visual = DefaultVisual (display_info->dpy, screen_info->screen);
211     screen_info->shape_win = (Window) None;
212     myScreenComputeSize (screen_info);
213 
214     screen_info->xfwm4_win = gdk_x11_window_get_xid (gtk_widget_get_window (screen_info->gtk_win));
215     if (!myScreenSetWMAtom (screen_info, replace_wm))
216     {
217         gtk_widget_destroy (screen_info->gtk_win);
218         g_free (screen_info);
219         return NULL;
220     }
221 
222     event_win = eventFilterAddWin (gscr, display_info->devices, event_mask);
223     if (!event_win)
224     {
225         gtk_widget_destroy (screen_info->gtk_win);
226         g_free (screen_info);
227         return NULL;
228     }
229     gdk_window_set_user_data (event_win, screen_info->gtk_win);
230 
231     screen_info->current_ws = 0;
232     screen_info->previous_ws = 0;
233     screen_info->current_ws = 0;
234     screen_info->previous_ws = 0;
235 
236     screen_info->margins[STRUTS_TOP] = screen_info->gnome_margins[STRUTS_TOP] = 0;
237     screen_info->margins[STRUTS_LEFT] = screen_info->gnome_margins[STRUTS_LEFT] = 0;
238     screen_info->margins[STRUTS_RIGHT] = screen_info->gnome_margins[STRUTS_RIGHT] = 0;
239     screen_info->margins[STRUTS_BOTTOM] = screen_info->gnome_margins[STRUTS_BOTTOM] = 0;
240 
241     screen_info->workspace_count = 0;
242     screen_info->workspace_names = NULL;
243     screen_info->workspace_names_items = 0;
244 
245     screen_info->windows_stack = NULL;
246     screen_info->last_raise = NULL;
247     screen_info->windows = NULL;
248     screen_info->clients = NULL;
249     screen_info->client_count = 0;
250     screen_info->client_serial = 0L;
251     screen_info->button_handler_id = 0L;
252 
253     screen_info->key_grabs = 0;
254     screen_info->pointer_grabs = 0;
255 
256     getHint (display_info, screen_info->xroot, NET_SHOWING_DESKTOP, &desktop_visible);
257     screen_info->show_desktop = (desktop_visible != 0);
258 
259     /* Create the side windows to detect edge movement */
260 
261     /*left*/
262     xfwmWindowTemp (screen_info,
263                     NULL, 0,
264                     screen_info->xroot,
265                     &screen_info->sidewalk[0],
266                     0, 0,
267                     1, screen_info->height,
268                     EnterWindowMask,
269                     TRUE);
270 
271     /*right*/
272     xfwmWindowTemp (screen_info,
273                     NULL, 0,
274                     screen_info->xroot,
275                     &screen_info->sidewalk[1],
276                     screen_info->width - 1, 0,
277                     1, screen_info->height,
278                     EnterWindowMask,
279                     TRUE);
280 
281     /*top*/
282     xfwmWindowTemp (screen_info,
283                     NULL, 0,
284                     screen_info->xroot,
285                     &screen_info->sidewalk[2],
286                     0, 0,
287                     screen_info->width, 1,
288                     EnterWindowMask,
289                     TRUE);
290 
291     /*bottom*/
292     xfwmWindowTemp (screen_info,
293                     NULL, 0,
294                     screen_info->xroot,
295                     &screen_info->sidewalk[3],
296                     0, screen_info->height - 1,
297                     screen_info->width, 1,
298                     EnterWindowMask,
299                     TRUE);
300 
301 #ifdef ENABLE_KDE_SYSTRAY_PROXY
302     g_snprintf (selection, sizeof (selection), "_NET_SYSTEM_TRAY_S%d", screen_info->screen);
303     screen_info->net_system_tray_selection = XInternAtom (display_info->dpy, selection, FALSE);
304     screen_info->systray = getSystrayWindow (display_info, screen_info->net_system_tray_selection);
305 #endif
306 
307     screen_info->font_desc = NULL;
308     screen_info->pango_attr_list = NULL;
309     screen_info->box_gc = None;
310 
311     for (i = 0; i < SIDE_COUNT; i++)
312     {
313         xfwmPixmapInit (screen_info, &screen_info->sides[i][ACTIVE]);
314         xfwmPixmapInit (screen_info, &screen_info->sides[i][INACTIVE]);
315     }
316     for (i = 0; i < CORNER_COUNT; i++)
317     {
318         xfwmPixmapInit (screen_info, &screen_info->corners[i][ACTIVE]);
319         xfwmPixmapInit (screen_info, &screen_info->corners[i][INACTIVE]);
320     }
321     for (i = 0; i < BUTTON_COUNT; i++)
322     {
323         for (j = 0; j < STATE_COUNT; j++)
324         {
325             xfwmPixmapInit (screen_info, &screen_info->buttons[i][j]);
326         }
327     }
328     for (i = 0; i < TITLE_COUNT; i++)
329     {
330         xfwmPixmapInit (screen_info, &screen_info->title[i][ACTIVE]);
331         xfwmPixmapInit (screen_info, &screen_info->title[i][INACTIVE]);
332         xfwmPixmapInit (screen_info, &screen_info->top[i][ACTIVE]);
333         xfwmPixmapInit (screen_info, &screen_info->top[i][INACTIVE]);
334     }
335 
336     screen_info->monitors_index = NULL;
337     myScreenInvalidateMonitorCache (screen_info);
338     myScreenRebuildMonitorIndex (screen_info);
339 
340     return (screen_info);
341 }
342 
343 ScreenInfo *
myScreenClose(ScreenInfo * screen_info)344 myScreenClose (ScreenInfo *screen_info)
345 {
346     DisplayInfo *display_info;
347 
348     g_return_val_if_fail (screen_info, NULL);
349     TRACE ("entering");
350 
351     display_info = screen_info->display_info;
352 
353     clientUnframeAll (screen_info);
354     compositorUnmanageScreen (screen_info);
355     closeSettings (screen_info);
356 
357     if (screen_info->workspace_names)
358     {
359         g_strfreev (screen_info->workspace_names);
360     }
361     screen_info->workspace_names = NULL;
362     screen_info->workspace_names_items = 0;
363 
364     if (screen_info->shape_win != None)
365     {
366         XDestroyWindow (display_info->dpy, screen_info->shape_win);
367         screen_info->shape_win = (Window) None;
368     }
369 
370     xfwmWindowDelete (&screen_info->sidewalk[0]);
371     xfwmWindowDelete (&screen_info->sidewalk[1]);
372     xfwmWindowDelete (&screen_info->sidewalk[2]);
373     xfwmWindowDelete (&screen_info->sidewalk[3]);
374     XSetInputFocus (display_info->dpy, screen_info->xroot, RevertToPointerRoot, CurrentTime);
375 
376     g_free (screen_info->params);
377     screen_info->params = NULL;
378 
379     gtk_widget_destroy (screen_info->gtk_win);
380     screen_info->gtk_win = NULL;
381 
382     g_list_free (screen_info->windows_stack);
383     screen_info->windows_stack = NULL;
384 
385     g_list_free (screen_info->windows);
386     screen_info->windows = NULL;
387 
388     if (screen_info->monitors_index)
389     {
390         g_array_free (screen_info->monitors_index, TRUE);
391         screen_info->monitors_index = NULL;
392     }
393 
394     if (screen_info->pango_attr_list)
395     {
396         pango_attr_list_unref (screen_info->pango_attr_list);
397     }
398 
399     return (screen_info);
400 }
401 
402 Display *
myScreenGetXDisplay(ScreenInfo * screen_info)403 myScreenGetXDisplay (ScreenInfo *screen_info)
404 {
405     DisplayInfo *display_info;
406 
407     g_return_val_if_fail (screen_info, NULL);
408     g_return_val_if_fail (screen_info->display_info, NULL);
409 
410     display_info = screen_info->display_info;
411     return display_info->dpy;
412 }
413 
414 GtkWidget *
myScreenGetGtkWidget(ScreenInfo * screen_info)415 myScreenGetGtkWidget (ScreenInfo *screen_info)
416 {
417     g_return_val_if_fail (screen_info, NULL);
418 
419     return screen_info->gtk_win;
420 }
421 
422 GdkWindow *
myScreenGetGdkWindow(ScreenInfo * screen_info)423 myScreenGetGdkWindow (ScreenInfo *screen_info)
424 {
425     g_return_val_if_fail (screen_info, NULL);
426 
427     return gtk_widget_get_window (screen_info->gtk_win);
428 }
429 
430 gboolean
myScreenGrabKeyboard(ScreenInfo * screen_info,guint event_mask,guint32 timestamp)431 myScreenGrabKeyboard (ScreenInfo *screen_info, guint event_mask, guint32 timestamp)
432 {
433     gboolean grab;
434 
435     g_return_val_if_fail (screen_info, FALSE);
436 
437     TRACE ("timestamp %u", (unsigned int) timestamp);
438 
439     grab = TRUE;
440     if (screen_info->key_grabs == 0)
441     {
442         myDisplayErrorTrapPush (screen_info->display_info);
443         grab = xfwm_device_grab (screen_info->display_info->devices,
444                                  &screen_info->display_info->devices->keyboard,
445                                  myScreenGetXDisplay (screen_info), screen_info->xroot,
446                                  TRUE, event_mask, GrabModeAsync, screen_info->xroot,
447                                  None, (Time) timestamp);
448         myDisplayErrorTrapPopIgnored (screen_info->display_info);
449     }
450     screen_info->key_grabs++;
451     TRACE ("global key grabs %i", screen_info->key_grabs);
452 
453     return grab;
454 }
455 
456 gboolean
myScreenGrabPointer(ScreenInfo * screen_info,gboolean owner_events,guint event_mask,Cursor cursor,guint32 timestamp)457 myScreenGrabPointer (ScreenInfo *screen_info, gboolean owner_events,
458                      guint event_mask, Cursor cursor, guint32 timestamp)
459 {
460     gboolean grab;
461 
462     g_return_val_if_fail (screen_info, FALSE);
463     TRACE ("timestamp %u", (unsigned int) timestamp);
464 
465     grab = TRUE;
466     if (screen_info->pointer_grabs == 0)
467     {
468         myDisplayErrorTrapPush (screen_info->display_info);
469         grab = xfwm_device_grab (screen_info->display_info->devices,
470                                  &screen_info->display_info->devices->pointer,
471                                  myScreenGetXDisplay (screen_info), screen_info->xroot,
472                                  owner_events, event_mask, GrabModeAsync, screen_info->xroot,
473                                  cursor, (Time) timestamp);
474         myDisplayErrorTrapPopIgnored (screen_info->display_info);
475     }
476     screen_info->pointer_grabs++;
477     TRACE ("global pointer grabs %i", screen_info->pointer_grabs);
478 
479     return grab;
480 }
481 
482 gboolean
myScreenChangeGrabPointer(ScreenInfo * screen_info,gboolean owner_events,guint event_mask,Cursor cursor,guint32 timestamp)483 myScreenChangeGrabPointer (ScreenInfo *screen_info, gboolean owner_events,
484                            guint event_mask, Cursor cursor, guint32 timestamp)
485 {
486     gboolean grab;
487 
488     g_return_val_if_fail (screen_info, FALSE);
489     TRACE ("timestamp %u", (unsigned int) timestamp);
490 
491     grab = FALSE;
492     if (screen_info->pointer_grabs > 0)
493     {
494         myDisplayErrorTrapPush (screen_info->display_info);
495         if (screen_info->display_info->devices->pointer.xi2_device == None)
496         {
497             grab = (XChangeActivePointerGrab (myScreenGetXDisplay (screen_info),
498                                               event_mask, cursor, (Time) timestamp) == GrabSuccess);
499         }
500         else
501         {
502             grab = xfwm_device_grab (screen_info->display_info->devices,
503                                      &screen_info->display_info->devices->pointer,
504                                      myScreenGetXDisplay (screen_info), screen_info->xroot,
505                                      owner_events, event_mask, GrabModeAsync, screen_info->xroot,
506                                      cursor, (Time) timestamp);
507         }
508         myDisplayErrorTrapPopIgnored (screen_info->display_info);
509     }
510 
511     return grab;
512 }
513 
514 unsigned int
myScreenUngrabKeyboard(ScreenInfo * screen_info,guint32 timestamp)515 myScreenUngrabKeyboard (ScreenInfo *screen_info, guint32 timestamp)
516 {
517     g_return_val_if_fail (screen_info, 0);
518     TRACE ("timestamp %u", (unsigned int) timestamp);
519 
520     screen_info->key_grabs--;
521     if (screen_info->key_grabs < 0)
522     {
523         screen_info->key_grabs = 0;
524     }
525     if (screen_info->key_grabs == 0)
526     {
527         myDisplayErrorTrapPush (screen_info->display_info);
528         xfwm_device_ungrab (screen_info->display_info->devices,
529                             &screen_info->display_info->devices->keyboard,
530                             myScreenGetXDisplay (screen_info), (Time) timestamp);
531         myDisplayErrorTrapPopIgnored (screen_info->display_info);
532     }
533     TRACE ("global key grabs %i", screen_info->key_grabs);
534 
535     return screen_info->key_grabs;
536 }
537 
538 unsigned int
myScreenUngrabPointer(ScreenInfo * screen_info,guint32 timestamp)539 myScreenUngrabPointer (ScreenInfo *screen_info, guint32 timestamp)
540 {
541     g_return_val_if_fail (screen_info, 0);
542     TRACE ("timestamp %u", (unsigned int) timestamp);
543 
544     screen_info->pointer_grabs--;
545     if (screen_info->pointer_grabs < 0)
546     {
547         screen_info->pointer_grabs = 0;
548     }
549     if (screen_info->pointer_grabs == 0)
550     {
551         myDisplayErrorTrapPush (screen_info->display_info);
552         xfwm_device_ungrab (screen_info->display_info->devices,
553                             &screen_info->display_info->devices->pointer,
554                             myScreenGetXDisplay (screen_info), (Time) timestamp);
555         myDisplayErrorTrapPopIgnored (screen_info->display_info);
556     }
557     TRACE ("global pointer grabs %i", screen_info->pointer_grabs);
558 
559     return screen_info->pointer_grabs;
560 }
561 
562 void
myScreenGrabKeys(ScreenInfo * screen_info)563 myScreenGrabKeys (ScreenInfo *screen_info)
564 {
565     Display *dpy;
566     int i;
567 
568     g_return_if_fail (screen_info != NULL);
569 
570     dpy = myScreenGetXDisplay (screen_info);
571 
572     for (i = FIRST_KEY; i < KEY_COUNT; i++)
573     {
574         grabKey (screen_info->display_info->devices, dpy,
575                  &screen_info->params->keys[i], screen_info->xroot);
576     }
577 }
578 
579 void
myScreenUngrabKeys(ScreenInfo * screen_info)580 myScreenUngrabKeys (ScreenInfo *screen_info)
581 {
582     Display *dpy;
583 
584     g_return_if_fail (screen_info != NULL);
585 
586     dpy = myScreenGetXDisplay (screen_info);
587     ungrabKeys (screen_info->display_info->devices, dpy, screen_info->xroot);
588 }
589 
590 gint
myScreenGetKeyPressed(ScreenInfo * screen_info,XfwmEventKey * event)591 myScreenGetKeyPressed (ScreenInfo *screen_info, XfwmEventKey *event)
592 {
593     gint key;
594     guint state;
595 
596     state = event->state & MODIFIER_MASK;
597     for (key = 0; key < KEY_COUNT; key++)
598     {
599         if ((screen_info->params->keys[key].keycode == event->keycode)
600             && (screen_info->params->keys[key].modifier == state))
601         {
602             break;
603         }
604     }
605 
606     return key;
607 }
608 
609 int
myScreenGetModifierPressed(ScreenInfo * screen_info)610 myScreenGetModifierPressed (ScreenInfo *screen_info)
611 {
612     int rx, ry;
613 
614     return (int) getMouseXY (screen_info, &rx, &ry);
615 }
616 
617 Client *
myScreenGetClientFromWindow(ScreenInfo * screen_info,Window w,unsigned short mode)618 myScreenGetClientFromWindow (ScreenInfo *screen_info, Window w, unsigned short mode)
619 {
620     Client *c;
621     guint i;
622 
623     g_return_val_if_fail (w != None, NULL);
624     TRACE ("looking for (0x%lx)", w);
625 
626     for (c = screen_info->clients, i = 0; i < screen_info->client_count; c = c->next, i++)
627     {
628         if (clientGetFromWindow (c, w, mode))
629         {
630             return (c);
631         }
632     }
633     TRACE ("no client found");
634 
635     return NULL;
636 }
637 
638 gboolean
myScreenComputeSize(ScreenInfo * screen_info)639 myScreenComputeSize (ScreenInfo *screen_info)
640 {
641     gint num_monitors, i;
642     gint width, height;
643     GdkRectangle monitor;
644     gboolean changed;
645 
646     g_return_val_if_fail (screen_info != NULL, FALSE);
647     g_return_val_if_fail (GDK_IS_SCREEN (screen_info->gscr), FALSE);
648 
649     width = 0;
650     height = 0;
651     num_monitors = xfwm_get_n_monitors (screen_info->gscr);
652 
653     if (num_monitors == 0)
654     {
655         return FALSE;
656     }
657 
658     for (i = 0; i < num_monitors; i++)
659     {
660         xfwm_get_monitor_geometry (screen_info->gscr, i, &monitor, TRUE);
661         width = MAX (monitor.x + monitor.width, width);
662         height = MAX (monitor.y + monitor.height, height);
663     }
664 
665     /* If we failed to compute the size, use whatever xlib reports */
666     if (width == 0 || height == 0)
667     {
668         width = WidthOfScreen (screen_info->xscreen);
669         height = HeightOfScreen (screen_info->xscreen);
670     }
671 
672     changed = ((screen_info->width != width) | (screen_info->height != height));
673     screen_info->width = width;
674     screen_info->height = height;
675     TRACE ("width=%i, height=%i", width, height);
676 
677     return changed;
678 }
679 
680 gboolean
myScreenHasPrimaryMonitor(ScreenInfo * screen_info,Window w)681 myScreenHasPrimaryMonitor (ScreenInfo *screen_info, Window w)
682 {
683 #ifdef HAVE_RANDR
684   Display *display;
685   RROutput primary;
686 
687   primary = None;
688   display = myScreenGetXDisplay (screen_info);
689 
690   g_return_val_if_fail (display, FALSE);
691 
692   if (screen_info->display_info->have_xrandr)
693     primary = XRRGetOutputPrimary (display, w);
694 
695   if (primary != None)
696     return TRUE;
697 #endif /* HAVE_RANDR */
698   return FALSE;
699 }
700 
701 gint
myScreenGetNumMonitors(ScreenInfo * screen_info)702 myScreenGetNumMonitors (ScreenInfo *screen_info)
703 {
704     g_return_val_if_fail (screen_info != NULL, 0);
705     g_return_val_if_fail (screen_info->monitors_index != NULL, 0);
706 
707     return (screen_info->monitors_index->len);
708 }
709 
710 gint
myScreenGetMonitorIndex(ScreenInfo * screen_info,gint idx)711 myScreenGetMonitorIndex (ScreenInfo *screen_info, gint idx)
712 {
713     g_return_val_if_fail (screen_info != NULL, 0);
714     g_return_val_if_fail (screen_info->monitors_index != NULL, 0);
715 
716     return (g_array_index (screen_info->monitors_index, gint, idx));
717 }
718 
719 gboolean
myScreenRebuildMonitorIndex(ScreenInfo * screen_info)720 myScreenRebuildMonitorIndex (ScreenInfo *screen_info)
721 {
722     gint i, j, num_monitors, previous_num_monitors;
723     GdkRectangle monitor, previous;
724     gboolean cloned;
725 
726     g_return_val_if_fail (screen_info != NULL, FALSE);
727 
728     previous_num_monitors = screen_info->num_monitors;
729     screen_info->num_monitors = 0;
730 
731     if (screen_info->monitors_index)
732     {
733         g_array_free (screen_info->monitors_index, TRUE);
734     }
735     screen_info->monitors_index = g_array_new (FALSE, TRUE, sizeof (guint));
736 
737     /* GDK already sorts monitors for us, for "cloned" monitors, sort
738      * the bigger ones first (giving preference to taller monitors
739      * over wider monitors)
740      */
741     num_monitors = xfwm_get_n_monitors (screen_info->gscr);
742     for (i = 0; i < num_monitors; i++)
743     {
744         xfwm_get_monitor_geometry (screen_info->gscr, i, &monitor, TRUE);
745         cloned = FALSE;
746         for (j = 0; j < (gint) screen_info->monitors_index->len; j++)
747         {
748             xfwm_get_monitor_geometry (screen_info->gscr, j, &previous, TRUE);
749             if ((previous.x == monitor.x) && (previous.y == monitor.y))
750             {
751                 cloned = TRUE;
752             }
753         }
754         if (!cloned)
755         {
756             screen_info->num_monitors++;
757             g_array_append_val (screen_info->monitors_index , i);
758         }
759     }
760 
761     TRACE ("physical monitor reported.: %i", num_monitors);
762     TRACE ("logical views found.......: %i", screen_info->num_monitors);
763 
764     return (screen_info->num_monitors != previous_num_monitors);
765 }
766 
767 void
myScreenInvalidateMonitorCache(ScreenInfo * screen_info)768 myScreenInvalidateMonitorCache (ScreenInfo *screen_info)
769 {
770     g_return_if_fail (screen_info != NULL);
771     TRACE ("entering");
772 
773     screen_info->cache_monitor.x = -1;
774     screen_info->cache_monitor.y = -1;
775     screen_info->cache_monitor.width = 0;
776     screen_info->cache_monitor.height = 0;
777 }
778 
779 /*
780    gdk_screen_get_monitor_at_point () doesn't give accurate results
781    when the point is off screen, use my own implementation from xfce 3
782  */
783 void
myScreenFindMonitorAtPoint(ScreenInfo * screen_info,gint x,gint y,GdkRectangle * rect)784 myScreenFindMonitorAtPoint (ScreenInfo *screen_info, gint x, gint y, GdkRectangle *rect)
785 {
786     gint dx, dy, center_x, center_y, num_monitors, i;
787     guint32 distsquare, min_distsquare;
788     GdkRectangle monitor, nearest_monitor = { G_MAXINT, G_MAXINT, 0, 0 };
789 
790     g_return_if_fail (screen_info != NULL);
791     g_return_if_fail (rect != NULL);
792     g_return_if_fail (GDK_IS_SCREEN (screen_info->gscr));
793     TRACE ("(%i,%i)", x, y);
794 
795     /* Cache system */
796     if ((x >= screen_info->cache_monitor.x) && (x < screen_info->cache_monitor.x + screen_info->cache_monitor.width) &&
797         (y >= screen_info->cache_monitor.y) && (y < screen_info->cache_monitor.y + screen_info->cache_monitor.height))
798     {
799         *rect = screen_info->cache_monitor;
800         return;
801     }
802 
803     min_distsquare = G_MAXUINT32;
804     num_monitors = myScreenGetNumMonitors (screen_info);
805 
806     for (i = 0; i < num_monitors; i++)
807     {
808         gint monitor_index;
809 
810         monitor_index = myScreenGetMonitorIndex (screen_info, i);
811         xfwm_get_monitor_geometry (screen_info->gscr, monitor_index, &monitor, TRUE);
812 
813         if ((x >= monitor.x) && (x < (monitor.x + monitor.width)) &&
814             (y >= monitor.y) && (y < (monitor.y + monitor.height)))
815         {
816             screen_info->cache_monitor = monitor;
817             *rect = screen_info->cache_monitor;
818             return;
819         }
820 
821         center_x = monitor.x + (monitor.width / 2);
822         center_y = monitor.y + (monitor.height / 2);
823 
824         dx = x - center_x;
825         dy = y - center_y;
826 
827         distsquare = (dx * dx) + (dy * dy);
828 
829         if (distsquare < min_distsquare)
830         {
831             min_distsquare = distsquare;
832             nearest_monitor = monitor;
833         }
834     }
835 
836     screen_info->cache_monitor = nearest_monitor;
837     *rect = screen_info->cache_monitor;
838 }
839 
840 void
myScreenGetXineramaMonitorGeometry(ScreenInfo * screen_info,gint monitor_num,GdkRectangle * rect)841 myScreenGetXineramaMonitorGeometry (ScreenInfo *screen_info, gint monitor_num, GdkRectangle *rect)
842 {
843     XineramaScreenInfo *infos;
844     int n;
845 
846     g_return_if_fail (screen_info != NULL);
847     g_return_if_fail (rect != NULL);
848     g_return_if_fail (XineramaIsActive (myScreenGetXDisplay (screen_info)));
849 
850     infos = XineramaQueryScreens (myScreenGetXDisplay (screen_info), &n);
851     if (infos == NULL || n <= 0 || monitor_num > n)
852     {
853         g_warning ("Cannot query Xinerama!");
854         XFree (infos);
855 
856         /* Using screen size as fallback, safer than some random values */
857         rect->x = 0;
858         rect->y = 0;
859         rect->width = screen_info->width;
860         rect->height = screen_info->height;
861 
862         return;
863     }
864 
865     rect->x = infos[monitor_num].x_org;
866     rect->y = infos[monitor_num].y_org;
867     rect->width = infos[monitor_num].width;
868     rect->height = infos[monitor_num].height;
869 
870     XFree (infos);
871 }
872 
873 PangoFontDescription *
myScreenGetFontDescription(ScreenInfo * screen_info)874 myScreenGetFontDescription (ScreenInfo *screen_info)
875 {
876     GtkWidget *widget;
877 
878     g_return_val_if_fail (screen_info != NULL, FALSE);
879 
880     if (screen_info->font_desc != NULL)
881     {
882         return screen_info->font_desc;
883     }
884 
885     widget = myScreenGetGtkWidget (screen_info);
886     return getUIPangoFontDesc (widget);
887 }
888 
889 void
myScreenUpdateFontAttr(ScreenInfo * screen_info)890 myScreenUpdateFontAttr (ScreenInfo *screen_info)
891 {
892     PangoAttribute *attr;
893     GtkWidget *widget;
894     gint scale;
895 
896     g_clear_pointer (&screen_info->pango_attr_list, pango_attr_list_unref);
897 
898     widget = myScreenGetGtkWidget (screen_info);
899     scale = gtk_widget_get_scale_factor (widget);
900     screen_info->pango_attr_list = pango_attr_list_new ();
901     attr = pango_attr_scale_new (scale);
902     pango_attr_list_insert (screen_info->pango_attr_list, attr);
903 }
904 
905