Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/ScreenHelperGTK.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "ScreenHelperGTK.h"
8
9
#ifdef MOZ_X11
10
#include <gdk/gdkx.h>
11
#endif /* MOZ_X11 */
12
#ifdef MOZ_WAYLAND
13
#include <gdk/gdkwayland.h>
14
#endif /* MOZ_WAYLAND */
15
#include <dlfcn.h>
16
#include <gtk/gtk.h>
17
18
#include "gfxPlatformGtk.h"
19
#include "mozilla/Logging.h"
20
#include "nsGtkUtils.h"
21
#include "nsTArray.h"
22
23
namespace mozilla {
24
namespace widget {
25
26
static LazyLogModule sScreenLog("WidgetScreen");
27
28
static void
29
monitors_changed(GdkScreen* aScreen, gpointer aClosure)
30
0
{
31
0
  MOZ_LOG(sScreenLog, LogLevel::Debug, ("Received monitors-changed event"));
32
0
  ScreenHelperGTK* self = static_cast<ScreenHelperGTK*>(aClosure);
33
0
  self->RefreshScreens();
34
0
}
35
36
static GdkFilterReturn
37
root_window_event_filter(GdkXEvent* aGdkXEvent, GdkEvent* aGdkEvent,
38
                         gpointer aClosure)
39
0
{
40
0
#ifdef MOZ_X11
41
0
  ScreenHelperGTK* self = static_cast<ScreenHelperGTK*>(aClosure);
42
0
  XEvent *xevent = static_cast<XEvent*>(aGdkXEvent);
43
0
44
0
  switch (xevent->type) {
45
0
    case PropertyNotify:
46
0
      {
47
0
        XPropertyEvent *propertyEvent = &xevent->xproperty;
48
0
        if (propertyEvent->atom == self->NetWorkareaAtom()) {
49
0
          MOZ_LOG(sScreenLog, LogLevel::Debug, ("Work area size changed"));
50
0
          self->RefreshScreens();
51
0
        }
52
0
      }
53
0
      break;
54
0
    default:
55
0
      break;
56
0
  }
57
0
#endif
58
0
59
0
  return GDK_FILTER_CONTINUE;
60
0
}
61
62
ScreenHelperGTK::ScreenHelperGTK()
63
  : mRootWindow(nullptr)
64
#ifdef MOZ_X11
65
  , mNetWorkareaAtom(0)
66
#endif
67
0
{
68
0
  MOZ_LOG(sScreenLog, LogLevel::Debug, ("ScreenHelperGTK created"));
69
0
  GdkScreen *defaultScreen = gdk_screen_get_default();
70
0
  if (!defaultScreen) {
71
0
    // Sometimes we don't initial X (e.g., xpcshell)
72
0
    MOZ_LOG(sScreenLog, LogLevel::Debug, ("defaultScreen is nullptr, running headless"));
73
0
    return;
74
0
  }
75
0
  mRootWindow = gdk_get_default_root_window();
76
0
  MOZ_ASSERT(mRootWindow);
77
0
78
0
  g_object_ref(mRootWindow);
79
0
80
0
  // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify
81
0
  gdk_window_set_events(mRootWindow,
82
0
                        GdkEventMask(gdk_window_get_events(mRootWindow) |
83
0
                                     GDK_PROPERTY_CHANGE_MASK));
84
0
85
0
  g_signal_connect(defaultScreen, "monitors-changed",
86
0
                   G_CALLBACK(monitors_changed), this);
87
0
#ifdef MOZ_X11
88
0
  gdk_window_add_filter(mRootWindow, root_window_event_filter, this);
89
0
  if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
90
0
    mNetWorkareaAtom =
91
0
      XInternAtom(GDK_WINDOW_XDISPLAY(mRootWindow), "_NET_WORKAREA", False);
92
0
  }
93
0
#endif
94
0
  RefreshScreens();
95
0
}
96
97
ScreenHelperGTK::~ScreenHelperGTK()
98
0
{
99
0
  if (mRootWindow) {
100
0
    g_signal_handlers_disconnect_by_func(gdk_screen_get_default(),
101
0
                                         FuncToGpointer(monitors_changed),
102
0
                                         this);
103
0
104
0
    gdk_window_remove_filter(mRootWindow, root_window_event_filter, this);
105
0
    g_object_unref(mRootWindow);
106
0
    mRootWindow = nullptr;
107
0
  }
108
0
}
109
110
gint
111
ScreenHelperGTK::GetGTKMonitorScaleFactor(gint aMonitorNum)
112
0
{
113
0
#if (MOZ_WIDGET_GTK >= 3)
114
0
  // Since GDK 3.10
115
0
  static auto sGdkScreenGetMonitorScaleFactorPtr = (gint (*)(GdkScreen*, gint))
116
0
    dlsym(RTLD_DEFAULT, "gdk_screen_get_monitor_scale_factor");
117
0
  if (sGdkScreenGetMonitorScaleFactorPtr) {
118
0
    GdkScreen *screen = gdk_screen_get_default();
119
0
    return sGdkScreenGetMonitorScaleFactorPtr(screen, aMonitorNum);
120
0
  }
121
0
#endif
122
0
  return 1;
123
0
}
124
125
static uint32_t
126
GetGTKPixelDepth()
127
0
{
128
0
  GdkVisual * visual = gdk_screen_get_system_visual(gdk_screen_get_default());
129
0
  return gdk_visual_get_depth(visual);
130
0
}
131
132
static already_AddRefed<Screen>
133
MakeScreen(GdkScreen* aScreen, gint aMonitorNum)
134
0
{
135
0
  GdkRectangle monitor;
136
0
  GdkRectangle workarea;
137
0
  gdk_screen_get_monitor_geometry(aScreen, aMonitorNum, &monitor);
138
0
  gdk_screen_get_monitor_workarea(aScreen, aMonitorNum, &workarea);
139
0
  gint gdkScaleFactor = ScreenHelperGTK::GetGTKMonitorScaleFactor(aMonitorNum);
140
0
141
0
  // gdk_screen_get_monitor_geometry / workarea returns application pixels
142
0
  // (desktop pixels), so we need to convert it to device pixels with
143
0
  // gdkScaleFactor.
144
0
  LayoutDeviceIntRect rect(monitor.x * gdkScaleFactor,
145
0
                           monitor.y * gdkScaleFactor,
146
0
                           monitor.width * gdkScaleFactor,
147
0
                           monitor.height * gdkScaleFactor);
148
0
  LayoutDeviceIntRect availRect(workarea.x * gdkScaleFactor,
149
0
                                workarea.y * gdkScaleFactor,
150
0
                                workarea.width * gdkScaleFactor,
151
0
                                workarea.height * gdkScaleFactor);
152
0
  uint32_t pixelDepth = GetGTKPixelDepth();
153
0
154
0
  // Use per-monitor scaling factor in gtk/wayland, or 1.0 otherwise.
155
0
  DesktopToLayoutDeviceScale contentsScale(1.0);
156
#ifdef MOZ_WAYLAND
157
    GdkDisplay* gdkDisplay = gdk_display_get_default();
158
    if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) {
159
      contentsScale.scale = gdkScaleFactor;
160
    }
161
#endif
162
163
0
  CSSToLayoutDeviceScale defaultCssScale(
164
0
    gdkScaleFactor * gfxPlatformGtk::GetFontScaleFactor());
165
0
166
0
  float dpi = 96.0f;
167
0
  gint heightMM = gdk_screen_get_monitor_height_mm(aScreen, aMonitorNum);
168
0
  if (heightMM > 0) {
169
0
    dpi = rect.height / (heightMM / MM_PER_INCH_FLOAT);
170
0
  }
171
0
172
0
  MOZ_LOG(sScreenLog, LogLevel::Debug,
173
0
           ("New screen [%d %d %d %d (%d %d %d %d) %d %f %f %f]",
174
0
            rect.x, rect.y, rect.width, rect.height,
175
0
            availRect.x, availRect.y, availRect.width, availRect.height,
176
0
            pixelDepth, contentsScale.scale, defaultCssScale.scale, dpi));
177
0
  RefPtr<Screen> screen = new Screen(rect, availRect,
178
0
                                     pixelDepth, pixelDepth,
179
0
                                     contentsScale, defaultCssScale,
180
0
                                     dpi);
181
0
  return screen.forget();
182
0
}
183
184
void
185
ScreenHelperGTK::RefreshScreens()
186
0
{
187
0
  MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens"));
188
0
  AutoTArray<RefPtr<Screen>, 4> screenList;
189
0
190
0
  GdkScreen *defaultScreen = gdk_screen_get_default();
191
0
  gint numScreens = gdk_screen_get_n_monitors(defaultScreen);
192
0
  MOZ_LOG(sScreenLog, LogLevel::Debug,
193
0
          ("GDK reports %d screens", numScreens));
194
0
195
0
  for (gint i = 0; i < numScreens; i++) {
196
0
    screenList.AppendElement(MakeScreen(defaultScreen, i));
197
0
  }
198
0
199
0
  ScreenManager& screenManager = ScreenManager::GetSingleton();
200
0
  screenManager.Refresh(std::move(screenList));
201
0
}
202
203
} // namespace widget
204
} // namespace mozilla