/* gdkscreen-quartz.c
*
* Copyright (C) 2005 Imendio AB
+ * Copyright (C) 2009,2010 Kristian Rietveld <kris@gtk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
-#include "gdk.h"
+
+#include <gdk/gdk.h>
+
#include "gdkprivate-quartz.h"
-/* FIXME: If we want to do it properly, this should be stored
- * in a proper GdkScreen subclass.
+
+/* A couple of notes about this file are in order. In GDK, a
+ * GdkScreen can contain multiple monitors. A GdkScreen has an
+ * associated root window, in which the monitors are placed. The
+ * root window "spans" all monitors. The origin is at the top-left
+ * corner of the root window.
+ *
+ * Cocoa works differently. The system has a "screen" (NSScreen) for
+ * each monitor that is connected (note the conflicting definitions
+ * of screen). The screen containing the menu bar is screen 0 and the
+ * bottom-left corner of this screen is the origin of the "monitor
+ * coordinate space". All other screens are positioned according to this
+ * origin. If the menu bar is on a secondary screen (for example on
+ * a monitor hooked up to a laptop), then this screen is screen 0 and
+ * other monitors will be positioned according to the "secondary screen".
+ * The main screen is the monitor that shows the window that is currently
+ * active (has focus), the position of the menu bar does not have influence
+ * on this!
+ *
+ * Upon start up and changes in the layout of screens, we calculate the
+ * size of the GdkScreen root window that is needed to be able to place
+ * all monitors in the root window. Once that size is known, we iterate
+ * over the monitors and translate their Cocoa position to a position
+ * in the root window of the GdkScreen. This happens below in the
+ * function gdk_quartz_screen_calculate_layout().
+ *
+ * A Cocoa coordinate is always relative to the origin of the monitor
+ * coordinate space. Such coordinates are mapped to their respective
+ * position in the GdkScreen root window (_gdk_quartz_window_xy_to_gdk_xy)
+ * and vice versa (_gdk_quartz_window_gdk_xy_to_xy). Both functions can
+ * be found in gdkwindow-quartz.c. Note that Cocoa coordinates can have
+ * negative values (in case a monitor is located left or below of screen 0),
+ * but GDK coordinates can *not*!
*/
-static GdkColormap *default_colormap = NULL;
-static int n_screens = 0;
-static GdkRectangle *screen_rects = NULL;
+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 display_reconfiguration_callback (CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void *userInfo);
+
+G_DEFINE_TYPE (GdkQuartzScreen, gdk_quartz_screen, GDK_TYPE_SCREEN);
+
+static void
+gdk_quartz_screen_init (GdkQuartzScreen *quartz_screen)
+{
+ GdkScreen *screen = GDK_SCREEN (quartz_screen);
+ NSScreen *nsscreen;
+
+ nsscreen = [[NSScreen screens] objectAtIndex:0];
+ gdk_screen_set_resolution (screen,
+ 72.0 * [nsscreen userSpaceScaleFactor]);
+
+ gdk_quartz_screen_calculate_layout (quartz_screen);
+
+ CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
+ screen);
+
+ quartz_screen->emit_monitors_changed = FALSE;
+}
+
+static void
+gdk_quartz_screen_dispose (GObject *object)
+{
+ GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
+
+ if (screen->screen_changed_id)
+ {
+ g_source_remove (screen->screen_changed_id);
+ screen->screen_changed_id = 0;
+ }
+
+ CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
+ screen);
+
+ G_OBJECT_CLASS (gdk_quartz_screen_parent_class)->dispose (object);
+}
+
+static void
+gdk_quartz_screen_screen_rects_free (GdkQuartzScreen *screen)
+{
+ screen->n_screens = 0;
+
+ if (screen->screen_rects)
+ {
+ g_free (screen->screen_rects);
+ screen->screen_rects = NULL;
+ }
+}
static void
-screen_rects_init (void)
+gdk_quartz_screen_finalize (GObject *object)
+{
+ GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
+
+ gdk_quartz_screen_screen_rects_free (screen);
+}
+
+
+static void
+gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen)
{
NSArray *array;
- NSRect largest_rect;
int i;
+ int max_x, max_y;
GDK_QUARTZ_ALLOC_POOL;
- array = [NSScreen screens];
+ gdk_quartz_screen_screen_rects_free (screen);
- n_screens = [array count];
- screen_rects = g_new0 (GdkRectangle, n_screens);
+ array = [NSScreen screens];
- /* FIXME: as stated above the get_width() and get_height() functions
- * in this file, we only support horizontal screen layouts for now.
- */
+ screen->width = 0;
+ screen->height = 0;
+ screen->min_x = 0;
+ screen->min_y = 0;
+ max_x = max_y = 0;
- /* Find the monitor with the largest height. All monitors should be
- * offset to this one in the GDK screen space instead of offset to
- * the screen with the menu bar.
+ /* 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.
*/
- largest_rect = [[array objectAtIndex:0] frame];
- for (i = 1; i < [array count]; i++)
+ for (i = 0; i < [array count]; i++)
{
NSRect rect = [[array objectAtIndex:i] frame];
- if (rect.size.height > largest_rect.size.height)
- largest_rect = [[array objectAtIndex:i] frame];
+ 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.origin.y);
+ max_y = MAX (max_y, rect.origin.y + rect.size.height);
}
- for (i = 0; i < n_screens; i++)
+ screen->width = max_x - screen->min_x;
+ screen->height = max_y - screen->min_y;
+
+ screen->n_screens = [array count];
+ screen->screen_rects = g_new0 (GdkRectangle, screen->n_screens);
+
+ for (i = 0; i < screen->n_screens; i++)
{
NSScreen *nsscreen;
NSRect rect;
nsscreen = [array objectAtIndex:i];
rect = [nsscreen frame];
- screen_rects[i].x = rect.origin.x;
- screen_rects[i].width = rect.size.width;
- screen_rects[i].height = rect.size.height;
-
- if (largest_rect.size.height - rect.size.height == 0)
- screen_rects[i].y = 0;
- else
- screen_rects[i].y = largest_rect.size.height - rect.size.height + largest_rect.origin.y;
+ screen->screen_rects[i].x = rect.origin.x - screen->min_x;
+ screen->screen_rects[i].y
+ = screen->height - (rect.origin.y + rect.size.height) + screen->min_y;
+ screen->screen_rects[i].width = rect.size.width;
+ screen->screen_rects[i].height = rect.size.height;
}
GDK_QUARTZ_RELEASE_POOL;
}
void
-_gdk_quartz_screen_init (void)
+_gdk_quartz_screen_update_window_sizes (GdkScreen *screen)
{
- gdk_screen_set_default_colormap (_gdk_screen,
- gdk_screen_get_system_colormap (_gdk_screen));
+ GList *windows, *list;
+
+ /* The size of the root window is so that it can contain all
+ * monitors attached to this machine. The monitors are laid out
+ * within this root window. We calculate the size of the root window
+ * and the positions of the different monitors in gdkscreen-quartz.c.
+ *
+ * This data is updated when the monitor configuration is changed.
+ */
- screen_rects_init ();
-}
+ /* FIXME: At some point, fetch the root window from GdkScreen. But
+ * on OS X will we only have a single root window anyway.
+ */
+ _gdk_root->x = 0;
+ _gdk_root->y = 0;
+ _gdk_root->abs_x = 0;
+ _gdk_root->abs_y = 0;
+ _gdk_root->width = gdk_screen_get_width (screen);
+ _gdk_root->height = gdk_screen_get_height (screen);
-GdkDisplay *
-gdk_screen_get_display (GdkScreen *screen)
-{
- g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ windows = gdk_screen_get_toplevel_windows (screen);
- return _gdk_display;
-}
+ for (list = windows; list; list = list->next)
+ _gdk_quartz_window_update_position (list->data);
+ g_list_free (windows);
+}
-GdkWindow *
-gdk_screen_get_root_window (GdkScreen *screen)
+static void
+process_display_reconfiguration (GdkQuartzScreen *screen)
{
- g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ int width, height;
- return _gdk_root;
-}
+ width = gdk_screen_get_width (GDK_SCREEN (screen));
+ height = gdk_screen_get_height (GDK_SCREEN (screen));
-gint
-gdk_screen_get_number (GdkScreen *screen)
-{
- g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+ gdk_quartz_screen_calculate_layout (GDK_QUARTZ_SCREEN (screen));
- return 0;
-}
+ _gdk_quartz_screen_update_window_sizes (GDK_SCREEN (screen));
-gchar *
-_gdk_windowing_substitute_screen_number (const gchar *display_name,
- int screen_number)
-{
- if (screen_number != 0)
- return NULL;
+ if (screen->emit_monitors_changed)
+ {
+ g_signal_emit_by_name (screen, "monitors-changed");
+ screen->emit_monitors_changed = FALSE;
+ }
- return g_strdup (display_name);
+ if (width != gdk_screen_get_width (GDK_SCREEN (screen))
+ || height != gdk_screen_get_height (GDK_SCREEN (screen)))
+ g_signal_emit_by_name (screen, "size-changed");
}
-GdkColormap*
-gdk_screen_get_default_colormap (GdkScreen *screen)
+static gboolean
+screen_changed_idle (gpointer data)
{
- return default_colormap;
-}
+ GdkQuartzScreen *screen = data;
-void
-gdk_screen_set_default_colormap (GdkScreen *screen,
- GdkColormap *colormap)
-{
- GdkColormap *old_colormap;
-
- g_return_if_fail (GDK_IS_SCREEN (screen));
- g_return_if_fail (GDK_IS_COLORMAP (colormap));
+ process_display_reconfiguration (data);
- old_colormap = default_colormap;
+ screen->screen_changed_id = 0;
- default_colormap = g_object_ref (colormap);
-
- if (old_colormap)
- g_object_unref (old_colormap);
+ return FALSE;
}
-/* FIXME: note on the get_width() and the get_height() methods. For
- * now we only support screen layouts where the screens are laid out
- * horizontally. Mac OS X also supports laying out the screens vertically
- * and the screens having "non-standard" offsets from eachother. In the
- * future we need a much more sophiscated algorithm to translate these
- * layouts to GDK coordinate space and GDK screen layout.
- */
-gint
-gdk_screen_get_width (GdkScreen *screen)
+static void
+display_reconfiguration_callback (CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void *userInfo)
{
- int i;
- int width;
- NSArray *array;
-
- g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+ GdkQuartzScreen *screen = userInfo;
- GDK_QUARTZ_ALLOC_POOL;
- array = [NSScreen screens];
-
- width = 0;
- for (i = 0; i < [array count]; i++)
+ if (flags & kCGDisplayBeginConfigurationFlag)
{
- NSRect rect = [[array objectAtIndex:i] frame];
- width += rect.size.width;
+ /* 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);
+ }
+}
- GDK_QUARTZ_RELEASE_POOL;
-
- return width;
+static GdkDisplay *
+gdk_quartz_screen_get_display (GdkScreen *screen)
+{
+ return _gdk_display;
}
-gint
-gdk_screen_get_height (GdkScreen *screen)
+static GdkWindow *
+gdk_quartz_screen_get_root_window (GdkScreen *screen)
{
- int i;
- int height;
- NSArray *array;
+ return _gdk_root;
+}
- g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+static gint
+gdk_quartz_screen_get_number (GdkScreen *screen)
+{
+ return 0;
+}
- GDK_QUARTZ_ALLOC_POOL;
- array = [NSScreen screens];
+gchar *
+_gdk_windowing_substitute_screen_number (const gchar *display_name,
+ int screen_number)
+{
+ if (screen_number != 0)
+ return NULL;
- height = 0;
- for (i = 0; i < [array count]; i++)
- {
- NSRect rect = [[array objectAtIndex:i] frame];
- height = MAX (height, rect.size.height);
- }
+ return g_strdup (display_name);
+}
- GDK_QUARTZ_RELEASE_POOL;
+static gint
+gdk_quartz_screen_get_width (GdkScreen *screen)
+{
+ return GDK_QUARTZ_SCREEN (screen)->width;
+}
- return height;
+static gint
+gdk_quartz_screen_get_height (GdkScreen *screen)
+{
+ return GDK_QUARTZ_SCREEN (screen)->height;
}
static gint
return (pixels / dpi) * 25.4;
}
-gint
-gdk_screen_get_width_mm (GdkScreen *screen)
+static NSScreen *
+get_nsscreen_for_monitor (gint monitor_num)
{
- int i;
- gint width;
NSArray *array;
-
- g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+ NSScreen *screen;
GDK_QUARTZ_ALLOC_POOL;
- array = [NSScreen screens];
- width = 0;
- for (i = 0; i < [array count]; i++)
- {
- NSScreen *screen = [array objectAtIndex:i];
- NSRect rect = [screen frame];
- width += get_mm_from_pixels (screen, rect.size.width);
- }
+ array = [NSScreen screens];
+ screen = [array objectAtIndex:monitor_num];
GDK_QUARTZ_RELEASE_POOL;
- return width;
+ return screen;
}
-gint
-gdk_screen_get_height_mm (GdkScreen *screen)
+static gint
+gdk_quartz_screen_get_width_mm (GdkScreen *screen)
{
- int i;
- gint height;
- NSArray *array;
-
- g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
-
- GDK_QUARTZ_ALLOC_POOL;
- array = [NSScreen screens];
-
- height = 0;
- for (i = 0; i < [array count]; i++)
- {
- NSScreen *screen = [array objectAtIndex:i];
- NSRect rect = [screen frame];
- gint h = get_mm_from_pixels (screen, rect.size.height);
- height = MAX (height, h);
- }
-
- GDK_QUARTZ_RELEASE_POOL;
-
- return height;
+ return get_mm_from_pixels (get_nsscreen_for_monitor (0),
+ GDK_QUARTZ_SCREEN (screen)->width);
}
-int
-gdk_screen_get_n_monitors (GdkScreen *screen)
+static gint
+gdk_quartz_screen_get_height_mm (GdkScreen *screen)
{
- g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
-
- return n_screens;
+ return get_mm_from_pixels (get_nsscreen_for_monitor (0),
+ GDK_QUARTZ_SCREEN (screen)->height);
}
-static NSScreen *
-get_nsscreen_for_monitor (gint monitor_num)
+static gint
+gdk_quartz_screen_get_n_monitors (GdkScreen *screen)
{
- NSArray *array;
- NSScreen *screen;
-
- GDK_QUARTZ_ALLOC_POOL;
-
- array = [NSScreen screens];
- screen = [array objectAtIndex:monitor_num];
-
- GDK_QUARTZ_RELEASE_POOL;
-
- return screen;
+ return GDK_QUARTZ_SCREEN (screen)->n_screens;
}
-gint
-gdk_screen_get_monitor_width_mm (GdkScreen *screen,
- gint monitor_num)
+static gint
+gdk_quartz_screen_get_primary_monitor (GdkScreen *screen)
{
- g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
- g_return_val_if_fail (monitor_num < gdk_screen_get_n_monitors (screen), 0);
- g_return_val_if_fail (monitor_num >= 0, 0);
+ return 0;
+}
+static gint
+gdk_quartz_screen_get_monitor_width_mm (GdkScreen *screen,
+ gint monitor_num)
+{
return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
- screen_rects[monitor_num].width);
+ GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].width);
}
-gint
-gdk_screen_get_monitor_height_mm (GdkScreen *screen,
- gint monitor_num)
+static gint
+gdk_quartz_screen_get_monitor_height_mm (GdkScreen *screen,
+ gint monitor_num)
{
- g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
- g_return_val_if_fail (monitor_num < gdk_screen_get_n_monitors (screen), 0);
- g_return_val_if_fail (monitor_num >= 0, 0);
-
return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
- screen_rects[monitor_num].height);
+ GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].height);
}
-gchar *
-gdk_screen_get_monitor_plug_name (GdkScreen *screen,
- gint monitor_num)
+static gchar *
+gdk_quartz_screen_get_monitor_plug_name (GdkScreen *screen,
+ gint monitor_num)
{
/* FIXME: Is there some useful name we could use here? */
return NULL;
}
-void
-gdk_screen_get_monitor_geometry (GdkScreen *screen,
- gint monitor_num,
- GdkRectangle *dest)
+static void
+gdk_quartz_screen_get_monitor_geometry (GdkScreen *screen,
+ gint monitor_num,
+ GdkRectangle *dest)
+{
+ *dest = GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num];
+}
+
+static void
+gdk_quartz_screen_get_monitor_workarea (GdkScreen *screen,
+ gint monitor_num,
+ GdkRectangle *dest)
{
- g_return_if_fail (GDK_IS_SCREEN (screen));
- g_return_if_fail (monitor_num < gdk_screen_get_n_monitors (screen));
- g_return_if_fail (monitor_num >= 0);
+ GdkQuartzScreen *quartz_screen = GDK_QUARTZ_SCREEN (screen);
+ NSArray *array;
+ NSScreen *nsscreen;
+ NSRect rect;
- *dest = screen_rects[monitor_num];
+ GDK_QUARTZ_ALLOC_POOL;
+
+ array = [NSScreen screens];
+ nsscreen = [array objectAtIndex:monitor_num];
+ rect = [nsscreen visibleFrame];
+
+ dest->x = rect.origin.x - quartz_screen->min_x;
+ dest->y = quartz_screen->height - (rect.origin.y + rect.size.height) + quartz_screen->min_y;
+ dest->width = rect.size.width;
+ dest->height = rect.size.height;
+
+ GDK_QUARTZ_RELEASE_POOL;
}
-gchar *
-gdk_screen_make_display_name (GdkScreen *screen)
+static gchar *
+gdk_quartz_screen_make_display_name (GdkScreen *screen)
{
return g_strdup (gdk_display_get_name (_gdk_display));
}
-GdkWindow *
-gdk_screen_get_active_window (GdkScreen *screen)
+static GdkWindow *
+gdk_quartz_screen_get_active_window (GdkScreen *screen)
{
- g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
-
return NULL;
}
-GList *
-gdk_screen_get_window_stack (GdkScreen *screen)
+static GList *
+gdk_quartz_screen_get_window_stack (GdkScreen *screen)
{
- g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
-
return NULL;
}
-gboolean
-gdk_screen_is_composited (GdkScreen *screen)
+static gboolean
+gdk_quartz_screen_is_composited (GdkScreen *screen)
{
- g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
-
return TRUE;
}
+
+static void
+gdk_quartz_screen_class_init (GdkQuartzScreenClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdkScreenClass *screen_class = GDK_SCREEN_CLASS (klass);
+
+ object_class->dispose = gdk_quartz_screen_dispose;
+ object_class->finalize = gdk_quartz_screen_finalize;
+
+ screen_class->get_display = gdk_quartz_screen_get_display;
+ screen_class->get_width = gdk_quartz_screen_get_width;
+ screen_class->get_height = gdk_quartz_screen_get_height;
+ screen_class->get_width_mm = gdk_quartz_screen_get_width_mm;
+ screen_class->get_height_mm = gdk_quartz_screen_get_height_mm;
+ screen_class->get_number = gdk_quartz_screen_get_number;
+ screen_class->get_root_window = gdk_quartz_screen_get_root_window;
+ screen_class->get_n_monitors = gdk_quartz_screen_get_n_monitors;
+ screen_class->get_primary_monitor = gdk_quartz_screen_get_primary_monitor;
+ screen_class->get_monitor_width_mm = gdk_quartz_screen_get_monitor_width_mm;
+ screen_class->get_monitor_height_mm = gdk_quartz_screen_get_monitor_height_mm;
+ screen_class->get_monitor_plug_name = gdk_quartz_screen_get_monitor_plug_name;
+ screen_class->get_monitor_geometry = gdk_quartz_screen_get_monitor_geometry;
+ screen_class->get_monitor_workarea = gdk_quartz_screen_get_monitor_workarea;
+ screen_class->is_composited = gdk_quartz_screen_is_composited;
+ screen_class->make_display_name = gdk_quartz_screen_make_display_name;
+ screen_class->get_active_window = gdk_quartz_screen_get_active_window;
+ screen_class->get_window_stack = gdk_quartz_screen_get_window_stack;
+ screen_class->broadcast_client_message = _gdk_quartz_screen_broadcast_client_message;
+ screen_class->get_setting = _gdk_quartz_screen_get_setting;
+ screen_class->get_rgba_visual = _gdk_quartz_screen_get_rgba_visual;
+ screen_class->get_system_visual = _gdk_quartz_screen_get_system_visual;
+ screen_class->visual_get_best_depth = _gdk_quartz_screen_visual_get_best_depth;
+ screen_class->visual_get_best_type = _gdk_quartz_screen_visual_get_best_type;
+ screen_class->visual_get_best = _gdk_quartz_screen_visual_get_best;
+ screen_class->visual_get_best_with_depth = _gdk_quartz_screen_visual_get_best_with_depth;
+ screen_class->visual_get_best_with_type = _gdk_quartz_screen_visual_get_best_with_type;
+ screen_class->visual_get_best_with_both = _gdk_quartz_screen_visual_get_best_with_both;
+ screen_class->query_depths = _gdk_quartz_screen_query_depths;
+ screen_class->query_visual_types = _gdk_quartz_screen_query_visual_types;
+ screen_class->list_visuals = _gdk_quartz_screen_list_visuals;
+}