1 /*
2  * CsScreen: An introspectable C class that establishes an event
3  * trap for the screensaver.  It watches for any X events that could result
4  * in other windows showing up over our Stage, and ensures the Stage stays on
5  * top.  This will only ever be other override-redirect (non-managed) X windows,
6  * such as native Firefox or Chrome notification popups.
7  *
8  */
9 
10 #include "config.h"
11 #include "cs-screen.h"
12 
13 #include <string.h>
14 
15 #include <X11/Xlib.h>
16 #include <X11/Xutil.h>
17 
18 #ifdef HAVE_SOLARIS_XINERAMA
19 #include <X11/extensions/xinerama.h>
20 #endif
21 #ifdef HAVE_XFREE_XINERAMA
22 #include <X11/extensions/Xinerama.h>
23 #endif
24 #ifdef HAVE_RANDR
25 #include <X11/extensions/Xrandr.h>
26 #endif
27 
28 enum {
29         SCREEN_MONITORS_CHANGED,
30         SCREEN_SIZE_CHANGED,
31         COMPOSITED_CHANGED,
32         LAST_SIGNAL
33 };
34 
35 static guint signals [LAST_SIGNAL] = { 0, };
36 
37 G_DEFINE_TYPE (CsScreen, cs_screen, G_TYPE_OBJECT);
38 
39 static gboolean debug_mode = FALSE;
40 #define DEBUG(...) if (debug_mode) g_printerr (__VA_ARGS__)
41 
42 #define cs_XFree(p) do { if ((p)) XFree ((p)); } while (0)
43 
44 #define PRIMARY_MONITOR 0
45 
46 static gboolean
cs_rectangle_equal(const GdkRectangle * src1,const GdkRectangle * src2)47 cs_rectangle_equal (const GdkRectangle *src1,
48                     const GdkRectangle *src2)
49 {
50     return ((src1->x == src2->x) &&
51             (src1->y == src2->y) &&
52             (src1->width == src2->width) &&
53             (src1->height == src2->height));
54 }
55 
56 /* The list of monitors reported by the windowing system might include
57  * mirrored monitors with identical bounds. Since mirrored monitors
58  * shouldn't be treated as separate monitors for most purposes, we
59  * filter them out here. (We ignore the possibility of partially
60  * overlapping monitors because they are rare and it's hard to come
61  * up with any sensible interpretation.)
62  */
63 static void
filter_mirrored_monitors(CsScreen * screen)64 filter_mirrored_monitors (CsScreen *screen)
65 {
66     int i, j;
67 
68     /* Currently always true and simplifies things */
69     g_assert (screen->primary_monitor_index == 0);
70 
71     for (i = 1; i < screen->n_monitor_infos; i++)
72     {
73         /* In case we've filtered previous monitors */
74         screen->monitor_infos[i].number = i;
75 
76         for (j = 0; j < i; j++)
77         {
78             if (cs_rectangle_equal (&screen->monitor_infos[i].rect,
79                                     &screen->monitor_infos[j].rect))
80             {
81                 memmove (&screen->monitor_infos[i],
82                          &screen->monitor_infos[i + 1],
83                          (screen->n_monitor_infos - i - 1) * sizeof (CsMonitorInfo));
84                 screen->n_monitor_infos--;
85                 i--;
86 
87                 continue;
88             }
89         }
90     }
91 }
92 
93 #ifdef HAVE_RANDR
94 static CsMonitorInfo *
find_monitor_with_rect(CsScreen * screen,int x,int y,int w,int h)95 find_monitor_with_rect (CsScreen *screen, int x, int y, int w, int h)
96 {
97     CsMonitorInfo *info;
98     int i;
99 
100     for (i = 0; i < screen->n_monitor_infos; i++)
101     {
102         info = &screen->monitor_infos[i];
103         if (x == info->rect.x &&
104             y == info->rect.y &&
105             w == info->rect.width &&
106             h == info->rect.height)
107         {
108             return info;
109         }
110     }
111 
112   return NULL;
113 }
114 
115 /* In the case of multiple outputs of a single crtc (mirroring), we consider one of the
116  * outputs the "main". This is the one we consider "owning" the windows, so if
117  * the mirroring is changed to a dual monitor setup then the windows are moved to the
118  * crtc that now has that main output. If one of the outputs is the primary that is
119  * always the main, otherwise we just use the first.
120  */
121 static XID
find_main_output_for_crtc(XRRScreenResources * resources,XRRCrtcInfo * crtc,Display * xdisplay,XID xroot)122 find_main_output_for_crtc (XRRScreenResources *resources,
123                            XRRCrtcInfo        *crtc,
124                            Display            *xdisplay,
125                            XID                 xroot)
126 {
127     XRROutputInfo *output;
128     RROutput primary_output;
129     int i;
130     XID res;
131 
132     primary_output = XRRGetOutputPrimary (xdisplay, xroot);
133 
134     res = None;
135     for (i = 0; i < crtc->noutput; i++)
136     {
137         output = XRRGetOutputInfo (xdisplay, resources, crtc->outputs[i]);
138         if (output->connection != RR_Disconnected &&
139             (res == None || crtc->outputs[i] == primary_output))
140         {
141             res = crtc->outputs[i];
142         }
143 
144         XRRFreeOutputInfo (output);
145     }
146 
147     return res;
148 }
149 #endif
150 
151 static void
apply_scale_factor(CsMonitorInfo * infos,gint n_infos,gint factor)152 apply_scale_factor (CsMonitorInfo *infos,
153                     gint           n_infos,
154                     gint           factor)
155 {
156     gint i;
157 
158     for (i = 0; i < n_infos; i++)
159     {
160         infos[i].rect.x /= factor;
161         infos[i].rect.y /= factor;
162         infos[i].rect.width /= factor;
163         infos[i].rect.height /= factor;
164 
165         DEBUG ("Scale factor of %d applied.  Monitor %d is %d,%d %d x %d\n",
166                factor,
167                infos[i].number,
168                infos[i].rect.x,
169                infos[i].rect.y,
170                infos[i].rect.width,
171                infos[i].rect.height);
172     }
173 }
174 
175 #define MONITOR_WIDTH_THRESHOLD 1200
176 #define MONITOR_HEIGHT_THRESHOLD 1000
177 
178 static gboolean
get_low_res_mode(CsScreen * screen,CsMonitorInfo * infos,gint n_infos)179 get_low_res_mode (CsScreen      *screen,
180                   CsMonitorInfo *infos,
181                   gint           n_infos)
182 {
183     gint i;
184     gint smallest_width, smallest_height;
185 
186     smallest_width = smallest_height = G_MAXINT;
187 
188     for (i = 0; i < n_infos; i++)
189     {
190         smallest_width = MIN (infos[i].rect.width, smallest_width);
191         smallest_height = MIN (infos[i].rect.height, smallest_height);
192     }
193 
194     screen->smallest_width = smallest_width;
195     screen->smallest_height = smallest_height;
196 
197     if (smallest_width < MONITOR_WIDTH_THRESHOLD || smallest_height < MONITOR_HEIGHT_THRESHOLD)
198     {
199         DEBUG ("Narrowest monitor width after scaling (%dx%d) is below threshold of %dx%d, applying low-res mode\n",
200                smallest_width,
201                smallest_height,
202                MONITOR_WIDTH_THRESHOLD,
203                MONITOR_HEIGHT_THRESHOLD);
204 
205         return TRUE;
206     }
207 
208     return FALSE;
209 }
210 
211 static void
reload_monitor_infos(CsScreen * screen)212 reload_monitor_infos (CsScreen *screen)
213 {
214     GdkDisplay *gdk_display;
215     Display *xdisplay;
216     Window xroot;
217 
218     gdk_display = gdk_screen_get_display (screen->gdk_screen);
219     xdisplay = gdk_x11_display_get_xdisplay (gdk_display);
220 
221     xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (screen->gdk_screen));
222 
223     /* Any previous screen->monitor_infos is freed by the caller */
224 
225     screen->monitor_infos = NULL;
226     screen->n_monitor_infos = 0;
227 
228     /* Xinerama doesn't have a concept of primary monitor, however XRandR
229      * does. However, the XRandR xinerama compat code always sorts the
230      * primary output first, so we rely on that here. We could use the
231      * native XRandR calls instead of xinerama, but that would be
232      * slightly problematic for _NET_WM_FULLSCREEN_MONITORS support, as
233      * that is defined in terms of xinerama monitor indexes.
234      * So, since we don't need anything in xrandr except the primary
235      * we can keep using xinerama and use the first monitor as the
236      * primary.
237      */
238 
239     screen->primary_monitor_index = PRIMARY_MONITOR;
240 
241 
242 #ifdef HAVE_XFREE_XINERAMA
243     if (screen->n_monitor_infos == 0 &&
244         XineramaIsActive (xdisplay))
245     {
246         XineramaScreenInfo *infos;
247         int n_infos;
248         int i;
249 
250         n_infos = 0;
251         infos = XineramaQueryScreens (xdisplay, &n_infos);
252 
253         DEBUG ("Found %d Xinerama screens on display %s\n",
254                n_infos, gdk_display_get_name (gdk_display));
255 
256         if (n_infos > 0)
257         {
258             screen->monitor_infos = g_new0 (CsMonitorInfo, n_infos);
259             screen->n_monitor_infos = n_infos;
260 
261             i = 0;
262             while (i < n_infos)
263             {
264                 screen->monitor_infos[i].number = infos[i].screen_number;
265                 screen->monitor_infos[i].rect.x = infos[i].x_org;
266                 screen->monitor_infos[i].rect.y = infos[i].y_org;
267                 screen->monitor_infos[i].rect.width = infos[i].width;
268                 screen->monitor_infos[i].rect.height = infos[i].height;
269 
270                 DEBUG ("Monitor %d is %d,%d %d x %d\n",
271                        screen->monitor_infos[i].number,
272                        screen->monitor_infos[i].rect.x,
273                        screen->monitor_infos[i].rect.y,
274                        screen->monitor_infos[i].rect.width,
275                        screen->monitor_infos[i].rect.height);
276 
277                 ++i;
278             }
279         }
280 
281         cs_XFree (infos);
282 
283 #ifdef HAVE_RANDR
284     {
285         XRRScreenResources *resources;
286 
287         resources = XRRGetScreenResourcesCurrent (xdisplay, xroot);
288 
289         if (resources)
290         {
291             for (i = 0; i < resources->ncrtc; i++)
292             {
293                 XRRCrtcInfo *crtc;
294                 CsMonitorInfo *info;
295 
296                 crtc = XRRGetCrtcInfo (xdisplay, resources, resources->crtcs[i]);
297                 info = find_monitor_with_rect (screen, crtc->x, crtc->y, (int)crtc->width, (int)crtc->height);
298 
299                 if (info)
300                 {
301                   info->output = find_main_output_for_crtc (resources, crtc, xdisplay, xroot);
302                 }
303 
304                 XRRFreeCrtcInfo (crtc);
305             }
306 
307             XRRFreeScreenResources (resources);
308         }
309     }
310 #endif
311     }
312     else if (screen->n_monitor_infos > 0)
313     {
314         DEBUG ("No XFree86 Xinerama extension or XFree86 Xinerama inactive on display %s\n",
315                gdk_display_get_name (gdk_display));
316     }
317 #else
318     DEBUG ("Muffin compiled without XFree86 Xinerama support\n");
319 #endif /* HAVE_XFREE_XINERAMA */
320 
321 #ifdef HAVE_SOLARIS_XINERAMA
322     /* This code from GDK, Copyright (C) 2002 Sun Microsystems */
323     if (screen->n_monitor_infos == 0 &&
324         XineramaGetState (xdisplay,
325                           gdk_screen_get_number (screen->gdk_screen)))
326     {
327         XRectangle monitors[MAXFRAMEBUFFERS];
328         unsigned char hints[16];
329         int result;
330         int n_monitors;
331         int i;
332 
333         n_monitors = 0;
334         result = XineramaGetInfo (xdisplay,
335                                   gdk_screen_get_number (screen->gdk_screen),
336                                   monitors, hints,
337                                   &n_monitors);
338         /* Yes I know it should be Success but the current implementation
339          * returns the num of monitor
340          */
341         if (result > 0)
342         {
343             g_assert (n_monitors > 0);
344 
345             screen->monitor_infos = g_new0 (CsMonitorInfo, n_monitors);
346             screen->n_monitor_infos = n_monitors;
347 
348             i = 0;
349             while (i < n_monitors)
350             {
351                 screen->monitor_infos[i].number = i;
352                 screen->monitor_infos[i].rect.x = monitors[i].x;
353                 screen->monitor_infos[i].rect.y = monitors[i].y;
354                 screen->monitor_infos[i].rect.width = monitors[i].width;
355                 screen->monitor_infos[i].rect.height = monitors[i].height;
356 
357                 DEBUG ("Monitor %d is %d,%d %d x %d\n",
358                        screen->monitor_infos[i].number,
359                        screen->monitor_infos[i].rect.x,
360                        screen->monitor_infos[i].rect.y,
361                        screen->monitor_infos[i].rect.width,
362                        screen->monitor_infos[i].rect.height);
363                 ++i;
364             }
365         }
366     }
367     else if (screen->n_monitor_infos == 0)
368     {
369         DEBUG ("No Solaris Xinerama extension or Solaris Xinerama inactive on display %s\n",
370                gdk_display_get_name (gdk_display));
371     }
372 #else
373     DEBUG ("Cinnamon Screensaver compiled without Solaris Xinerama support\n");
374 #endif /* HAVE_SOLARIS_XINERAMA */
375 
376     /* If no Xinerama, fill in the single screen info so
377      * we can use the field unconditionally
378     */
379     if (screen->n_monitor_infos == 0)
380     {
381         DEBUG ("No Xinerama screens, using default screen info\n");
382 
383         screen->monitor_infos = g_new0 (CsMonitorInfo, 1);
384         screen->n_monitor_infos = 1;
385 
386         screen->monitor_infos[0].number = 0;
387         screen->monitor_infos[0].rect = screen->rect;
388     }
389 
390     filter_mirrored_monitors (screen);
391 
392     screen->monitor_infos[screen->primary_monitor_index].is_primary = TRUE;
393 
394     apply_scale_factor (screen->monitor_infos,
395                         screen->n_monitor_infos,
396                         gdk_screen_get_monitor_scale_factor (screen->gdk_screen, PRIMARY_MONITOR));
397 
398     screen->low_res = get_low_res_mode (screen,
399                                         screen->monitor_infos,
400                                         screen->n_monitor_infos);
401 
402     g_assert (screen->n_monitor_infos > 0);
403     g_assert (screen->monitor_infos != NULL);
404 }
405 
406 static void
reload_screen_info(CsScreen * screen)407 reload_screen_info (CsScreen *screen)
408 {
409     screen->rect.x = screen->rect.y = 0;
410     screen->rect.width = gdk_screen_get_width (screen->gdk_screen);
411     screen->rect.height = gdk_screen_get_height (screen->gdk_screen);
412 }
413 
414 static void
on_monitors_changed(GdkScreen * gdk_screen,gpointer user_data)415 on_monitors_changed (GdkScreen *gdk_screen, gpointer user_data)
416 {
417     CsMonitorInfo *old_monitor_infos;
418     CsScreen *screen;
419 
420     screen = CS_SCREEN (user_data);
421 
422     reload_screen_info (screen);
423     g_signal_emit (screen, signals[SCREEN_SIZE_CHANGED], 0);
424 
425     gdk_flush ();
426 
427     DEBUG ("CsScreen received 'monitors-changed' signal from GdkScreen\n");
428 
429     old_monitor_infos = screen->monitor_infos;
430     reload_monitor_infos (screen);
431 
432     g_free (old_monitor_infos);
433 
434     g_signal_emit (screen, signals[SCREEN_MONITORS_CHANGED], 0);
435 }
436 
437 static void
on_screen_changed(GdkScreen * gdk_screen,gpointer user_data)438 on_screen_changed (GdkScreen *gdk_screen, gpointer user_data)
439 {
440     CsScreen *screen;
441 
442     screen = CS_SCREEN (user_data);
443 
444     DEBUG ("CsScreen received 'size-changed' signal from GdkScreen\n");
445 
446     reload_screen_info (screen);
447     g_signal_emit (screen, signals[SCREEN_SIZE_CHANGED], 0);
448 }
449 
450 static void
on_composited_changed(GdkScreen * gdk_screen,gpointer user_data)451 on_composited_changed (GdkScreen *gdk_screen, gpointer user_data)
452 {
453     CsScreen *screen;
454 
455     screen = CS_SCREEN (user_data);
456 
457     DEBUG ("CsScreen received 'composited-changed' signal from GdkScreen\n");
458 
459     g_signal_emit (screen, signals[COMPOSITED_CHANGED], 0);
460 }
461 
462 static void
cs_screen_init(CsScreen * screen)463 cs_screen_init (CsScreen *screen)
464 {
465     screen->gdk_screen = gdk_screen_get_default ();
466 
467     screen->monitors_changed_id = g_signal_connect (screen->gdk_screen,
468                                                     "monitors-changed",
469                                                     G_CALLBACK (on_monitors_changed),
470                                                     screen);
471     screen->screen_size_changed_id = g_signal_connect (screen->gdk_screen,
472                                                        "size-changed",
473                                                        G_CALLBACK (on_screen_changed),
474                                                        screen);
475     screen->composited_changed_id = g_signal_connect (screen->gdk_screen,
476                                                       "composited-changed",
477                                                       G_CALLBACK (on_composited_changed),
478                                                       screen);
479 
480     reload_screen_info (screen);
481     reload_monitor_infos (screen);
482 }
483 
484 static void
cs_screen_finalize(GObject * object)485 cs_screen_finalize (GObject *object)
486 {
487     CsScreen *screen;
488 
489     g_return_if_fail (object != NULL);
490     g_return_if_fail (CS_IS_SCREEN (object));
491 
492     screen = CS_SCREEN (object);
493 
494     if (screen->monitor_infos)
495     {
496         g_free (screen->monitor_infos);
497     }
498 
499     DEBUG ("CsScreen finalize\n");
500 
501     G_OBJECT_CLASS (cs_screen_parent_class)->finalize (object);
502 }
503 
504 static void
cs_screen_dispose(GObject * object)505 cs_screen_dispose (GObject *object)
506 {
507     CsScreen *screen;
508 
509     g_return_if_fail (object != NULL);
510     g_return_if_fail (CS_IS_SCREEN (object));
511 
512     screen = CS_SCREEN (object);
513 
514     if (screen->monitors_changed_id > 0)
515     {
516         g_signal_handler_disconnect (screen->gdk_screen, screen->monitors_changed_id);
517         screen->monitors_changed_id = 0;
518     }
519 
520     if (screen->screen_size_changed_id > 0)
521     {
522         g_signal_handler_disconnect (screen->gdk_screen, screen->screen_size_changed_id);
523         screen->screen_size_changed_id = 0;
524     }
525 
526     if (screen->composited_changed_id > 0)
527     {
528         g_signal_handler_disconnect (screen->gdk_screen, screen->composited_changed_id);
529         screen->composited_changed_id = 0;
530     }
531 
532     DEBUG ("CsScreen dispose\n");
533 
534     G_OBJECT_CLASS (cs_screen_parent_class)->dispose (object);
535 }
536 
537 static void
cs_screen_class_init(CsScreenClass * klass)538 cs_screen_class_init (CsScreenClass *klass)
539 {
540     GObjectClass *object_class = G_OBJECT_CLASS (klass);
541 
542     object_class->finalize = cs_screen_finalize;
543     object_class->dispose = cs_screen_dispose;
544 
545     signals[SCREEN_MONITORS_CHANGED] = g_signal_new ("monitors-changed",
546                                               G_TYPE_FROM_CLASS (object_class),
547                                               G_SIGNAL_RUN_LAST,
548                                               0,
549                                               NULL, NULL, NULL,
550                                               G_TYPE_NONE, 0);
551 
552     signals[SCREEN_SIZE_CHANGED] = g_signal_new ("size-changed",
553                                             G_TYPE_FROM_CLASS (object_class),
554                                             G_SIGNAL_RUN_LAST,
555                                             0,
556                                             NULL, NULL, NULL,
557                                             G_TYPE_NONE, 0);
558 
559     signals[COMPOSITED_CHANGED] = g_signal_new ("composited-changed",
560                                             G_TYPE_FROM_CLASS (object_class),
561                                             G_SIGNAL_RUN_LAST,
562                                             0,
563                                             NULL, NULL, NULL,
564                                             G_TYPE_NONE, 0);
565 }
566 
567 CsScreen *
cs_screen_new(gboolean debug)568 cs_screen_new (gboolean debug)
569 {
570     GObject     *result;
571 
572     debug_mode = debug;
573 
574     result = g_object_new (CS_TYPE_SCREEN, NULL);
575 
576     return CS_SCREEN (result);
577 }
578 
579 /**
580  * cs_screen_get_monitor_geometry:
581  * @screen: a #CsScreen
582  * @monitor: the monitor number
583  * @geometry: (out): location to store the monitor geometry
584  *
585  * Stores the location and size of the indicated monitor in @geometry.
586  */
587 void
cs_screen_get_monitor_geometry(CsScreen * screen,gint monitor,GdkRectangle * geometry)588 cs_screen_get_monitor_geometry (CsScreen     *screen,
589                                 gint          monitor,
590                                 GdkRectangle *geometry)
591 {
592     g_return_if_fail (CS_IS_SCREEN (screen));
593     g_return_if_fail (monitor >= 0 && monitor < screen->n_monitor_infos);
594     g_return_if_fail (geometry != NULL);
595 
596     geometry->x = screen->monitor_infos[monitor].rect.x;
597     geometry->y = screen->monitor_infos[monitor].rect.y;
598     geometry->width = screen->monitor_infos[monitor].rect.width;
599     geometry->height = screen->monitor_infos[monitor].rect.height;
600 }
601 
602 /**
603  * cs_screen_get_screen_geometry:
604  * @screen: a #CsScreen
605  * @geometry: (out): location to store the screen geometry
606  *
607  * Stores the location and size of the screen in @geometry.
608  */
609 void
cs_screen_get_screen_geometry(CsScreen * screen,GdkRectangle * geometry)610 cs_screen_get_screen_geometry (CsScreen     *screen,
611                                GdkRectangle *geometry)
612 {
613     g_return_if_fail (CS_IS_SCREEN (screen));
614     g_return_if_fail (geometry != NULL);
615 
616     geometry->x = screen->rect.x;
617     geometry->y = screen->rect.y;
618     geometry->width = screen->rect.width;
619     geometry->height = screen->rect.height;
620 }
621 
622 /**
623  * cs_screen_get_primary_monitor:
624  * @screen: a #CsScreen
625  *
626  * Gets the index of the primary monitor on this @screen.
627  *
628  * Return value: a monitor index
629  */
630 gint
cs_screen_get_primary_monitor(CsScreen * screen)631 cs_screen_get_primary_monitor (CsScreen *screen)
632 {
633     g_return_val_if_fail (CS_IS_SCREEN (screen), 0);
634 
635     return screen->primary_monitor_index;
636 }
637 
638 /**
639  * cs_screen_get_n_monitors:
640  * @screen: a #CsScreen
641  *
642  * Gets the number of monitors that are joined together to form @screen.
643  *
644  * Return value: the number of monitors
645  */
646 gint
cs_screen_get_n_monitors(CsScreen * screen)647 cs_screen_get_n_monitors (CsScreen *screen)
648 {
649     g_return_val_if_fail (CS_IS_SCREEN (screen), 0);
650 
651     return screen->n_monitor_infos;
652 }
653 
654 /**
655  * cs_screen_get_mouse_monitor:
656  * @screen: a #CsScreen
657  *
658  * Gets the index of the monitor that the mouse pointer currently
659  * occupies.
660  *
661  * Return value: the monitor index for the pointer
662  */
663 gint
cs_screen_get_mouse_monitor(CsScreen * screen)664 cs_screen_get_mouse_monitor (CsScreen *screen)
665 {
666     GdkDisplay *gdk_display;
667 
668     Window xroot, root_return, child_return;
669     int root_x_return, root_y_return;
670     int win_x_return, win_y_return;
671     unsigned int mask_return;
672     gint scale_factor;
673 
674     gint i;
675     gint ret = 0;
676 
677     g_return_val_if_fail (CS_IS_SCREEN (screen), 0);
678 
679     gdk_display = gdk_screen_get_display (screen->gdk_screen);
680     xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (screen->gdk_screen));
681 
682     gdk_error_trap_push ();
683     XQueryPointer (gdk_x11_display_get_xdisplay (gdk_display),
684                    xroot,
685                    &root_return,
686                    &child_return,
687                    &root_x_return,
688                    &root_y_return,
689                    &win_x_return,
690                    &win_y_return,
691                    &mask_return);
692     gdk_error_trap_pop_ignored ();
693 
694     scale_factor = gdk_screen_get_monitor_scale_factor (screen->gdk_screen, 0);
695     root_x_return /= scale_factor;
696     root_y_return /= scale_factor;
697 
698     for (i = 0; i < screen->n_monitor_infos; i++)
699     {
700         GdkRectangle iter = screen->monitor_infos[i].rect;
701 
702         if (root_x_return >= iter.x && root_x_return <= iter.x + iter.width &&
703             root_y_return >= iter.y && root_y_return <= iter.y + iter.height)
704         {
705             ret = i;
706             break;
707         }
708     }
709 
710     return ret;
711 }
712 
713 /**
714  * cs_screen_get_low_res_mode:
715  * @screen: a #CsScreen
716  *
717  * Gets whether or not one of our monitors falls below the low res threshold (1200 wide).
718  * This lets us display certain things at smaller sizes to prevent truncating of images, etc.
719  *
720  * Returns: Whether or not to use low res mode.
721  */
722 gboolean
cs_screen_get_low_res_mode(CsScreen * screen)723 cs_screen_get_low_res_mode (CsScreen *screen)
724 {
725     g_return_val_if_fail (CS_IS_SCREEN (screen), FALSE);
726 
727     return screen->low_res;
728 }
729 
730 /**
731  * cs_screen_get_smallest_monitor_sizes:
732  * @screen: a #CsScreen
733  * @width: (out): width of the smallest monitor
734  * @height: (out): height of the smallest monitor
735  *
736  * Gets whether or not one of our monitors falls below the low res threshold (1200 wide).
737  * This lets us display certain things at smaller sizes to prevent truncating of images, etc.
738  *
739  * Returns: Whether or not to use low res mode.
740  */
741 void
cs_screen_get_smallest_monitor_sizes(CsScreen * screen,gint * width,gint * height)742 cs_screen_get_smallest_monitor_sizes (CsScreen *screen,
743                                       gint     *width,
744                                       gint     *height)
745 {
746     g_return_if_fail (CS_IS_SCREEN (screen));
747 
748     if (width != NULL)
749     {
750         *width = screen->smallest_width;
751     }
752 
753     if (height != NULL)
754     {
755         *height = screen->smallest_height;
756     }
757 }
758 
759 /**
760  * cs_screen_center_pointer_in_primary_monitor:
761  * @screen: The #CsScreen
762  *
763  * Warps the mouse pointer to the center in x, and half again below center
764  * in y, of the primary monitor.  This is used during waking to have the
765  * unlock dialog appear on the primary monitor (at least, initially).
766  */
767 void
cs_screen_place_pointer_in_primary_monitor(CsScreen * screen)768 cs_screen_place_pointer_in_primary_monitor (CsScreen *screen)
769 {
770     GdkDisplay *display;
771     GdkRectangle rect;
772     GdkSeat *seat;
773     GdkDevice *pointer;
774 
775     g_return_if_fail (CS_IS_SCREEN (screen));
776 
777     cs_screen_get_monitor_geometry (screen,
778                                     screen->primary_monitor_index,
779                                     &rect);
780 
781     display = gdk_screen_get_display (screen->gdk_screen);
782     seat = gdk_display_get_default_seat (display);
783 
784     pointer = gdk_seat_get_pointer (seat);
785 
786     gdk_device_warp (pointer,
787                      screen->gdk_screen,
788                      rect.x + (rect.width * .5),
789                      rect.y + (rect.height * .75));
790 }
791 
792 /**
793  * cs_screen_reset_screensaver:
794  *
795  * Resets the screensaver idle timer. If called when the screensaver is active
796  * it will stop it.
797  *
798  */
799 void
cs_screen_reset_screensaver(void)800 cs_screen_reset_screensaver (void)
801 {
802     XResetScreenSaver (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()));
803 }
804 
805 void
cs_screen_nuke_focus(void)806 cs_screen_nuke_focus (void)
807 {
808     Window focus = 0;
809     int    rev = 0;
810 
811     DEBUG ("Nuking focus\n");
812 
813     gdk_error_trap_push ();
814 
815     XGetInputFocus (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &focus, &rev);
816     XSetInputFocus (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), PointerRoot, RevertToNone, CurrentTime);
817 
818     gdk_error_trap_pop_ignored ();
819 }
820