8e72657de1d3ea4cdd2caa7961fd7d101bbd131b
[WebKit-https.git] / Source / WebCore / platform / mac / PlatformScreenMac.mm
1 /*
2  * Copyright (C) 2006-2018 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "PlatformScreen.h"
28
29 #if PLATFORM(MAC)
30
31 #import "FloatRect.h"
32 #import "FrameView.h"
33 #import "HostWindow.h"
34 #import "ScreenProperties.h"
35 #import <ColorSync/ColorSync.h>
36 #import <pal/spi/cg/CoreGraphicsSPI.h>
37 #import <wtf/ProcessPrivilege.h>
38
39 extern "C" {
40 bool CGDisplayUsesInvertedPolarity(void);
41 bool CGDisplayUsesForceToGray(void);
42 }
43
44 namespace WebCore {
45
46 // These functions scale between screen and page coordinates because JavaScript/DOM operations
47 // assume that the screen and the page share the same coordinate system.
48
49 PlatformDisplayID displayID(NSScreen *screen)
50 {
51     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
52     return [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
53 }
54
55 static PlatformDisplayID displayID(Widget* widget)
56 {
57     if (!widget)
58         return 0;
59
60     auto* view = widget->root();
61     if (!view)
62         return 0;
63
64     auto* hostWindow = view->hostWindow();
65     if (!hostWindow)
66         return 0;
67
68     return hostWindow->displayID();
69 }
70
71 // Screen containing the menubar.
72 static NSScreen *firstScreen()
73 {
74     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
75     NSArray *screens = [NSScreen screens];
76     if (![screens count])
77         return nil;
78     return [screens objectAtIndex:0];
79 }
80
81 static NSWindow *window(Widget* widget)
82 {
83     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
84     if (!widget)
85         return nil;
86     return widget->platformWidget().window;
87 }
88
89 static NSScreen *screen(Widget* widget)
90 {
91     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
92     // If the widget is in a window, use that, otherwise use the display ID from the host window.
93     // First case is for when the NSWindow is in the same process, second case for when it's not.
94     if (auto screenFromWindow = window(widget).screen)
95         return screenFromWindow;
96     return screen(displayID(widget));
97 }
98
99 static ScreenProperties& screenProperties()
100 {
101     static NeverDestroyed<ScreenProperties> screenProperties;
102     return screenProperties;
103 }
104
105 PlatformDisplayID primaryScreenDisplayID()
106 {
107     return screenProperties().primaryDisplayID;
108 }
109
110 ScreenProperties collectScreenProperties()
111 {
112     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
113
114     ScreenProperties screenProperties;
115
116     for (NSScreen *screen in [NSScreen screens]) {
117         auto displayID = WebCore::displayID(screen);
118         FloatRect screenAvailableRect = screen.visibleFrame;
119         screenAvailableRect.setY(NSMaxY(screen.frame) - (screenAvailableRect.y() + screenAvailableRect.height())); // flip
120         FloatRect screenRect = screen.frame;
121
122         RetainPtr<CGColorSpaceRef> colorSpace = screen.colorSpace.CGColorSpace;
123
124         int screenDepth = NSBitsPerPixelFromDepth(screen.depth);
125         int screenDepthPerComponent = NSBitsPerSampleFromDepth(screen.depth);
126         bool screenSupportsExtendedColor = [screen canRepresentDisplayGamut:NSDisplayGamutP3];
127         bool screenHasInvertedColors = CGDisplayUsesInvertedPolarity();
128         bool screenIsMonochrome = CGDisplayUsesForceToGray();
129         uint32_t displayMask = CGDisplayIDToOpenGLDisplayMask(displayID);
130         IORegistryGPUID gpuID = 0;
131
132 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
133         if (displayMask)
134             gpuID = gpuIDForDisplayMask(displayMask);
135 #endif
136
137         screenProperties.screenDataMap.set(displayID, ScreenData { screenAvailableRect, screenRect, colorSpace, screenDepth, screenDepthPerComponent, screenSupportsExtendedColor, screenHasInvertedColors, screenIsMonochrome, displayMask, gpuID });
138
139         if (!screenProperties.primaryDisplayID)
140             screenProperties.primaryDisplayID = displayID;
141     }
142
143     return screenProperties;
144 }
145
146 void setScreenProperties(const ScreenProperties& properties)
147 {
148     screenProperties() = properties;
149 }
150
151 static ScreenData screenData(PlatformDisplayID screendisplayID)
152 {
153     RELEASE_ASSERT(!screenProperties().screenDataMap.isEmpty());
154
155     // Return property of the first screen if the screen is not found in the map.
156     auto displayID = screendisplayID ? screendisplayID : primaryScreenDisplayID();
157     if (displayID) {
158         auto screenPropertiesForDisplay = screenProperties().screenDataMap.find(displayID);
159         if (screenPropertiesForDisplay != screenProperties().screenDataMap.end())
160             return screenPropertiesForDisplay->value;
161     }
162
163     // Last resort: use the first item in the screen list.
164     return screenProperties().screenDataMap.begin()->value;
165 }
166
167 uint32_t primaryOpenGLDisplayMask()
168 {
169     if (!screenProperties().screenDataMap.isEmpty())
170         return screenData(primaryScreenDisplayID()).displayMask;
171     
172     return 0;
173 }
174
175 uint32_t displayMaskForDisplay(PlatformDisplayID displayID)
176 {
177     if (!screenProperties().screenDataMap.isEmpty())
178         return screenData(displayID).displayMask;
179     
180     ASSERT_NOT_REACHED();
181     return 0;
182 }
183
184 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
185 IORegistryGPUID primaryGPUID()
186 {
187     return gpuIDForDisplay(screenProperties().primaryDisplayID);
188 }
189
190 IORegistryGPUID gpuIDForDisplay(PlatformDisplayID displayID)
191 {
192 #if ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
193     if (!screenProperties().screenDataMap.isEmpty())
194         return screenData(displayID).gpuID;
195 #else
196     return gpuIDForDisplayMask(CGDisplayIDToOpenGLDisplayMask(displayID));
197 #endif
198     return 0;
199 }
200
201 IORegistryGPUID gpuIDForDisplayMask(GLuint displayMask)
202 {
203     GLint numRenderers = 0;
204     CGLRendererInfoObj rendererInfo = nullptr;
205     CGLError error = CGLQueryRendererInfo(displayMask, &rendererInfo, &numRenderers);
206     if (!numRenderers || !rendererInfo || error != kCGLNoError)
207         return 0;
208
209     // The 0th renderer should not be the software renderer.
210     GLint isAccelerated;
211     error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPAccelerated, &isAccelerated);
212     if (!isAccelerated || error != kCGLNoError)
213         return 0;
214
215     GLint gpuIDLow = 0;
216     GLint gpuIDHigh = 0;
217
218     error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDLow, &gpuIDLow);
219     if (error != kCGLNoError)
220         return 0;
221
222     error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDHigh, &gpuIDHigh);
223     if (error != kCGLNoError)
224         return 0;
225
226     return (IORegistryGPUID) gpuIDHigh << 32 | gpuIDLow;
227 }
228 #endif // !__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
229
230 static ScreenData getScreenProperties(Widget* widget)
231 {
232     return screenData(displayID(widget));
233 }
234
235 bool screenIsMonochrome(Widget* widget)
236 {
237     if (!screenProperties().screenDataMap.isEmpty())
238         return getScreenProperties(widget).screenIsMonochrome;
239
240     // This is a system-wide accessibility setting, same on all screens.
241     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
242     return CGDisplayUsesForceToGray();
243 }
244
245 bool screenHasInvertedColors()
246 {
247     if (!screenProperties().screenDataMap.isEmpty())
248         return screenData(primaryScreenDisplayID()).screenHasInvertedColors;
249
250     // This is a system-wide accessibility setting, same on all screens.
251     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
252     return CGDisplayUsesInvertedPolarity();
253 }
254
255 int screenDepth(Widget* widget)
256 {
257     if (!screenProperties().screenDataMap.isEmpty()) {
258         auto screenDepth = getScreenProperties(widget).screenDepth;
259         ASSERT(screenDepth);
260         return screenDepth;
261     }
262
263     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
264     return NSBitsPerPixelFromDepth(screen(widget).depth);
265 }
266
267 int screenDepthPerComponent(Widget* widget)
268 {
269     if (!screenProperties().screenDataMap.isEmpty()) {
270         auto depthPerComponent = getScreenProperties(widget).screenDepthPerComponent;
271         ASSERT(depthPerComponent);
272         return depthPerComponent;
273     }
274
275     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
276     return NSBitsPerSampleFromDepth(screen(widget).depth);
277 }
278
279 FloatRect screenRectForDisplay(PlatformDisplayID displayID)
280 {
281     if (!screenProperties().screenDataMap.isEmpty()) {
282         auto screenRect = screenData(displayID).screenRect;
283         ASSERT(!screenRect.isEmpty());
284         return screenRect;
285     }
286
287     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
288     return screen(displayID).frame;
289 }
290
291 FloatRect screenRectForPrimaryScreen()
292 {
293     return screenRectForDisplay(primaryScreenDisplayID());
294 }
295
296 FloatRect screenRect(Widget* widget)
297 {
298     if (!screenProperties().screenDataMap.isEmpty())
299         return getScreenProperties(widget).screenRect;
300
301     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
302     return toUserSpace([screen(widget) frame], window(widget));
303 }
304
305 FloatRect screenAvailableRect(Widget* widget)
306 {
307     if (!screenProperties().screenDataMap.isEmpty())
308         return getScreenProperties(widget).screenAvailableRect;
309
310     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
311     return toUserSpace([screen(widget) visibleFrame], window(widget));
312 }
313
314 NSScreen *screen(NSWindow *window)
315 {
316     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
317     return [window screen] ?: firstScreen();
318 }
319
320 NSScreen *screen(PlatformDisplayID displayID)
321 {
322     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
323     for (NSScreen *screen in [NSScreen screens]) {
324         if (WebCore::displayID(screen) == displayID)
325             return screen;
326     }
327     return firstScreen();
328 }
329
330 CGColorSpaceRef screenColorSpace(Widget* widget)
331 {
332     if (!screenProperties().screenDataMap.isEmpty())
333         return getScreenProperties(widget).colorSpace.get();
334
335     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
336     return screen(widget).colorSpace.CGColorSpace;
337 }
338
339 bool screenSupportsExtendedColor(Widget* widget)
340 {
341     if (!screenProperties().screenDataMap.isEmpty())
342         return getScreenProperties(widget).screenSupportsExtendedColor;
343
344     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
345     return [screen(widget) canRepresentDisplayGamut:NSDisplayGamutP3];
346 }
347
348 FloatRect toUserSpace(const NSRect& rect, NSWindow *destination)
349 {
350     FloatRect userRect = rect;
351     userRect.setY(NSMaxY([screen(destination) frame]) - (userRect.y() + userRect.height())); // flip
352     return userRect;
353 }
354
355 FloatRect toUserSpaceForPrimaryScreen(const NSRect& rect)
356 {
357     FloatRect userRect = rect;
358     userRect.setY(NSMaxY(screenRectForDisplay(primaryScreenDisplayID())) - (userRect.y() + userRect.height())); // flip
359     return userRect;
360 }
361
362 NSRect toDeviceSpace(const FloatRect& rect, NSWindow *source)
363 {
364     FloatRect deviceRect = rect;
365     deviceRect.setY(NSMaxY([screen(source) frame]) - (deviceRect.y() + deviceRect.height())); // flip
366     return deviceRect;
367 }
368
369 NSPoint flipScreenPoint(const NSPoint& screenPoint, NSScreen *screen)
370 {
371     NSPoint flippedPoint = screenPoint;
372     flippedPoint.y = NSMaxY([screen frame]) - flippedPoint.y;
373     return flippedPoint;
374 }
375
376 } // namespace WebCore
377
378 #endif // PLATFORM(MAC)