/*
* Copyright © 2020 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include
#include
#include "gdkmacosdisplay-private.h"
#include "gdkmacosmonitor-private.h"
#include "gdkmacosutils-private.h"
struct _GdkMacosMonitor
{
GdkMonitor parent_instance;
CGDirectDisplayID screen_id;
NSRect workarea;
guint has_opengl : 1;
};
struct _GdkMacosMonitorClass
{
GdkMonitorClass parent_class;
};
G_DEFINE_TYPE (GdkMacosMonitor, gdk_macos_monitor, GDK_TYPE_MONITOR)
/**
* gdk_macos_monitor_get_workarea:
* @monitor: a `GdkMonitor`
* @workarea: (out): a `GdkRectangle` to be filled with the monitor workarea
*
* Retrieves the size and position of the “work area” on a monitor
* within the display coordinate space.
*
* The returned geometry is in ”application pixels”, not in ”device pixels”
* (see [method@Gdk.Monitor.get_scale_factor]).
*/
void
gdk_macos_monitor_get_workarea (GdkMonitor *monitor,
GdkRectangle *geometry)
{
GdkMacosMonitor *self = (GdkMacosMonitor *)monitor;
int x, y;
g_return_if_fail (GDK_IS_MACOS_MONITOR (self));
g_return_if_fail (geometry != NULL);
x = self->workarea.origin.x;
y = self->workarea.origin.y + self->workarea.size.height;
_gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (monitor->display),
x, y,
&x, &y);
geometry->x = x;
geometry->y = y;
geometry->width = self->workarea.size.width;
geometry->height = self->workarea.size.height;
}
static void
gdk_macos_monitor_class_init (GdkMacosMonitorClass *klass)
{
}
static void
gdk_macos_monitor_init (GdkMacosMonitor *self)
{
}
static GdkSubpixelLayout
GetSubpixelLayout (CGDirectDisplayID screen_id)
{
#if 0
GDK_BEGIN_MACOS_ALLOC_POOL;
GdkSubpixelLayout subpixel_layout = GDK_SUBPIXEL_LAYOUT_UNKNOWN;
io_service_t iosvc;
NSDictionary *dict;
guint layout;
gboolean rotation;
rotation = CGDisplayRotation (screen_id);
iosvc = CGDisplayIOServicePort (screen_id);
dict = (NSDictionary *)IODisplayCreateInfoDictionary (iosvc, kIODisplayOnlyPreferredName);
layout = [[dict objectForKey:@kDisplaySubPixelLayout] unsignedIntValue];
switch (layout)
{
case kDisplaySubPixelLayoutRGB:
if (rotation == 0.0)
subpixel_layout = GDK_SUBPIXEL_LAYOUT_HORIZONTAL_RGB;
else if (rotation == 90.0)
subpixel_layout = GDK_SUBPIXEL_LAYOUT_VERTICAL_RGB;
else if (rotation == 180.0 || rotation == -180.0)
subpixel_layout = GDK_SUBPIXEL_LAYOUT_HORIZONTAL_BGR;
else if (rotation == -90.0)
subpixel_layout = GDK_SUBPIXEL_LAYOUT_VERTICAL_BGR;
break;
case kDisplaySubPixelLayoutBGR:
if (rotation == 0.0)
subpixel_layout = GDK_SUBPIXEL_LAYOUT_HORIZONTAL_BGR;
else if (rotation == 90.0)
subpixel_layout = GDK_SUBPIXEL_LAYOUT_VERTICAL_BGR;
else if (rotation == 180.0 || rotation == -180.0)
subpixel_layout = GDK_SUBPIXEL_LAYOUT_HORIZONTAL_RGB;
else if (rotation == -90.0 || rotation == 270.0)
subpixel_layout = GDK_SUBPIXEL_LAYOUT_VERTICAL_RGB;
break;
default:
break;
}
GDK_END_MACOS_ALLOC_POOL;
return subpixel_layout;
#endif
return GDK_SUBPIXEL_LAYOUT_UNKNOWN;
}
static char *
GetLocalizedName (NSScreen *screen)
{
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_15_AND_LATER
GDK_BEGIN_MACOS_ALLOC_POOL;
NSString *str;
char *name;
g_assert (screen);
str = [screen localizedName];
name = g_strdup ([str UTF8String]);
GDK_END_MACOS_ALLOC_POOL;
return g_steal_pointer (&name);
#else
return NULL;
#endif
}
static char *
GetConnectorName (CGDirectDisplayID screen_id)
{
guint unit = CGDisplayUnitNumber (screen_id);
return g_strdup_printf ("unit-%u", unit);
}
static NSScreen *
find_screen (CGDirectDisplayID screen_id)
{
GDK_BEGIN_MACOS_ALLOC_POOL;
NSScreen *screen = NULL;
for (id obj in [NSScreen screens])
{
if (screen_id == [[[obj deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue])
{
screen = (NSScreen *)obj;
break;
}
}
GDK_END_MACOS_ALLOC_POOL;
return screen;
}
gboolean
_gdk_macos_monitor_reconfigure (GdkMacosMonitor *self)
{
GdkSubpixelLayout subpixel_layout;
CGDisplayModeRef mode;
GdkMacosDisplay *display;
NSScreen *screen;
GdkRectangle geom;
gboolean has_opengl;
CGSize size;
CGRect bounds;
size_t width;
size_t pixel_width;
char *connector;
char *name;
int refresh_rate;
int scale_factor = 1;
int width_mm;
int height_mm;
g_return_val_if_fail (GDK_IS_MACOS_MONITOR (self), FALSE);
display = GDK_MACOS_DISPLAY (GDK_MONITOR (self)->display);
if (!(screen = find_screen (self->screen_id)) ||
!(mode = CGDisplayCopyDisplayMode (self->screen_id)))
return FALSE;
size = CGDisplayScreenSize (self->screen_id);
bounds = [screen frame];
width = CGDisplayModeGetWidth (mode);
pixel_width = CGDisplayModeGetPixelWidth (mode);
has_opengl = CGDisplayUsesOpenGLAcceleration (self->screen_id);
subpixel_layout = GetSubpixelLayout (self->screen_id);
name = GetLocalizedName (screen);
connector = GetConnectorName (self->screen_id);
if (width != 0 && pixel_width != 0)
scale_factor = MAX (1, pixel_width / width);
width_mm = size.width;
height_mm = size.height;
geom.x = bounds.origin.x - display->min_x;
geom.y = display->height - bounds.origin.y - bounds.size.height + display->min_y;
geom.width = bounds.size.width;
geom.height = bounds.size.height;
/* We will often get 0 back from CGDisplayModeGetRefreshRate(). We
* can fallback by getting it from CoreVideo based on a CVDisplayLink
* setting (which is also used by the frame clock).
*/
if (!(refresh_rate = CGDisplayModeGetRefreshRate (mode)))
refresh_rate = _gdk_macos_display_get_nominal_refresh_rate (display);
gdk_monitor_set_connector (GDK_MONITOR (self), connector);
gdk_monitor_set_model (GDK_MONITOR (self), name);
gdk_monitor_set_geometry (GDK_MONITOR (self), &geom);
gdk_monitor_set_physical_size (GDK_MONITOR (self), width_mm, height_mm);
gdk_monitor_set_scale_factor (GDK_MONITOR (self), scale_factor);
gdk_monitor_set_refresh_rate (GDK_MONITOR (self), refresh_rate);
gdk_monitor_set_subpixel_layout (GDK_MONITOR (self), GDK_SUBPIXEL_LAYOUT_UNKNOWN);
self->workarea = [screen visibleFrame];
/* We might be able to use this at some point to change which GSK renderer
* we use for surfaces on this monitor. For example, it might be better
* to use cairo if we cannot use OpenGL (or it would be software) anyway.
* Presumably that is more common in cases where macOS is running under
* an emulator such as QEMU.
*/
self->has_opengl = !!has_opengl;
CGDisplayModeRelease (mode);
g_free (name);
g_free (connector);
return TRUE;
}
GdkMacosMonitor *
_gdk_macos_monitor_new (GdkMacosDisplay *display,
CGDirectDisplayID screen_id)
{
GdkMacosMonitor *self;
g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL);
self = g_object_new (GDK_TYPE_MACOS_MONITOR,
"display", display,
NULL);
self->screen_id = screen_id;
_gdk_macos_monitor_reconfigure (self);
return g_steal_pointer (&self);
}
CGDirectDisplayID
_gdk_macos_monitor_get_screen_id (GdkMacosMonitor *self)
{
g_return_val_if_fail (GDK_IS_MACOS_MONITOR (self), 0);
return self->screen_id;
}
CGColorSpaceRef
_gdk_macos_monitor_copy_colorspace (GdkMacosMonitor *self)
{
g_return_val_if_fail (GDK_IS_MACOS_MONITOR (self), NULL);
return CGDisplayCopyColorSpace (self->screen_id);
}