gtk/gdk/gdkdisplaymanager.c

476 lines
14 KiB
C
Raw Normal View History

/* GDK - The GIMP Drawing Kit
* Copyright (C) 2000 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 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
2012-02-27 13:01:10 +00:00
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
#include "gdkconfig.h"
#include "gdkdisplaymanagerprivate.h"
#include "gdkdisplayprivate.h"
#include "gdkkeysprivate.h"
#include <glib/gi18n-lib.h>
#ifdef GDK_WINDOWING_X11
#include "x11/gdkx.h"
#include "x11/gdkprivate-x11.h"
#endif
#ifdef GDK_WINDOWING_BROADWAY
#include "broadway/gdkprivate-broadway.h"
#endif
macos: prototype new GDK backend for macOS This is fairly substantial rewrite of the GDK backend for quartz and renamed to macOS to allow for a greenfield implementation. Many things have come across from the quartz implementation fairly intact such as the eventloop integration design and discovery of event windows from the NSEvent. However much has been changed to fit in with the new GDK design and how removal of child GdkWindow have been completely eliminated. Furthermore, the new GdkPopup allows for regular NSWindow to be used to provide popovers unlike the previous implementation. The object design more closely follows the ideal for a GDK backend. Views have been broken out into subclasses so that we can support multiple GSK renderer paths such as GL and Cairo (and Metal in the future). However mixed mode GL and Cairo will not be supported. Currently only the Cairo renderer has been implemented. A new frame clock implementation using CVDisplayLink provides more accurate information about when to draw drawing the next frame. Some testing will need to be done here to understand the power implications of this. This implementation has also gained edge snapping for CSD windows. Some work was also done to ensure that CSD windows have opaque regions registered with the display server. ** This is still very much a work-in-progress ** Some outstanding work that needs to be done: - Finish a GL context for macOS and alternate NSView for GL rendering (possibly using speciailized CALayer for OpenGL). - Input rework to ensure that we don't loose remapping of keys that was dropped from GDK during GTK 4 development. - Make sure input methods continue to work. - Drag-n-Drop is still very much a work in progress - High resolution input scrolling needs various work in GDK to land first before we can plumb that to NSEvent. - gtk/ has a number of things based on GDK_WINDOWING_QUARTZ that need to be updated to use the macOS backend. But this is good enough to start playing with and breaking things which is what I'd like to see.
2020-04-23 23:36:46 +00:00
#ifdef GDK_WINDOWING_MACOS
#include "macos/gdkmacosdisplay-private.h"
#endif
#ifdef GDK_WINDOWING_WIN32
#include "win32/gdkwin32.h"
#include "win32/gdkprivate-win32.h"
#endif
2010-12-18 20:38:49 +00:00
#ifdef GDK_WINDOWING_WAYLAND
#include "wayland/gdkprivate-wayland.h"
2010-12-18 20:38:49 +00:00
#endif
/**
* GdkDisplayManager:
*
* A singleton object that offers notification when displays appear or
* disappear.
*
* You can use [func@Gdk.DisplayManager.get] to obtain the `GdkDisplayManager`
* singleton, but that should be rarely necessary. Typically, initializing
* GTK opens a display that you can work with without ever accessing the
* `GdkDisplayManager`.
*
* The GDK library can be built with support for multiple backends.
* The `GdkDisplayManager` object determines which backend is used
* at runtime.
*
* In the rare case that you need to influence which of the backends
* is being used, you can use [func@Gdk.set_allowed_backends]. Note
* that you need to call this function before initializing GTK.
*
* ## Backend-specific code
*
* When writing backend-specific code that is supposed to work with
* multiple GDK backends, you have to consider both compile time and
* runtime. At compile time, use the `GDK_WINDOWING_X11`, `GDK_WINDOWING_WIN32`
* macros, etc. to find out which backends are present in the GDK library
* you are building your application against. At runtime, use type-check
* macros like GDK_IS_X11_DISPLAY() to find out which backend is in use:
*
* ```c
* #ifdef GDK_WINDOWING_X11
* if (GDK_IS_X11_DISPLAY (display))
* {
* // make X11-specific calls here
* }
* else
* #endif
* #ifdef GDK_WINDOWING_MACOS
* if (GDK_IS_MACOS_DISPLAY (display))
* {
* // make Quartz-specific calls here
* }
* else
* #endif
* g_error ("Unsupported GDK backend");
* ```
2017-12-26 19:39:24 +00:00
*/
enum {
PROP_0,
PROP_DEFAULT_DISPLAY
};
enum {
DISPLAY_OPENED,
LAST_SIGNAL
};
static void gdk_display_manager_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gdk_display_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (GdkDisplayManager, gdk_display_manager, G_TYPE_OBJECT)
static void
gdk_display_manager_class_init (GdkDisplayManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = gdk_display_manager_set_property;
object_class->get_property = gdk_display_manager_get_property;
/**
* GdkDisplayManager::display-opened:
* @manager: the object on which the signal is emitted
* @display: the opened display
*
* Emitted when a display is opened.
*/
signals[DISPLAY_OPENED] =
g_signal_new (g_intern_static_string ("display-opened"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdkDisplayManagerClass, display_opened),
NULL, NULL,
NULL,
G_TYPE_NONE,
1,
GDK_TYPE_DISPLAY);
/**
* GdkDisplayManager:default-display: (attributes org.gtk.Property.get=gdk_display_manager_get_default_display)
*
* The default display.
*/
g_object_class_install_property (object_class,
PROP_DEFAULT_DISPLAY,
g_param_spec_object ("default-display", NULL, NULL,
GDK_TYPE_DISPLAY,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS));
}
static void
gdk_display_manager_init (GdkDisplayManager *manager)
{
}
static void
gdk_display_manager_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
case PROP_DEFAULT_DISPLAY:
gdk_display_manager_set_default_display (GDK_DISPLAY_MANAGER (object),
g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdk_display_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
case PROP_DEFAULT_DISPLAY:
g_value_set_object (value,
gdk_display_manager_get_default_display (GDK_DISPLAY_MANAGER (object)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
2020-07-24 18:40:36 +00:00
static const char *allowed_backends;
/**
* gdk_set_allowed_backends:
* @backends: a comma-separated list of backends
*
* Sets a list of backends that GDK should try to use.
*
2020-05-28 08:00:03 +00:00
* This can be useful if your application does not
* work with certain GDK backends.
*
* By default, GDK tries all included backends.
*
* For example:
*
* ```c
* gdk_set_allowed_backends ("wayland,macos,*");
* ```
*
* instructs GDK to try the Wayland backend first, followed by the
* MacOs backend, and then all others.
*
* If the `GDK_BACKEND` environment variable is set, it determines
* what backends are tried in what order, while still respecting the
* set of allowed backends that are specified by this function.
*
* The possible backend names are:
*
* - `broadway`
* - `macos`
* - `wayland`.
* - `win32`
* - `x11`
*
* You can also include a `*` in the list to try all remaining backends.
*
* This call must happen prior to functions that open a display, such
* as [func@Gdk.Display.open], `gtk_init()`, or `gtk_init_check()`
* in order to take effect.
*/
void
2020-07-24 18:40:36 +00:00
gdk_set_allowed_backends (const char *backends)
{
allowed_backends = g_strdup (backends);
}
typedef struct _GdkBackend GdkBackend;
struct _GdkBackend {
const char *name;
GdkDisplay * (* open_display) (const char *name);
};
2013-05-01 12:46:50 +00:00
static GdkBackend gdk_backends[] = {
macos: prototype new GDK backend for macOS This is fairly substantial rewrite of the GDK backend for quartz and renamed to macOS to allow for a greenfield implementation. Many things have come across from the quartz implementation fairly intact such as the eventloop integration design and discovery of event windows from the NSEvent. However much has been changed to fit in with the new GDK design and how removal of child GdkWindow have been completely eliminated. Furthermore, the new GdkPopup allows for regular NSWindow to be used to provide popovers unlike the previous implementation. The object design more closely follows the ideal for a GDK backend. Views have been broken out into subclasses so that we can support multiple GSK renderer paths such as GL and Cairo (and Metal in the future). However mixed mode GL and Cairo will not be supported. Currently only the Cairo renderer has been implemented. A new frame clock implementation using CVDisplayLink provides more accurate information about when to draw drawing the next frame. Some testing will need to be done here to understand the power implications of this. This implementation has also gained edge snapping for CSD windows. Some work was also done to ensure that CSD windows have opaque regions registered with the display server. ** This is still very much a work-in-progress ** Some outstanding work that needs to be done: - Finish a GL context for macOS and alternate NSView for GL rendering (possibly using speciailized CALayer for OpenGL). - Input rework to ensure that we don't loose remapping of keys that was dropped from GDK during GTK 4 development. - Make sure input methods continue to work. - Drag-n-Drop is still very much a work in progress - High resolution input scrolling needs various work in GDK to land first before we can plumb that to NSEvent. - gtk/ has a number of things based on GDK_WINDOWING_QUARTZ that need to be updated to use the macOS backend. But this is good enough to start playing with and breaking things which is what I'd like to see.
2020-04-23 23:36:46 +00:00
#ifdef GDK_WINDOWING_MACOS
{ "macos", _gdk_macos_display_open },
#endif
#ifdef GDK_WINDOWING_WIN32
{ "win32", _gdk_win32_display_open },
#endif
#ifdef GDK_WINDOWING_WAYLAND
{ "wayland", _gdk_wayland_display_open },
#endif
#ifdef GDK_WINDOWING_X11
{ "x11", gdk_x11_display_open },
#endif
#ifdef GDK_WINDOWING_BROADWAY
{ "broadway", _gdk_broadway_display_open },
#endif
/* NULL-terminating this array so we can use commas above */
{ NULL, NULL }
};
/**
* gdk_display_manager_get:
*
* Gets the singleton `GdkDisplayManager` object.
*
* When called for the first time, this function consults the
* `GDK_BACKEND` environment variable to find out which of the
* supported GDK backends to use (in case GDK has been compiled
* with multiple backends).
*
* Applications can use [func@set_allowed_backends] to limit what
* backends will be used.
*
* Returns: (transfer none): The global `GdkDisplayManager` singleton
*/
GdkDisplayManager*
gdk_display_manager_get (void)
{
static GdkDisplayManager *manager = NULL;
if (manager == NULL)
manager = g_object_new (GDK_TYPE_DISPLAY_MANAGER, NULL);
return manager;
}
/**
* gdk_display_manager_get_default_display: (attributes org.gtk.Method.get_property=default-display)
* @manager: a `GdkDisplayManager`
*
* Gets the default `GdkDisplay`.
*
* Returns: (nullable) (transfer none): a `GdkDisplay`
*/
GdkDisplay *
gdk_display_manager_get_default_display (GdkDisplayManager *manager)
{
return manager->default_display;
}
/**
* gdk_display_get_default:
*
* Gets the default `GdkDisplay`.
*
* This is a convenience function for:
*
* gdk_display_manager_get_default_display (gdk_display_manager_get ())
*
* Returns: (nullable) (transfer none): a `GdkDisplay`, or %NULL if
* there is no default display
*/
GdkDisplay *
gdk_display_get_default (void)
{
return gdk_display_manager_get_default_display (gdk_display_manager_get ());
}
/**
* gdk_display_manager_set_default_display:
* @manager: a `GdkDisplayManager`
* @display: a `GdkDisplay`
*
* Sets @display as the default display.
*/
void
gdk_display_manager_set_default_display (GdkDisplayManager *manager,
GdkDisplay *display)
{
manager->default_display = display;
if (display)
GDK_DISPLAY_GET_CLASS (display)->make_default (display);
g_object_notify (G_OBJECT (manager), "default-display");
}
/**
* gdk_display_manager_list_displays:
* @manager: a `GdkDisplayManager`
*
* List all currently open displays.
*
* Returns: (transfer container) (element-type GdkDisplay): a newly
* allocated `GSList` of `GdkDisplay` objects
*/
GSList *
gdk_display_manager_list_displays (GdkDisplayManager *manager)
{
return g_slist_copy (manager->displays);
}
2010-12-25 05:02:39 +00:00
/**
* gdk_display_manager_open_display:
* @manager: a `GdkDisplayManager`
2010-12-25 05:02:39 +00:00
* @name: the name of the display to open
*
* Opens a display.
*
* Returns: (nullable) (transfer none): a `GdkDisplay`, or %NULL
* if the display could not be opened
2010-12-25 05:02:39 +00:00
*/
GdkDisplay *
gdk_display_manager_open_display (GdkDisplayManager *manager,
2020-07-24 18:40:36 +00:00
const char *name)
{
2020-07-24 18:40:36 +00:00
const char *backend_list;
GdkDisplay *display;
2020-07-24 18:40:36 +00:00
char **backends;
2020-07-24 13:54:49 +00:00
int i, j;
gboolean allow_any;
if (allowed_backends == NULL)
allowed_backends = "*";
allow_any = strstr (allowed_backends, "*") != NULL;
backend_list = g_getenv ("GDK_BACKEND");
if (backend_list == NULL)
backend_list = allowed_backends;
else if (g_strcmp0 (backend_list, "help") == 0)
{
fprintf (stderr, "Supported GDK backends:");
for (i = 0; gdk_backends[i].name != NULL; i++)
fprintf (stderr, " %s", gdk_backends[i].name);
fprintf (stderr, "\n");
backend_list = allowed_backends;
}
backends = g_strsplit (backend_list, ",", 0);
display = NULL;
for (i = 0; display == NULL && backends[i] != NULL; i++)
{
2020-07-24 18:40:36 +00:00
const char *backend = backends[i];
gboolean any = g_str_equal (backend, "*");
gboolean found = FALSE;
if (!allow_any && !any && !strstr (allowed_backends, backend))
{
GDK_DEBUG (MISC, "Skipping %s backend", backend);
continue;
}
for (j = 0; gdk_backends[j].name != NULL; j++)
{
if (g_str_equal (backend, gdk_backends[j].name))
found = TRUE;
if ((any && allow_any) ||
(any && strstr (allowed_backends, gdk_backends[j].name)) ||
g_str_equal (backend, gdk_backends[j].name))
{
2022-09-23 00:48:58 +00:00
GDK_DEBUG (MISC, "Trying %s backend", gdk_backends[j].name);
display = gdk_backends[j].open_display (name);
if (display)
{
GDK_DEBUG (MISC, "Using %s display %s", gdk_backends[j].name, gdk_display_get_name (display));
break;
}
}
}
if (!found && !display)
g_warning ("No such backend: %s", backend);
}
g_strfreev (backends);
return display;
}
2010-12-16 05:37:39 +00:00
void
_gdk_display_manager_add_display (GdkDisplayManager *manager,
GdkDisplay *display)
{
if (manager->displays == NULL)
gdk_display_manager_set_default_display (manager, display);
manager->displays = g_slist_prepend (manager->displays, display);
g_signal_emit (manager, signals[DISPLAY_OPENED], 0, display);
}
/* NB: This function can be called multiple times per display. */
void
_gdk_display_manager_remove_display (GdkDisplayManager *manager,
GdkDisplay *display)
{
manager->displays = g_slist_remove (manager->displays, display);
if (manager->default_display == display)
{
if (manager->displays)
gdk_display_manager_set_default_display (manager, manager->displays->data);
else
gdk_display_manager_set_default_display (manager, NULL);
}
}