Move the CGDisplayReconfigurationCallback to gdkdisplay-quartz.c.

Handling more flags, handling them correctly, and emitting the requisite
signals.

Change screen layout to use CGGetActiveDisplayList instead of NSScreens,
eliminating the latency between updating screens and recomputing the
root window.
This commit is contained in:
John Ralls 2018-11-21 21:32:41 +09:00
parent 9773b1951c
commit 941f3c3887
4 changed files with 150 additions and 175 deletions

View File

@ -30,7 +30,13 @@
#include "gdkscreen.h" #include "gdkscreen.h"
#include "gdkmonitorprivate.h" #include "gdkmonitorprivate.h"
#include "gdkdisplay-quartz.h" #include "gdkdisplay-quartz.h"
#include "gdkmonitor-quartz.h"
static gint MONITORS_CHANGED = 0;
static void display_reconfiguration_callback (CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *data);
static GdkWindow * static GdkWindow *
gdk_quartz_display_get_default_group (GdkDisplay *display) gdk_quartz_display_get_default_group (GdkDisplay *display)
@ -198,18 +204,92 @@ gdk_quartz_display_notify_startup_complete (GdkDisplay *display,
the same for determining the number of monitors and indexing them. the same for determining the number of monitors and indexing them.
*/ */
int
get_active_displays (CGDirectDisplayID **screens)
{
unsigned int displays = 0;
CGGetActiveDisplayList (0, NULL, &displays);
if (screens)
{
*screens = g_new0 (CGDirectDisplayID, displays);
CGGetActiveDisplayList (displays, *screens, &displays);
}
return displays;
}
static void
configure_monitor (GdkMonitor *monitor)
{
GdkQuartzMonitor *quartz_monitor = GDK_QUARTZ_MONITOR (monitor);
CGSize disp_size = CGDisplayScreenSize (quartz_monitor->id);
gint width = (int)trunc (disp_size.width);
gint height = (int)trunc (disp_size.height);
CGRect disp_bounds = CGDisplayBounds (quartz_monitor->id);
GdkRectangle disp_geometry = {(int)trunc (disp_bounds.origin.x),
(int)trunc (disp_bounds.origin.y),
(int)trunc (disp_bounds.size.width),
(int)trunc (disp_bounds.size.height)};
CGDisplayModeRef mode = CGDisplayCopyDisplayMode (quartz_monitor->id);
gint refresh_rate = (int)trunc (CGDisplayModeGetRefreshRate (mode));
monitor->width_mm = width;
monitor->height_mm = height;
monitor->geometry = disp_geometry;
monitor->scale_factor = 1;
monitor->refresh_rate = refresh_rate;
monitor->subpixel_layout = GDK_SUBPIXEL_LAYOUT_UNKNOWN;
CGDisplayModeRelease (mode);
}
static void
display_reconfiguration_callback (CGDirectDisplayID cg_display,
CGDisplayChangeSummaryFlags flags,
void *data)
{
GdkQuartzDisplay *display = data;
GdkQuartzMonitor *monitor;
/* Ignore the begin configuration signal. */
if (flags & kCGDisplayBeginConfigurationFlag)
return;
if (flags & (kCGDisplayMovedFlag | kCGDisplayAddFlag | kCGDisplayEnabledFlag |
kCGDisplaySetMainFlag | kCGDisplayDesktopShapeChangedFlag |
kCGDisplayMirrorFlag | kCGDisplayUnMirrorFlag))
{
monitor = g_hash_table_lookup (display->monitors,
GINT_TO_POINTER (cg_display));
if (!monitor)
{
monitor = g_object_new (GDK_TYPE_QUARTZ_MONITOR,
"display", display, NULL);
monitor->id = cg_display;
g_hash_table_insert (display->monitors, GINT_TO_POINTER (monitor->id),
monitor);
gdk_display_monitor_added (GDK_DISPLAY (display),
GDK_MONITOR (monitor));
}
configure_monitor (GDK_MONITOR (monitor));
}
else if (flags & (kCGDisplayRemoveFlag | kCGDisplayDisabledFlag))
{
GdkMonitor *monitor = g_hash_table_lookup (display->monitors,
GINT_TO_POINTER (cg_display));
gdk_display_monitor_removed (GDK_DISPLAY (display),
GDK_MONITOR (monitor));
g_hash_table_remove (display->monitors, GINT_TO_POINTER (cg_display));
}
g_signal_emit (display, MONITORS_CHANGED, 0);
}
static int static int
gdk_quartz_display_get_n_monitors (GdkDisplay *display) gdk_quartz_display_get_n_monitors (GdkDisplay *display)
{ {
int n; return get_active_displays (NULL);
GDK_QUARTZ_ALLOC_POOL;
n = [[NSScreen screens] count];
GDK_QUARTZ_RELEASE_POOL;
return n;
} }
static GdkMonitor * static GdkMonitor *
@ -217,23 +297,13 @@ gdk_quartz_display_get_monitor (GdkDisplay *display,
int monitor_num) int monitor_num)
{ {
GdkQuartzDisplay *quartz_display = GDK_QUARTZ_DISPLAY (display); GdkQuartzDisplay *quartz_display = GDK_QUARTZ_DISPLAY (display);
NSArray* screens; CGDirectDisplayID *screens = NULL;
NSScreen *screen = NULL;
CGDirectDisplayID id = 0;
GDK_QUARTZ_ALLOC_POOL; int count = get_active_displays (&screens);
screens = [NSScreen screens]; if (monitor_num >= 0 && monitor_num < count)
if (monitor_num >= 0 && monitor_num < [screens count]) return g_hash_table_lookup (quartz_display->monitors,
{ GINT_TO_POINTER (screens[monitor_num]));
screen = [screens objectAtIndex:monitor_num];
id = [[[screen deviceDescription] valueForKey: @"NSScreenNumber"] unsignedIntValue];
}
GDK_QUARTZ_RELEASE_POOL;
if (id)
return g_hash_table_lookup (quartz_display->monitors, GINT_TO_POINTER (id));
return NULL; return NULL;
} }
@ -255,6 +325,7 @@ gdk_quartz_display_init (GdkQuartzDisplay *display)
{ {
uint32_t max_displays = 0, disp; uint32_t max_displays = 0, disp;
CGDirectDisplayID *displays; CGDirectDisplayID *displays;
CGGetActiveDisplayList (0, NULL, &max_displays); CGGetActiveDisplayList (0, NULL, &max_displays);
display->monitors = g_hash_table_new_full (g_direct_hash, NULL, display->monitors = g_hash_table_new_full (g_direct_hash, NULL,
NULL, g_object_unref); NULL, g_object_unref);
@ -262,31 +333,16 @@ gdk_quartz_display_init (GdkQuartzDisplay *display)
CGGetActiveDisplayList (max_displays, displays, &max_displays); CGGetActiveDisplayList (max_displays, displays, &max_displays);
for (disp = 0; disp < max_displays; ++disp) for (disp = 0; disp < max_displays; ++disp)
{ {
CGSize disp_size = CGDisplayScreenSize (displays[disp]); GdkQuartzMonitor *monitor = g_object_new (GDK_TYPE_QUARTZ_MONITOR,
gint width = (int)trunc (disp_size.width);
gint height = (int)trunc (disp_size.height);
CGRect disp_bounds = CGDisplayBounds (displays[disp]);
GdkRectangle disp_geometry = {(int)trunc (disp_bounds.origin.x),
(int)trunc (disp_bounds.origin.y),
(int)trunc (disp_bounds.size.width),
(int)trunc (disp_bounds.size.height)};
CGDisplayModeRef mode = CGDisplayCopyDisplayMode (displays[disp]);
gint refresh_rate = (int)trunc (CGDisplayModeGetRefreshRate (mode));
GdkQuartzMonitor *quartz_monitor = g_object_new (GDK_TYPE_QUARTZ_MONITOR,
"display", display, NULL); "display", display, NULL);
GdkMonitor *monitor = GDK_MONITOR (quartz_monitor); monitor->id = displays[disp];
g_hash_table_insert (display->monitors, GINT_TO_POINTER (monitor->id),
monitor->width_mm = width;
monitor->height_mm = height;
monitor->geometry = disp_geometry;
monitor->scale_factor = 1;
monitor->refresh_rate = refresh_rate;
monitor->subpixel_layout = GDK_SUBPIXEL_LAYOUT_UNKNOWN;
g_hash_table_insert (display->monitors, GINT_TO_POINTER (displays[disp]),
monitor); monitor);
CGDisplayModeRelease (mode); configure_monitor (GDK_MONITOR (monitor));
} }
CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
display);
g_signal_emit (display, MONITORS_CHANGED, 0);
} }
static void static void
@ -295,6 +351,8 @@ gdk_quartz_display_dispose (GObject *object)
GdkQuartzDisplay *display_quartz = GDK_QUARTZ_DISPLAY (object); GdkQuartzDisplay *display_quartz = GDK_QUARTZ_DISPLAY (object);
g_hash_table_destroy (display_quartz->monitors); g_hash_table_destroy (display_quartz->monitors);
CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
display_quartz);
G_OBJECT_CLASS (gdk_quartz_display_parent_class)->dispose (object); G_OBJECT_CLASS (gdk_quartz_display_parent_class)->dispose (object);
} }
@ -362,13 +420,32 @@ gdk_quartz_display_class_init (GdkQuartzDisplayClass *class)
display_class->text_property_to_utf8_list = _gdk_quartz_display_text_property_to_utf8_list; display_class->text_property_to_utf8_list = _gdk_quartz_display_text_property_to_utf8_list;
display_class->utf8_to_string_target = _gdk_quartz_display_utf8_to_string_target; display_class->utf8_to_string_target = _gdk_quartz_display_utf8_to_string_target;
// display_class->get_default_seat = NULL; /* FIXME */ /* display_class->get_default_seat; The parent class default works fine. */
display_class->get_n_monitors = gdk_quartz_display_get_n_monitors; display_class->get_n_monitors = gdk_quartz_display_get_n_monitors;
display_class->get_monitor = gdk_quartz_display_get_monitor; display_class->get_monitor = gdk_quartz_display_get_monitor;
display_class->get_primary_monitor = gdk_quartz_display_get_primary_monitor; display_class->get_primary_monitor = gdk_quartz_display_get_primary_monitor;
display_class->get_monitor_at_window = NULL; /* FIXME */ display_class->get_monitor_at_window = NULL; /* FIXME */
/**
* GdkQuartzDisplay::monitors-changed:
* @display: The object on which the signal is emitted
*
* The ::monitors-changed signal is emitted whenever the arrangement
* of the monitors changes, either because of the addition or
* removal of a monitor or because of some other configuration
* change in System Preferences>Displays including a resolution
* change or a position change. Note that enabling or disabling
* mirroring will result in the addition or removal of the mirror
* monitor(s).
*/
MONITORS_CHANGED =
g_signal_new (g_intern_static_string ("monitors-changed"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0, NULL);
ProcessSerialNumber psn = { 0, kCurrentProcess }; ProcessSerialNumber psn = { 0, kCurrentProcess };
/* Make the current process a foreground application, i.e. an app /* Make the current process a foreground application, i.e. an app

View File

@ -30,6 +30,7 @@ struct _GdkQuartzMonitor
{ {
GdkMonitor parent; GdkMonitor parent;
gint monitor_num; gint monitor_num;
CGDirectDisplayID id;
}; };
struct _GdkQuartzMonitorClass { struct _GdkQuartzMonitorClass {
@ -37,4 +38,3 @@ struct _GdkQuartzMonitorClass {
}; };
#endif #endif

View File

@ -63,12 +63,10 @@
static void gdk_quartz_screen_dispose (GObject *object); static void gdk_quartz_screen_dispose (GObject *object);
static void gdk_quartz_screen_finalize (GObject *object); static void gdk_quartz_screen_finalize (GObject *object);
static void gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen); static void gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen);
static void gdk_quartz_screen_reconfigure (GdkQuartzDisplay *dispplay,
GdkQuartzScreen *screen);
static void display_reconfiguration_callback (CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *userInfo);
static const double dpi = 72.0; static const double dpi = 72.0;
static gint get_mm_from_pixels (NSScreen *screen, int pixels);
G_DEFINE_TYPE (GdkQuartzScreen, gdk_quartz_screen, GDK_TYPE_SCREEN); G_DEFINE_TYPE (GdkQuartzScreen, gdk_quartz_screen, GDK_TYPE_SCREEN);
@ -86,13 +84,11 @@ gdk_quartz_screen_init (GdkQuartzScreen *quartz_screen)
* pangocairo-coretext needs to default to that scaling factor. * pangocairo-coretext needs to default to that scaling factor.
*/ */
g_signal_connect (_gdk_display, "monitors-changed",
G_CALLBACK (gdk_quartz_screen_reconfigure), quartz_screen);
/* The first monitors-changed should have fired already. */
_gdk_screen_set_resolution (screen, dpi); _gdk_screen_set_resolution (screen, dpi);
gdk_quartz_screen_calculate_layout (quartz_screen); gdk_quartz_screen_calculate_layout (quartz_screen);
CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
screen);
quartz_screen->emit_monitors_changed = FALSE; quartz_screen->emit_monitors_changed = FALSE;
} }
@ -107,9 +103,6 @@ gdk_quartz_screen_dispose (GObject *object)
screen->screen_changed_id = 0; screen->screen_changed_id = 0;
} }
CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
screen);
G_OBJECT_CLASS (gdk_quartz_screen_parent_class)->dispose (object); G_OBJECT_CLASS (gdk_quartz_screen_parent_class)->dispose (object);
} }
@ -127,64 +120,42 @@ gdk_quartz_screen_finalize (GObject *object)
static void static void
gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen) gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen)
{ {
NSArray *array; int i, monitors;
int i;
int max_x, max_y; int max_x, max_y;
GdkDisplay *display = gdk_screen_get_display (GDK_SCREEN (screen)); GdkDisplay *display = gdk_screen_get_display (GDK_SCREEN (screen));
GDK_QUARTZ_ALLOC_POOL;
array = [NSScreen screens];
screen->width = 0; screen->width = 0;
screen->height = 0; screen->height = 0;
screen->min_x = 0; screen->min_x = 0;
screen->min_y = 0; screen->min_y = 0;
max_x = max_y = 0; max_x = max_y = 0;
screen->mm_width = 0;
screen->mm_height = 0;
/* We determine the minimum and maximum x and y coordinates /* We determine the minimum and maximum x and y coordinates
* covered by the monitors. From this we can deduce the width * covered by the monitors. From this we can deduce the width
* and height of the root screen. * and height of the root screen.
*/ */
for (i = 0; i < [array count]; i++) monitors = gdk_display_get_n_monitors (display);
for (i = 0; i < monitors; ++i)
{ {
GdkQuartzMonitor *monitor = gdk_display_get_monitor (display, i); GdkQuartzMonitor *monitor =
monitor->monitor_num = i; GDK_QUARTZ_MONITOR (gdk_display_get_monitor (display, i));
GdkRectangle rect;
NSRect rect = [[array objectAtIndex:i] frame]; gdk_monitor_get_geometry (GDK_MONITOR (monitor), &rect);
screen->min_x = MIN (screen->min_x, rect.x);
max_x = MAX (max_x, rect.x + rect.width);
screen->min_x = MIN (screen->min_x, rect.origin.x); screen->min_y = MIN (screen->min_y, rect.y);
max_x = MAX (max_x, rect.origin.x + rect.size.width); max_y = MAX (max_y, rect.y + rect.height);
screen->min_y = MIN (screen->min_y, rect.origin.y); screen->mm_height += GDK_MONITOR (monitor)->height_mm;
max_y = MAX (max_y, rect.origin.y + rect.size.height); screen->mm_width += GDK_MONITOR (monitor)->width_mm;
} }
screen->width = max_x - screen->min_x; screen->width = max_x - screen->min_x;
screen->height = max_y - screen->min_y; screen->height = max_y - screen->min_y;
for (i = 0; i < [array count] ; i++)
{
NSScreen *nsscreen;
NSRect rect;
GdkMonitor *monitor;
monitor = gdk_display_get_monitor (display, i);
nsscreen = [array objectAtIndex:i];
rect = [nsscreen frame];
monitor->geometry.x = rect.origin.x - screen->min_x;
monitor->geometry.y
= screen->height - (rect.origin.y + rect.size.height) + screen->min_y;
monitor->geometry.width = rect.size.width;
monitor->geometry.height = rect.size.height;
if (gdk_quartz_osx_version() >= GDK_OSX_LION)
monitor->scale_factor = [(id <ScaleFactor>) nsscreen backingScaleFactor];
else
monitor->scale_factor = 1;
}
GDK_QUARTZ_RELEASE_POOL;
} }
void void
@ -223,7 +194,7 @@ _gdk_quartz_screen_update_window_sizes (GdkScreen *screen)
} }
static void static void
process_display_reconfiguration (GdkQuartzScreen *screen) gdk_quartz_screen_reconfigure (GdkQuartzDisplay *display, GdkQuartzScreen *screen)
{ {
int width, height; int width, height;
@ -245,56 +216,6 @@ process_display_reconfiguration (GdkQuartzScreen *screen)
g_signal_emit_by_name (screen, "size-changed"); g_signal_emit_by_name (screen, "size-changed");
} }
static gboolean
screen_changed_idle (gpointer data)
{
GdkQuartzScreen *screen = data;
process_display_reconfiguration (data);
screen->screen_changed_id = 0;
return FALSE;
}
static void
display_reconfiguration_callback (CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *userInfo)
{
GdkQuartzScreen *screen = userInfo;
if (flags & kCGDisplayBeginConfigurationFlag)
{
/* Ignore the begin configuration signal. */
return;
}
else
{
/* We save information about the changes, so we can emit
* ::monitors-changed when appropriate. This signal must be
* emitted when the number, size of position of one of the
* monitors changes.
*/
if (flags & kCGDisplayMovedFlag
|| flags & kCGDisplayAddFlag
|| flags & kCGDisplayRemoveFlag
|| flags & kCGDisplayEnabledFlag
|| flags & kCGDisplayDisabledFlag)
screen->emit_monitors_changed = TRUE;
/* At this point Cocoa does not know about the new screen data
* yet, so we delay our refresh into an idle handler.
*/
if (!screen->screen_changed_id)
{
screen->screen_changed_id = gdk_threads_add_idle (screen_changed_idle,
screen);
g_source_set_name_by_id (screen->screen_changed_id, "[gtk+] screen_changed_idle");
}
}
}
static GdkDisplay * static GdkDisplay *
gdk_quartz_screen_get_display (GdkScreen *screen) gdk_quartz_screen_get_display (GdkScreen *screen)
{ {
@ -325,13 +246,6 @@ gdk_quartz_screen_get_height (GdkScreen *screen)
return GDK_QUARTZ_SCREEN (screen)->height; return GDK_QUARTZ_SCREEN (screen)->height;
} }
static gint
get_mm_from_pixels (NSScreen *screen, int pixels)
{
const float mm_per_inch = 25.4;
return (pixels / dpi) * mm_per_inch;
}
static gchar * static gchar *
gdk_quartz_screen_make_display_name (GdkScreen *screen) gdk_quartz_screen_make_display_name (GdkScreen *screen)
{ {
@ -356,34 +270,16 @@ gdk_quartz_screen_is_composited (GdkScreen *screen)
return TRUE; return TRUE;
} }
static NSScreen *
get_nsscreen_for_monitor (gint monitor_num)
{
NSArray *array;
NSScreen *screen;
GDK_QUARTZ_ALLOC_POOL;
array = [NSScreen screens];
screen = [array objectAtIndex:monitor_num];
GDK_QUARTZ_RELEASE_POOL;
return screen;
}
static gint static gint
gdk_quartz_screen_get_width_mm (GdkScreen *screen) gdk_quartz_screen_get_width_mm (GdkScreen *screen)
{ {
return get_mm_from_pixels (get_nsscreen_for_monitor (0), return GDK_QUARTZ_SCREEN (screen)->mm_width;
GDK_QUARTZ_SCREEN (screen)->width);
} }
static gint static gint
gdk_quartz_screen_get_height_mm (GdkScreen *screen) gdk_quartz_screen_get_height_mm (GdkScreen *screen)
{ {
return get_mm_from_pixels (get_nsscreen_for_monitor (0), return GDK_QUARTZ_SCREEN (screen)->mm_height;
GDK_QUARTZ_SCREEN (screen)->height);
} }
static void static void

View File

@ -35,6 +35,8 @@ struct _GdkQuartzScreen
gint width; gint width;
gint height; gint height;
gint mm_width;
gint mm_height;
guint screen_changed_id; guint screen_changed_id;