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 "gdkmonitorprivate.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 *
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.
*/
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
gdk_quartz_display_get_n_monitors (GdkDisplay *display)
{
int n;
GDK_QUARTZ_ALLOC_POOL;
n = [[NSScreen screens] count];
GDK_QUARTZ_RELEASE_POOL;
return n;
return get_active_displays (NULL);
}
static GdkMonitor *
@ -217,23 +297,13 @@ gdk_quartz_display_get_monitor (GdkDisplay *display,
int monitor_num)
{
GdkQuartzDisplay *quartz_display = GDK_QUARTZ_DISPLAY (display);
NSArray* screens;
NSScreen *screen = NULL;
CGDirectDisplayID id = 0;
CGDirectDisplayID *screens = NULL;
GDK_QUARTZ_ALLOC_POOL;
int count = get_active_displays (&screens);
screens = [NSScreen screens];
if (monitor_num >= 0 && monitor_num < [screens count])
{
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));
if (monitor_num >= 0 && monitor_num < count)
return g_hash_table_lookup (quartz_display->monitors,
GINT_TO_POINTER (screens[monitor_num]));
return NULL;
}
@ -255,6 +325,7 @@ gdk_quartz_display_init (GdkQuartzDisplay *display)
{
uint32_t max_displays = 0, disp;
CGDirectDisplayID *displays;
CGGetActiveDisplayList (0, NULL, &max_displays);
display->monitors = g_hash_table_new_full (g_direct_hash, NULL,
NULL, g_object_unref);
@ -262,31 +333,16 @@ gdk_quartz_display_init (GdkQuartzDisplay *display)
CGGetActiveDisplayList (max_displays, displays, &max_displays);
for (disp = 0; disp < max_displays; ++disp)
{
CGSize disp_size = CGDisplayScreenSize (displays[disp]);
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,
GdkQuartzMonitor *monitor = g_object_new (GDK_TYPE_QUARTZ_MONITOR,
"display", display, NULL);
GdkMonitor *monitor = GDK_MONITOR (quartz_monitor);
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->id = displays[disp];
g_hash_table_insert (display->monitors, GINT_TO_POINTER (monitor->id),
monitor);
CGDisplayModeRelease (mode);
configure_monitor (GDK_MONITOR (monitor));
}
CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
display);
g_signal_emit (display, MONITORS_CHANGED, 0);
}
static void
@ -295,6 +351,8 @@ gdk_quartz_display_dispose (GObject *object)
GdkQuartzDisplay *display_quartz = GDK_QUARTZ_DISPLAY (object);
g_hash_table_destroy (display_quartz->monitors);
CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
display_quartz);
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->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_monitor = gdk_quartz_display_get_monitor;
display_class->get_primary_monitor = gdk_quartz_display_get_primary_monitor;
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 };
/* Make the current process a foreground application, i.e. an app

View File

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

View File

@ -63,12 +63,10 @@
static void gdk_quartz_screen_dispose (GObject *object);
static void gdk_quartz_screen_finalize (GObject *object);
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 gint get_mm_from_pixels (NSScreen *screen, int pixels);
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.
*/
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_quartz_screen_calculate_layout (quartz_screen);
CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
screen);
quartz_screen->emit_monitors_changed = FALSE;
}
@ -107,9 +103,6 @@ gdk_quartz_screen_dispose (GObject *object)
screen->screen_changed_id = 0;
}
CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
screen);
G_OBJECT_CLASS (gdk_quartz_screen_parent_class)->dispose (object);
}
@ -127,64 +120,42 @@ gdk_quartz_screen_finalize (GObject *object)
static void
gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen)
{
NSArray *array;
int i;
int i, monitors;
int max_x, max_y;
GdkDisplay *display = gdk_screen_get_display (GDK_SCREEN (screen));
GDK_QUARTZ_ALLOC_POOL;
array = [NSScreen screens];
screen->width = 0;
screen->height = 0;
screen->min_x = 0;
screen->min_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
* covered by the monitors. From this we can deduce the width
* 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);
monitor->monitor_num = i;
GdkQuartzMonitor *monitor =
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);
max_x = MAX (max_x, rect.origin.x + rect.size.width);
screen->min_y = MIN (screen->min_y, rect.y);
max_y = MAX (max_y, rect.y + rect.height);
screen->min_y = MIN (screen->min_y, rect.origin.y);
max_y = MAX (max_y, rect.origin.y + rect.size.height);
screen->mm_height += GDK_MONITOR (monitor)->height_mm;
screen->mm_width += GDK_MONITOR (monitor)->width_mm;
}
screen->width = max_x - screen->min_x;
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
@ -223,7 +194,7 @@ _gdk_quartz_screen_update_window_sizes (GdkScreen *screen)
}
static void
process_display_reconfiguration (GdkQuartzScreen *screen)
gdk_quartz_screen_reconfigure (GdkQuartzDisplay *display, GdkQuartzScreen *screen)
{
int width, height;
@ -245,56 +216,6 @@ process_display_reconfiguration (GdkQuartzScreen *screen)
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 *
gdk_quartz_screen_get_display (GdkScreen *screen)
{
@ -325,13 +246,6 @@ gdk_quartz_screen_get_height (GdkScreen *screen)
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 *
gdk_quartz_screen_make_display_name (GdkScreen *screen)
{
@ -356,34 +270,16 @@ gdk_quartz_screen_is_composited (GdkScreen *screen)
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
gdk_quartz_screen_get_width_mm (GdkScreen *screen)
{
return get_mm_from_pixels (get_nsscreen_for_monitor (0),
GDK_QUARTZ_SCREEN (screen)->width);
return GDK_QUARTZ_SCREEN (screen)->mm_width;
}
static gint
gdk_quartz_screen_get_height_mm (GdkScreen *screen)
{
return get_mm_from_pixels (get_nsscreen_for_monitor (0),
GDK_QUARTZ_SCREEN (screen)->height);
return GDK_QUARTZ_SCREEN (screen)->mm_height;
}
static void

View File

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