mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 13:41:07 +00:00
1186 lines
32 KiB
C
1186 lines
32 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <AppKit/AppKit.h>
|
|
#include <float.h>
|
|
#include <gdk/gdk.h>
|
|
|
|
#import "GdkMacosView.h"
|
|
|
|
#include "gdkmacossurface-private.h"
|
|
|
|
#include "gdkdebugprivate.h"
|
|
#include "gdkdeviceprivate.h"
|
|
#include "gdkdisplay.h"
|
|
#include "gdkeventsprivate.h"
|
|
#include "gdkframeclockprivate.h"
|
|
#include "gdkseatprivate.h"
|
|
#include "gdksurfaceprivate.h"
|
|
|
|
#include "gdkmacosdevice.h"
|
|
#include "gdkmacosdevice-private.h"
|
|
#include "gdkmacosdisplay-private.h"
|
|
#include "gdkmacosdrag-private.h"
|
|
#include "gdkmacosdragsurface-private.h"
|
|
#include "gdkmacosglcontext-private.h"
|
|
#include "gdkmacosmonitor-private.h"
|
|
#include "gdkmacospopupsurface-private.h"
|
|
#include "gdkmacosutils-private.h"
|
|
#include "gdkmacostoplevelsurface-private.h"
|
|
|
|
G_DEFINE_ABSTRACT_TYPE (GdkMacosSurface, gdk_macos_surface, GDK_TYPE_SURFACE)
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_NATIVE,
|
|
LAST_PROP
|
|
};
|
|
|
|
static GParamSpec *properties [LAST_PROP];
|
|
|
|
void
|
|
_gdk_macos_surface_request_frame (GdkMacosSurface *self)
|
|
{
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (self->awaiting_frame)
|
|
return;
|
|
|
|
if (self->best_monitor != NULL)
|
|
{
|
|
self->awaiting_frame = TRUE;
|
|
_gdk_macos_monitor_add_frame_callback (GDK_MACOS_MONITOR (self->best_monitor), self);
|
|
gdk_surface_freeze_updates (GDK_SURFACE (self));
|
|
}
|
|
}
|
|
|
|
static void
|
|
_gdk_macos_surface_cancel_frame (GdkMacosSurface *self)
|
|
{
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (!self->awaiting_frame)
|
|
return;
|
|
|
|
if (self->best_monitor != NULL)
|
|
{
|
|
self->awaiting_frame = FALSE;
|
|
_gdk_macos_monitor_remove_frame_callback (GDK_MACOS_MONITOR (self->best_monitor), self);
|
|
gdk_surface_thaw_updates (GDK_SURFACE (self));
|
|
}
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_frame_presented (GdkMacosSurface *self,
|
|
gint64 presentation_time,
|
|
gint64 refresh_interval)
|
|
{
|
|
GdkFrameTimings *timings;
|
|
GdkFrameClock *frame_clock;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
self->awaiting_frame = FALSE;
|
|
|
|
if (GDK_SURFACE_DESTROYED (self))
|
|
return;
|
|
|
|
frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self));
|
|
|
|
if (self->pending_frame_counter)
|
|
{
|
|
timings = gdk_frame_clock_get_timings (frame_clock, self->pending_frame_counter);
|
|
|
|
if (timings != NULL)
|
|
{
|
|
timings->presentation_time = presentation_time - refresh_interval;
|
|
timings->complete = TRUE;
|
|
}
|
|
|
|
self->pending_frame_counter = 0;
|
|
}
|
|
|
|
timings = gdk_frame_clock_get_current_timings (frame_clock);
|
|
|
|
if (timings != NULL)
|
|
{
|
|
timings->refresh_interval = refresh_interval;
|
|
timings->predicted_presentation_time = presentation_time;
|
|
}
|
|
|
|
if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self)))
|
|
gdk_surface_thaw_updates (GDK_SURFACE (self));
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_reposition_children (GdkMacosSurface *self)
|
|
{
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
|
|
if (GDK_SURFACE_DESTROYED (self))
|
|
return;
|
|
|
|
for (const GList *iter = GDK_SURFACE (self)->children;
|
|
iter != NULL;
|
|
iter = iter->next)
|
|
{
|
|
GdkMacosSurface *child = iter->data;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (child));
|
|
|
|
if (GDK_IS_MACOS_POPUP_SURFACE (child))
|
|
_gdk_macos_popup_surface_reposition (GDK_MACOS_POPUP_SURFACE (child));
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_set_input_region (GdkSurface *surface,
|
|
cairo_region_t *region)
|
|
{
|
|
GdkMacosSurface *self = (GdkMacosSurface *)surface;
|
|
cairo_rectangle_int_t rect;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (self->window == NULL)
|
|
return;
|
|
|
|
cairo_region_get_extents (region, &rect);
|
|
|
|
[(GdkMacosBaseView *)[self->window contentView] setInputArea:&rect];
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_set_opaque_region (GdkSurface *surface,
|
|
cairo_region_t *region)
|
|
{
|
|
GdkMacosSurface *self = (GdkMacosSurface *)surface;
|
|
NSView *nsview;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if ((nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface))))
|
|
[(GdkMacosView *)nsview setOpaqueRegion:region];
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_hide (GdkSurface *surface)
|
|
{
|
|
GdkMacosSurface *self = (GdkMacosSurface *)surface;
|
|
GdkSeat *seat;
|
|
gboolean was_key;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
self->show_on_next_swap = FALSE;
|
|
|
|
_gdk_macos_surface_cancel_frame (self);
|
|
|
|
was_key = [self->window isKeyWindow];
|
|
|
|
seat = gdk_display_get_default_seat (surface->display);
|
|
gdk_seat_ungrab (seat);
|
|
|
|
[self->window hide];
|
|
|
|
_gdk_surface_clear_update_area (surface);
|
|
|
|
g_clear_object (&self->buffer);
|
|
g_clear_object (&self->front);
|
|
|
|
if (was_key)
|
|
{
|
|
GdkSurface *parent;
|
|
|
|
if (GDK_IS_TOPLEVEL (surface))
|
|
parent = surface->transient_for;
|
|
else
|
|
parent = surface->parent;
|
|
|
|
/* Return key input to the parent window if necessary */
|
|
if (parent != NULL && GDK_SURFACE_IS_MAPPED (parent))
|
|
{
|
|
GdkMacosWindow *parentWindow = GDK_MACOS_SURFACE (parent)->window;
|
|
|
|
[parentWindow showAndMakeKey:YES];
|
|
}
|
|
}
|
|
}
|
|
|
|
static double
|
|
gdk_macos_surface_get_scale (GdkSurface *surface)
|
|
{
|
|
GdkMacosSurface *self = (GdkMacosSurface *)surface;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
return [self->window backingScaleFactor];
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_begin_frame (GdkMacosSurface *self)
|
|
{
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
self->in_frame = TRUE;
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_end_frame (GdkMacosSurface *self)
|
|
{
|
|
GdkFrameTimings *timings;
|
|
GdkFrameClock *frame_clock;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (GDK_SURFACE_DESTROYED (self))
|
|
return;
|
|
|
|
frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self));
|
|
|
|
if ((timings = gdk_frame_clock_get_current_timings (frame_clock)))
|
|
self->pending_frame_counter = timings->frame_counter;
|
|
|
|
self->in_frame = FALSE;
|
|
|
|
_gdk_macos_surface_request_frame (self);
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_before_paint (GdkMacosSurface *self,
|
|
GdkFrameClock *frame_clock)
|
|
{
|
|
GdkSurface *surface = (GdkSurface *)self;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
g_assert (GDK_IS_FRAME_CLOCK (frame_clock));
|
|
|
|
if (GDK_SURFACE_DESTROYED (self))
|
|
return;
|
|
|
|
if (surface->update_freeze_count == 0)
|
|
gdk_macos_surface_begin_frame (self);
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_after_paint (GdkMacosSurface *self,
|
|
GdkFrameClock *frame_clock)
|
|
{
|
|
GdkSurface *surface = (GdkSurface *)self;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
g_assert (GDK_IS_FRAME_CLOCK (frame_clock));
|
|
|
|
if (GDK_SURFACE_DESTROYED (self))
|
|
return;
|
|
|
|
if (surface->update_freeze_count == 0)
|
|
gdk_macos_surface_end_frame (self);
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_get_root_coords (GdkSurface *surface,
|
|
int x,
|
|
int y,
|
|
int *root_x,
|
|
int *root_y)
|
|
{
|
|
GdkMacosSurface *self = (GdkMacosSurface *)surface;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (root_x)
|
|
*root_x = self->root_x + x;
|
|
|
|
if (root_y)
|
|
*root_y = self->root_y + y;
|
|
}
|
|
|
|
static gboolean
|
|
gdk_macos_surface_get_device_state (GdkSurface *surface,
|
|
GdkDevice *device,
|
|
double *x,
|
|
double *y,
|
|
GdkModifierType *mask)
|
|
{
|
|
GdkDisplay *display;
|
|
NSWindow *nswindow;
|
|
NSPoint point;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (surface));
|
|
g_assert (GDK_IS_MACOS_DEVICE (device));
|
|
g_assert (x != NULL);
|
|
g_assert (y != NULL);
|
|
g_assert (mask != NULL);
|
|
|
|
if (GDK_SURFACE_DESTROYED (surface))
|
|
return FALSE;
|
|
|
|
display = gdk_surface_get_display (surface);
|
|
nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
|
|
point = [nswindow mouseLocationOutsideOfEventStream];
|
|
|
|
*mask = _gdk_macos_display_get_current_keyboard_modifiers (GDK_MACOS_DISPLAY (display))
|
|
| _gdk_macos_display_get_current_mouse_modifiers (GDK_MACOS_DISPLAY (display));
|
|
|
|
*x = point.x;
|
|
*y = surface->height - point.y;
|
|
|
|
return *x >= 0 && *y >= 0 && *x < surface->width && *y < surface->height;
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_get_geometry (GdkSurface *surface,
|
|
int *x,
|
|
int *y,
|
|
int *width,
|
|
int *height)
|
|
{
|
|
g_assert (GDK_IS_MACOS_SURFACE (surface));
|
|
|
|
if (x != NULL)
|
|
*x = surface->x;
|
|
|
|
if (y != NULL)
|
|
*y = surface->y;
|
|
|
|
if (width != NULL)
|
|
*width = surface->width;
|
|
|
|
if (height != NULL)
|
|
*height = surface->height;
|
|
}
|
|
|
|
static GdkDrag *
|
|
gdk_macos_surface_drag_begin (GdkSurface *surface,
|
|
GdkDevice *device,
|
|
GdkContentProvider *content,
|
|
GdkDragAction actions,
|
|
double dx,
|
|
double dy)
|
|
{
|
|
GdkMacosSurface *self = (GdkMacosSurface *)surface;
|
|
GdkMacosSurface *drag_surface;
|
|
GdkMacosDrag *drag;
|
|
GdkCursor *cursor;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
g_assert (GDK_IS_MACOS_TOPLEVEL_SURFACE (self) ||
|
|
GDK_IS_MACOS_POPUP_SURFACE (self));
|
|
g_assert (GDK_IS_MACOS_DEVICE (device));
|
|
g_assert (GDK_IS_CONTENT_PROVIDER (content));
|
|
|
|
drag_surface = _gdk_macos_drag_surface_new (GDK_MACOS_DISPLAY (surface->display));
|
|
drag = g_object_new (GDK_TYPE_MACOS_DRAG,
|
|
"drag-surface", drag_surface,
|
|
"surface", surface,
|
|
"device", device,
|
|
"content", content,
|
|
"actions", actions,
|
|
NULL);
|
|
g_clear_object (&drag_surface);
|
|
|
|
cursor = gdk_drag_get_cursor (GDK_DRAG (drag),
|
|
gdk_drag_get_selected_action (GDK_DRAG (drag)));
|
|
gdk_drag_set_cursor (GDK_DRAG (drag), cursor);
|
|
|
|
if (!_gdk_macos_drag_begin (drag, content, self->window))
|
|
{
|
|
g_object_unref (drag);
|
|
return NULL;
|
|
}
|
|
|
|
/* Hold a reference until drop_done is called */
|
|
g_object_ref (drag);
|
|
|
|
return GDK_DRAG (g_steal_pointer (&drag));
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_destroy (GdkSurface *surface,
|
|
gboolean foreign_destroy)
|
|
{
|
|
GDK_BEGIN_MACOS_ALLOC_POOL;
|
|
|
|
GdkMacosSurface *self = (GdkMacosSurface *)surface;
|
|
GdkMacosWindow *window = g_steal_pointer (&self->window);
|
|
GdkFrameClock *frame_clock;
|
|
|
|
_gdk_macos_surface_cancel_frame (self);
|
|
g_clear_object (&self->best_monitor);
|
|
|
|
if ((frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self))))
|
|
{
|
|
g_signal_handlers_disconnect_by_func (frame_clock,
|
|
G_CALLBACK (gdk_macos_surface_before_paint),
|
|
self);
|
|
g_signal_handlers_disconnect_by_func (frame_clock,
|
|
G_CALLBACK (gdk_macos_surface_after_paint),
|
|
self);
|
|
}
|
|
|
|
g_clear_pointer (&self->title, g_free);
|
|
|
|
if (window != NULL)
|
|
[window close];
|
|
|
|
_gdk_macos_display_surface_removed (GDK_MACOS_DISPLAY (surface->display), self);
|
|
|
|
g_clear_pointer (&self->monitors, g_ptr_array_unref);
|
|
|
|
g_clear_object (&self->buffer);
|
|
g_clear_object (&self->front);
|
|
|
|
g_assert (self->sorted.prev == NULL);
|
|
g_assert (self->sorted.next == NULL);
|
|
g_assert (self->frame.prev == NULL);
|
|
g_assert (self->frame.next == NULL);
|
|
g_assert (self->main.prev == NULL);
|
|
g_assert (self->main.next == NULL);
|
|
|
|
GDK_END_MACOS_ALLOC_POOL;
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_constructed (GObject *object)
|
|
{
|
|
GdkMacosSurface *self = (GdkMacosSurface *)object;
|
|
GdkFrameClock *frame_clock;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
G_OBJECT_CLASS (gdk_macos_surface_parent_class)->constructed (object);
|
|
|
|
if ((frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self))))
|
|
{
|
|
g_signal_connect_object (frame_clock,
|
|
"before-paint",
|
|
G_CALLBACK (gdk_macos_surface_before_paint),
|
|
self,
|
|
G_CONNECT_SWAPPED);
|
|
g_signal_connect_object (frame_clock,
|
|
"after-paint",
|
|
G_CALLBACK (gdk_macos_surface_after_paint),
|
|
self,
|
|
G_CONNECT_SWAPPED);
|
|
}
|
|
|
|
gdk_surface_freeze_updates (GDK_SURFACE (self));
|
|
_gdk_macos_surface_monitor_changed (self);
|
|
|
|
if (self->window != NULL)
|
|
_gdk_macos_surface_configure (self);
|
|
|
|
_gdk_macos_display_surface_added (GDK_MACOS_DISPLAY (gdk_surface_get_display (GDK_SURFACE (self))),
|
|
self);
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GdkMacosSurface *self = GDK_MACOS_SURFACE (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_NATIVE:
|
|
g_value_set_pointer (value, self->window);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_class_init (GdkMacosSurfaceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GdkSurfaceClass *surface_class = GDK_SURFACE_CLASS (klass);
|
|
|
|
object_class->constructed = gdk_macos_surface_constructed;
|
|
object_class->get_property = gdk_macos_surface_get_property;
|
|
|
|
surface_class->destroy = gdk_macos_surface_destroy;
|
|
surface_class->drag_begin = gdk_macos_surface_drag_begin;
|
|
surface_class->get_device_state = gdk_macos_surface_get_device_state;
|
|
surface_class->get_geometry = gdk_macos_surface_get_geometry;
|
|
surface_class->get_root_coords = gdk_macos_surface_get_root_coords;
|
|
surface_class->get_scale = gdk_macos_surface_get_scale;
|
|
surface_class->hide = gdk_macos_surface_hide;
|
|
surface_class->set_input_region = gdk_macos_surface_set_input_region;
|
|
surface_class->set_opaque_region = gdk_macos_surface_set_opaque_region;
|
|
|
|
/**
|
|
* GdkMacosSurface:native: (attributes org.gtk.Property.get=gdk_macos_surface_get_native_window)
|
|
*
|
|
* The "native" property contains the underlying NSWindow.
|
|
*/
|
|
properties [PROP_NATIVE] =
|
|
g_param_spec_pointer ("native", NULL, NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (object_class, LAST_PROP, properties);
|
|
}
|
|
|
|
static void
|
|
gdk_macos_surface_init (GdkMacosSurface *self)
|
|
{
|
|
self->frame.data = self;
|
|
self->main.data = self;
|
|
self->sorted.data = self;
|
|
self->monitors = g_ptr_array_new_with_free_func (g_object_unref);
|
|
}
|
|
|
|
gboolean
|
|
_gdk_macos_surface_is_opaque (GdkMacosSurface *self)
|
|
{
|
|
GdkSurface *surface = (GdkSurface *)self;
|
|
|
|
g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), FALSE);
|
|
|
|
if (surface->opaque_region != NULL &&
|
|
cairo_region_num_rectangles (surface->opaque_region) == 1)
|
|
{
|
|
cairo_rectangle_int_t extents;
|
|
|
|
cairo_region_get_extents (surface->opaque_region, &extents);
|
|
|
|
return (extents.x == 0 &&
|
|
extents.y == 0 &&
|
|
extents.width == GDK_SURFACE (self)->width &&
|
|
extents.height == GDK_SURFACE (self)->height);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
const char *
|
|
_gdk_macos_surface_get_title (GdkMacosSurface *self)
|
|
{
|
|
|
|
return self->title;
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_set_title (GdkMacosSurface *self,
|
|
const char *title)
|
|
{
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (title == NULL)
|
|
title = "";
|
|
|
|
if (g_strcmp0 (self->title, title) != 0)
|
|
{
|
|
g_free (self->title);
|
|
self->title = g_strdup (title);
|
|
|
|
GDK_BEGIN_MACOS_ALLOC_POOL;
|
|
[self->window setTitle:[NSString stringWithUTF8String:title]];
|
|
GDK_END_MACOS_ALLOC_POOL;
|
|
}
|
|
}
|
|
|
|
CGDirectDisplayID
|
|
_gdk_macos_surface_get_screen_id (GdkMacosSurface *self)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), (CGDirectDisplayID)-1);
|
|
|
|
if (self->window != NULL)
|
|
{
|
|
NSScreen *screen = [self->window screen];
|
|
return [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
|
|
}
|
|
|
|
return (CGDirectDisplayID)-1;
|
|
}
|
|
|
|
NSWindow *
|
|
_gdk_macos_surface_get_native (GdkMacosSurface *self)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
|
|
|
|
return (NSWindow *)self->window;
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_set_native (GdkMacosSurface *self,
|
|
GdkMacosWindow *window)
|
|
{
|
|
g_assert (self->window == NULL);
|
|
|
|
self->window = window;
|
|
[self->window setGdkSurface:self];
|
|
}
|
|
|
|
/**
|
|
* gdk_macos_surface_get_native_window: (attributes org.gtk.Method.get_property=native)
|
|
* @self: a #GdkMacosSurface
|
|
*
|
|
* Gets the underlying NSWindow used by the surface.
|
|
*
|
|
* The NSWindow's contentView is an implementation detail and may change
|
|
* between releases of GTK.
|
|
*
|
|
* Returns: (nullable): a #NSWindow or %NULL
|
|
*
|
|
* Since: 4.8
|
|
*/
|
|
gpointer
|
|
gdk_macos_surface_get_native_window (GdkMacosSurface *self)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
|
|
|
|
return _gdk_macos_surface_get_native (self);
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_set_geometry_hints (GdkMacosSurface *self,
|
|
const GdkGeometry *geometry,
|
|
GdkSurfaceHints geom_mask)
|
|
{
|
|
NSSize max_size;
|
|
NSSize min_size;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
g_return_if_fail (geometry != NULL);
|
|
g_return_if_fail (self->window != NULL);
|
|
|
|
if (geom_mask & GDK_HINT_MAX_SIZE)
|
|
max_size = NSMakeSize (geometry->max_width, geometry->max_height);
|
|
else
|
|
max_size = NSMakeSize (FLT_MAX, FLT_MAX);
|
|
[self->window setContentMaxSize:max_size];
|
|
|
|
if (geom_mask & GDK_HINT_MIN_SIZE)
|
|
min_size = NSMakeSize (geometry->min_width, geometry->min_height);
|
|
else
|
|
min_size = NSMakeSize (1, 1);
|
|
[self->window setContentMinSize:min_size];
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_resize (GdkMacosSurface *self,
|
|
int width,
|
|
int height)
|
|
{
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
_gdk_macos_surface_move_resize (self, -1, -1, width, height);
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_update_fullscreen_state (GdkMacosSurface *self)
|
|
{
|
|
GdkToplevelState state;
|
|
gboolean is_fullscreen;
|
|
gboolean was_fullscreen;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
state = GDK_SURFACE (self)->state;
|
|
is_fullscreen = ([self->window styleMask] & NSWindowStyleMaskFullScreen) != 0;
|
|
was_fullscreen = (state & GDK_TOPLEVEL_STATE_FULLSCREEN) != 0;
|
|
|
|
if (is_fullscreen != was_fullscreen)
|
|
{
|
|
if (is_fullscreen)
|
|
gdk_synthesize_surface_state (GDK_SURFACE (self), 0, GDK_TOPLEVEL_STATE_FULLSCREEN);
|
|
else
|
|
gdk_synthesize_surface_state (GDK_SURFACE (self), GDK_TOPLEVEL_STATE_FULLSCREEN, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_configure (GdkMacosSurface *self)
|
|
{
|
|
GdkSurface *surface = (GdkSurface *)self;
|
|
GdkMacosDisplay *display;
|
|
GdkMacosSurface *parent;
|
|
NSRect frame_rect;
|
|
NSRect content_rect;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (GDK_SURFACE_DESTROYED (self))
|
|
return;
|
|
|
|
if (surface->parent != NULL)
|
|
parent = GDK_MACOS_SURFACE (surface->parent);
|
|
else if (surface->transient_for != NULL)
|
|
parent = GDK_MACOS_SURFACE (surface->transient_for);
|
|
else
|
|
parent = NULL;
|
|
|
|
display = GDK_MACOS_DISPLAY (GDK_SURFACE (self)->display);
|
|
frame_rect = [self->window frame];
|
|
content_rect = [self->window contentRectForFrameRect:frame_rect];
|
|
|
|
_gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display),
|
|
content_rect.origin.x,
|
|
content_rect.origin.y + content_rect.size.height,
|
|
&self->root_x, &self->root_y);
|
|
|
|
if (parent != NULL)
|
|
{
|
|
surface->x = self->root_x - parent->root_x;
|
|
surface->y = self->root_y - parent->root_y;
|
|
}
|
|
else
|
|
{
|
|
surface->x = self->root_x;
|
|
surface->y = self->root_y;
|
|
}
|
|
|
|
if (surface->width != content_rect.size.width ||
|
|
surface->height != content_rect.size.height)
|
|
{
|
|
surface->width = content_rect.size.width;
|
|
surface->height = content_rect.size.height;
|
|
|
|
g_clear_object (&self->buffer);
|
|
g_clear_object (&self->front);
|
|
|
|
_gdk_surface_update_size (surface);
|
|
gdk_surface_invalidate_rect (surface, NULL);
|
|
}
|
|
|
|
_gdk_macos_surface_reposition_children (self);
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_show (GdkMacosSurface *self)
|
|
{
|
|
gboolean was_mapped;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (GDK_SURFACE_DESTROYED (self))
|
|
return;
|
|
|
|
_gdk_macos_display_clear_sorting (GDK_MACOS_DISPLAY (GDK_SURFACE (self)->display));
|
|
self->show_on_next_swap = TRUE;
|
|
|
|
was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self));
|
|
|
|
if (!was_mapped)
|
|
{
|
|
gdk_surface_set_is_mapped (GDK_SURFACE (self), TRUE);
|
|
gdk_surface_request_layout (GDK_SURFACE (self));
|
|
gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL);
|
|
gdk_surface_thaw_updates (GDK_SURFACE (self));
|
|
}
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_synthesize_null_key (GdkMacosSurface *self)
|
|
{
|
|
GdkTranslatedKey translated = {0};
|
|
GdkTranslatedKey no_lock = {0};
|
|
GdkDisplay *display;
|
|
GdkEvent *event;
|
|
GdkSeat *seat;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
translated.keyval = GDK_KEY_VoidSymbol;
|
|
no_lock.keyval = GDK_KEY_VoidSymbol;
|
|
|
|
display = gdk_surface_get_display (GDK_SURFACE (self));
|
|
seat = gdk_display_get_default_seat (display);
|
|
event = gdk_key_event_new (GDK_KEY_PRESS,
|
|
GDK_SURFACE (self),
|
|
gdk_seat_get_keyboard (seat),
|
|
GDK_CURRENT_TIME,
|
|
0,
|
|
0,
|
|
FALSE,
|
|
&translated,
|
|
&no_lock,
|
|
NULL);
|
|
_gdk_event_queue_append (display, event);
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_move (GdkMacosSurface *self,
|
|
int x,
|
|
int y)
|
|
{
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
_gdk_macos_surface_move_resize (self, x, y, -1, -1);
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_move_resize (GdkMacosSurface *self,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
GdkSurface *surface = (GdkSurface *)self;
|
|
GdkDisplay *display;
|
|
NSRect content_rect;
|
|
NSRect frame_rect;
|
|
gboolean ignore_move;
|
|
gboolean ignore_size;
|
|
GdkRectangle current;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
/* Query for up-to-date values in case we're racing against
|
|
* an incoming frame notify which could be queued behind whatever
|
|
* we're processing right now.
|
|
*/
|
|
frame_rect = [self->window frame];
|
|
content_rect = [self->window contentRectForFrameRect:frame_rect];
|
|
_gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (GDK_SURFACE (self)->display),
|
|
content_rect.origin.x, content_rect.origin.y,
|
|
¤t.x, ¤t.y);
|
|
current.width = content_rect.size.width;
|
|
current.height = content_rect.size.height;
|
|
|
|
/* Check if we can ignore the operation all together */
|
|
ignore_move = (x == -1 || (x == current.x)) &&
|
|
(y == -1 || (y == current.y));
|
|
ignore_size = (width == -1 || (width == current.width)) &&
|
|
(height == -1 || (height == current.height));
|
|
|
|
if (ignore_move && ignore_size)
|
|
return;
|
|
|
|
display = gdk_surface_get_display (surface);
|
|
|
|
if (width == -1)
|
|
width = current.width;
|
|
|
|
if (height == -1)
|
|
height = current.height;
|
|
|
|
if (x == -1)
|
|
x = current.x;
|
|
|
|
if (y == -1)
|
|
y = current.y;
|
|
|
|
_gdk_macos_display_to_display_coords (GDK_MACOS_DISPLAY (display),
|
|
x, y + height,
|
|
&x, &y);
|
|
|
|
if (!ignore_move)
|
|
content_rect.origin = NSMakePoint (x, y);
|
|
|
|
if (!ignore_size)
|
|
content_rect.size = NSMakeSize (width, height);
|
|
|
|
frame_rect = [self->window frameRectForContentRect:content_rect];
|
|
[self->window setFrame:frame_rect display:NO];
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_user_resize (GdkMacosSurface *self,
|
|
CGRect new_frame)
|
|
{
|
|
GdkMacosDisplay *display;
|
|
CGRect content_rect;
|
|
int root_x, root_y;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
g_return_if_fail (GDK_IS_TOPLEVEL (self));
|
|
|
|
if (GDK_SURFACE_DESTROYED (self))
|
|
return;
|
|
|
|
display = GDK_MACOS_DISPLAY (GDK_SURFACE (self)->display);
|
|
content_rect = [self->window contentRectForFrameRect:new_frame];
|
|
|
|
_gdk_macos_display_from_display_coords (display,
|
|
new_frame.origin.x,
|
|
new_frame.origin.y + new_frame.size.height,
|
|
&root_x, &root_y);
|
|
|
|
self->next_layout.root_x = root_x;
|
|
self->next_layout.root_y = root_y;
|
|
self->next_layout.width = content_rect.size.width;
|
|
self->next_layout.height = content_rect.size.height;
|
|
|
|
gdk_surface_request_layout (GDK_SURFACE (self));
|
|
}
|
|
|
|
gboolean
|
|
_gdk_macos_surface_is_tracking (GdkMacosSurface *self,
|
|
NSTrackingArea *area)
|
|
{
|
|
GdkMacosBaseView *view;
|
|
|
|
g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), FALSE);
|
|
|
|
if (self->window == NULL)
|
|
return FALSE;
|
|
|
|
view = (GdkMacosBaseView *)[self->window contentView];
|
|
if (view == NULL)
|
|
return FALSE;
|
|
|
|
return [view trackingArea] == area;
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_monitor_changed (GdkMacosSurface *self)
|
|
{
|
|
GListModel *monitors;
|
|
GdkMonitor *best = NULL;
|
|
GdkRectangle rect;
|
|
GdkRectangle intersect;
|
|
GdkDisplay *display;
|
|
GdkMonitor *monitor;
|
|
guint n_monitors;
|
|
int best_area = 0;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (self->in_change_monitor)
|
|
return;
|
|
|
|
self->in_change_monitor = TRUE;
|
|
|
|
_gdk_macos_surface_cancel_frame (self);
|
|
_gdk_macos_surface_configure (self);
|
|
|
|
rect.x = self->root_x;
|
|
rect.y = self->root_y;
|
|
rect.width = GDK_SURFACE (self)->width;
|
|
rect.height = GDK_SURFACE (self)->height;
|
|
|
|
for (guint i = self->monitors->len; i > 0; i--)
|
|
{
|
|
monitor = g_ptr_array_index (self->monitors, i-1);
|
|
|
|
if (!gdk_rectangle_intersect (&monitor->geometry, &rect, &intersect))
|
|
{
|
|
g_object_ref (monitor);
|
|
g_ptr_array_remove_index (self->monitors, i-1);
|
|
gdk_surface_leave_monitor (GDK_SURFACE (self), monitor);
|
|
g_object_unref (monitor);
|
|
}
|
|
}
|
|
|
|
display = gdk_surface_get_display (GDK_SURFACE (self));
|
|
monitors = gdk_display_get_monitors (display);
|
|
n_monitors = g_list_model_get_n_items (monitors);
|
|
|
|
for (guint i = 0; i < n_monitors; i++)
|
|
{
|
|
monitor = g_list_model_get_item (monitors, i);
|
|
|
|
if (!g_ptr_array_find (self->monitors, monitor, NULL))
|
|
{
|
|
gdk_surface_enter_monitor (GDK_SURFACE (self), monitor);
|
|
g_ptr_array_add (self->monitors, g_object_ref (monitor));
|
|
}
|
|
|
|
g_object_unref (monitor);
|
|
}
|
|
|
|
/* We need to create a new IOSurface for this monitor */
|
|
g_clear_object (&self->buffer);
|
|
g_clear_object (&self->front);
|
|
|
|
/* Determine the best-fit monitor */
|
|
for (guint i = 0; i < self->monitors->len; i++)
|
|
{
|
|
monitor = g_ptr_array_index (self->monitors, i);
|
|
|
|
if (gdk_rectangle_intersect (&monitor->geometry, &rect, &intersect))
|
|
{
|
|
int area = intersect.width * intersect.height;
|
|
|
|
if (area > best_area)
|
|
{
|
|
best_area = area;
|
|
best = monitor;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_set_object (&self->best_monitor, best))
|
|
{
|
|
GDK_DEBUG (MISC, "Surface \"%s\" moved to monitor \"%s\"",
|
|
self->title ? self->title : "unknown",
|
|
gdk_monitor_get_connector (best));
|
|
|
|
_gdk_macos_surface_configure (self);
|
|
|
|
if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self)))
|
|
{
|
|
_gdk_macos_surface_request_frame (self);
|
|
gdk_surface_request_layout (GDK_SURFACE (self));
|
|
}
|
|
|
|
for (const GList *iter = GDK_SURFACE (self)->children;
|
|
iter != NULL;
|
|
iter = iter->next)
|
|
{
|
|
GdkMacosSurface *child = iter->data;
|
|
GdkRectangle area;
|
|
|
|
g_set_object (&child->best_monitor, best);
|
|
|
|
area.x = self->root_x + GDK_SURFACE (child)->x;
|
|
area.y = self->root_y + GDK_SURFACE (child)->y;
|
|
area.width = GDK_SURFACE (child)->width;
|
|
area.height = GDK_SURFACE (child)->height;
|
|
|
|
_gdk_macos_monitor_clamp (GDK_MACOS_MONITOR (best), &area);
|
|
_gdk_macos_surface_move (child, area.x, area.y);
|
|
gdk_surface_invalidate_rect (GDK_SURFACE (child), NULL);
|
|
}
|
|
}
|
|
|
|
gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL);
|
|
|
|
self->in_change_monitor = FALSE;
|
|
}
|
|
|
|
GdkMonitor *
|
|
_gdk_macos_surface_get_best_monitor (GdkMacosSurface *self)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
|
|
|
|
return self->best_monitor;
|
|
}
|
|
|
|
NSView *
|
|
_gdk_macos_surface_get_view (GdkMacosSurface *self)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
|
|
|
|
if (self->window == NULL)
|
|
return NULL;
|
|
|
|
return [self->window contentView];
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_set_opacity (GdkMacosSurface *self,
|
|
double opacity)
|
|
{
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
if (self->window != NULL)
|
|
[self->window setAlphaValue:opacity];
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_get_root_coords (GdkMacosSurface *self,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
GdkSurface *surface;
|
|
int out_x = 0;
|
|
int out_y = 0;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
for (surface = GDK_SURFACE (self); surface; surface = surface->parent)
|
|
{
|
|
out_x += surface->x;
|
|
out_y += surface->y;
|
|
}
|
|
|
|
if (x)
|
|
*x = out_x;
|
|
|
|
if (y)
|
|
*y = out_y;
|
|
}
|
|
|
|
GdkMacosBuffer *
|
|
_gdk_macos_surface_get_buffer (GdkMacosSurface *self)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
|
|
|
|
if (GDK_SURFACE_DESTROYED (self))
|
|
return NULL;
|
|
|
|
if (self->buffer == NULL)
|
|
{
|
|
/* Create replacement buffer. We always use 4-byte and 32-bit BGRA for
|
|
* our surface as that can work with both Cairo and GL. The GdkMacosTile
|
|
* handles opaque regions for the compositor, so using 3-byte/24-bit is
|
|
* not a necessary optimization.
|
|
*/
|
|
double scale = gdk_surface_get_scale_factor (GDK_SURFACE (self));
|
|
guint width = GDK_SURFACE (self)->width * scale;
|
|
guint height = GDK_SURFACE (self)->height * scale;
|
|
|
|
self->buffer = _gdk_macos_buffer_new (width, height, scale, 4, 32);
|
|
}
|
|
|
|
return self->buffer;
|
|
}
|
|
|
|
static void
|
|
_gdk_macos_surface_do_delayed_show (GdkMacosSurface *self)
|
|
{
|
|
GdkSurface *surface = (GdkSurface *)self;
|
|
|
|
g_assert (GDK_IS_MACOS_SURFACE (self));
|
|
|
|
self->show_on_next_swap = FALSE;
|
|
[self->window showAndMakeKey:YES];
|
|
|
|
_gdk_macos_display_clear_sorting (GDK_MACOS_DISPLAY (surface->display));
|
|
gdk_surface_request_motion (surface);
|
|
}
|
|
|
|
void
|
|
_gdk_macos_surface_swap_buffers (GdkMacosSurface *self,
|
|
const cairo_region_t *damage)
|
|
{
|
|
GdkMacosBuffer *swap;
|
|
|
|
g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
|
|
g_return_if_fail (damage != NULL);
|
|
|
|
swap = self->buffer;
|
|
self->buffer = self->front;
|
|
self->front = swap;
|
|
|
|
/* This code looks like it swaps buffers, but since the IOSurfaceRef
|
|
* appears to be retained on the other side, we really just ask all
|
|
* of the GdkMacosTile CALayer's to update their contents.
|
|
*/
|
|
[self->window swapBuffer:swap withDamage:damage];
|
|
|
|
/* We might have delayed actually showing the window until the buffer
|
|
* contents are ready to be displayed. Doing so ensures that we don't
|
|
* get a point where we might have invalid buffer contents before we
|
|
* have content to display to the user.
|
|
*/
|
|
if G_UNLIKELY (self->show_on_next_swap)
|
|
_gdk_macos_surface_do_delayed_show (self);
|
|
}
|