Quelle gdkandroiddisplay.c
Sprache: C
/*
* Copyright ( c ) 2024 Florian " sp1rit " < sp1rit @ disroot . org >
*
* 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 "gdkdisplayprivate.h"
#include "gdkeventsprivate.h"
#include "gdkframeclockprivate.h"
#include "gdkrgbaprivate.h"
#include "gdkandroidinit-private.h"
#include "gdkandroidchoreographersource-private.h"
#include "gdkandroidsurface-private.h"
#include "gdkandroidtoplevel-private.h"
#include "gdkandroidpopup-private.h"
#include "gdkandroidcairocontext-private.h"
#include "gdkandroidclipboard-private.h"
#include "gdkandroiddevice-private.h"
#include "gdkandroidkeymap-private.h"
#include "gdkandroidglcontext-private.h"
#include "gdkandroiddisplay-private.h"
// For GtkInterfaceColorScheme
#include "gtk/gtkenums.h"
#include <epoxy/egl.h>
G_DEFINE_ENUM_TYPE (GdkAndroidDisplayNightMode, gdk_android_display_night_mode,
G_DEFINE_ENUM_VALUE (GDK_ANDROID_DISPLAY_NIGHT_UNDEFINED, "undefined" ),
G_DEFINE_ENUM_VALUE (GDK_ANDROID_DISPLAY_NIGHT_NO, "no" ),
G_DEFINE_ENUM_VALUE (GDK_ANDROID_DISPLAY_NIGHT_YES, "yes" ))
/**
* GdkAndroidDisplay :
*
* The Android implementation of [ class @ Gdk . Display ] .
*
* In addition to the API provided by [ class @ Gdk . Display ] , this subclass
* provides [ method @ Gdk . AndroidDisplay . get_env ] , which allows you to
* interact with the [ ART ] ( https : //source.android.com/docs/core/runtime)
* with [ JNI ] ( https : //docs.oracle.com/en/java/javase/17/docs/specs/jni).
*
* Since : 4 . 18
*/
G_DEFINE_TYPE (GdkAndroidDisplay, gdk_android_display, GDK_TYPE_DISPLAY)
enum
{
PROP_NIGHT_MODE = 1 ,
PROP_ACCENT_COLOR,
N_PROPERTIES
};
static GParamSpec *obj_properties[N_PROPERTIES] = { 0 , };
static gboolean
gdk_android_display_on_choreographer_vsync (gpointer user_data)
{
GdkAndroidDisplay *display = GDK_ANDROID_DISPLAY (user_data);
for (GList *l = display->visible_surfaces; l != NULL; l = l->next)
{
GdkSurface *surface = GDK_SURFACE (l->data);
GdkFrameClock *clock = gdk_surface_get_frame_clock (surface);
if (clock)
gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_PAINT);
}
return G_SOURCE_CONTINUE;
}
static void
gdk_android_display_finalize (GObject *object)
{
GdkAndroidDisplay *self = (GdkAndroidDisplay *) object;
GdkDisplay *display = (GdkDisplay *) self;
if (self->choreographer_source)
{
g_source_destroy (self->choreographer_source);
g_source_unref (self->choreographer_source);
self->choreographer_source = NULL;
}
g_clear_list (&self->visible_surfaces, NULL);
g_clear_object (&display->clipboard);
if (g_hash_table_size (self->surfaces))
g_warning ("Gdk.AndroidDisplay was finalized with active surfaces. This is not supposed to happen!" );
g_hash_table_unref (self->surfaces);
g_object_unref (self->monitors);
g_object_unref (self->seat);
g_object_unref (self->keymap);
if (g_hash_table_size (self->drags) > 0 )
// This is something that *shouldn't* be happening theoretically, but due
// bad API design from Android, I could imagine that this could actually
// occur.
g_info ("Gdk.AndroidDisplay was finalized with active drags" );
g_hash_table_unref (self->drags);
g_mutex_clear (&self->surface_lock);
G_OBJECT_CLASS (gdk_android_display_parent_class)->finalize (object);
}
static void
gdk_android_display_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
GdkAndroidDisplay *self = (GdkAndroidDisplay *) object;
switch (prop_id)
{
case PROP_NIGHT_MODE:
g_value_set_enum (value, gdk_android_display_get_night_mode (self));
break ;
case PROP_ACCENT_COLOR:
g_value_set_boxed (value, gdk_android_display_get_accent_color (self));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static const char *
gdk_android_display_get_name (GdkDisplay *display)
{
return "AndroidDisplay" ;
}
static void
gdk_android_display_beep (GdkDisplay *display)
{
// TODO: use ToneGenerator
}
static void
gdk_android_display_sync (GdkDisplay *display)
{
// NOP
}
static void
gdk_android_display_flush (GdkDisplay *display)
{
// NOP
}
static void
gdk_android_display_queue_events (GdkDisplay *display)
{
// NOP
}
static GdkGLContext *
gdk_android_display_init_gl (GdkDisplay *display, GError **error)
{
if (!gdk_display_init_egl (display, EGL_PLATFORM_ANDROID_KHR, EGL_DEFAULT_DISPLAY, TRUE , error))
{
g_warning ("GdkAndroidDisplay.init_egl failed" );
return NULL;
}
return g_object_new (GDK_TYPE_ANDROID_GL_CONTEXT, "display" , display, NULL);
}
static gulong
gdk_android_display_get_next_serial (GdkDisplay *display)
{
return 0 ;
}
static void
gdk_android_display_notify_startup_complete (GdkDisplay *display, const char *startup_id)
{
g_debug ("Android Startup (%s) complete" , startup_id);
}
static GdkKeymap *
gdk_android_display_get_keymap (GdkDisplay *display)
{
GdkAndroidDisplay *self = (GdkAndroidDisplay *) display;
return self->keymap;
}
static GdkSeat *
gdk_android_display_get_default_seat (GdkDisplay *display)
{
GdkAndroidDisplay *self = (GdkAndroidDisplay *) display;
return (GdkSeat *) self->seat;
}
static GListModel *
gdk_android_display_get_monitors (GdkDisplay *display)
{
GdkAndroidDisplay *self = GDK_ANDROID_DISPLAY (display);
return G_LIST_MODEL (self->monitors);
}
static gboolean
gdk_android_display_get_setting (GdkDisplay *display,
const char *name,
GValue *value)
{
GdkAndroidDisplay *self = (GdkAndroidDisplay *) display;
if (g_strcmp0 (name, "gtk-application-prefer-dark-theme" ) == 0 )
{
g_value_set_boolean (value, self->night_mode == GDK_ANDROID_DISPLAY_NIGHT_YES);
return TRUE ;
}
else if (g_strcmp0 (name, "gtk-interface-color-scheme" ) == 0 )
{
const GtkInterfaceColorScheme color_schemes[] = {
[GDK_ANDROID_DISPLAY_NIGHT_UNDEFINED] = GTK_INTERFACE_COLOR_SCHEME_UNSUPPORTED,
[GDK_ANDROID_DISPLAY_NIGHT_NO] = GTK_INTERFACE_COLOR_SCHEME_LIGHT,
[GDK_ANDROID_DISPLAY_NIGHT_YES] = GTK_INTERFACE_COLOR_SCHEME_DARK
};
g_value_set_enum (value, color_schemes[self->night_mode]);
return TRUE ;
}
else if (g_strcmp0 (name, "gtk-decoration-layout" ) == 0 )
{
g_value_set_static_string (value, ":" );
return TRUE ;
}
else
{
return FALSE ;
}
}
static void
gdk_android_display_class_init (GdkAndroidDisplayClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GdkDisplayClass *display_class = GDK_DISPLAY_CLASS (klass);
object_class->finalize = gdk_android_display_finalize;
object_class->get_property = gdk_android_display_get_property;
display_class->toplevel_type = GDK_TYPE_ANDROID_TOPLEVEL;
display_class->popup_type = GDK_TYPE_ANDROID_POPUP;
display_class->cairo_context_type = GDK_TYPE_ANDROID_CAIRO_CONTEXT;
display_class->vk_context_type = G_TYPE_NONE;
display_class->vk_extension_name = NULL;
display_class->get_name = gdk_android_display_get_name;
display_class->beep = gdk_android_display_beep;
display_class->sync = gdk_android_display_sync;
display_class->flush = gdk_android_display_flush;
display_class->queue_events = gdk_android_display_queue_events;
display_class->init_gl = gdk_android_display_init_gl;
display_class->get_next_serial = gdk_android_display_get_next_serial;
display_class->notify_startup_complete = gdk_android_display_notify_startup_complete;
display_class->get_keymap = gdk_android_display_get_keymap;
display_class->get_default_seat = gdk_android_display_get_default_seat;
display_class->get_monitors = gdk_android_display_get_monitors;
display_class->get_setting = gdk_android_display_get_setting;
/**
* GdkAndroidDisplay : night - mode : ( getter get_night_mode )
*
* The current night mode state of the Android UI configuration .
*
* Since : 4 . 18
*/
obj_properties[PROP_NIGHT_MODE] = g_param_spec_enum ("night-mode" , NULL, NULL,
GDK_TYPE_ANDROID_DISPLAY_NIGHT_MODE, GDK_ANDROID_DISPLAY_NIGHT_UNDEFINED,
G_PARAM_STATIC_NAME | G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GdkAndroidDisplay : accent - color : ( getter get_accent_color )
*
* The current accent color of the OS .
*
* Since : 4 . 24
*/
obj_properties[PROP_ACCENT_COLOR] = g_param_spec_boxed ("accent-color" , NULL, NULL,
GDK_TYPE_RGBA,
G_PARAM_STATIC_NAME | G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties);
}
static void
gdk_android_display_init (GdkAndroidDisplay *self)
{
g_mutex_init (&self->surface_lock);
GdkDisplay *display = (GdkDisplay *) self;
display->clipboard = gdk_android_clipboard_new (display);
self->monitors = g_list_store_new (GDK_TYPE_ANDROID_MONITOR);
self->surfaces = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
self->seat = gdk_android_seat_new ((GdkDisplay *) self);
gdk_display_add_seat (display, (GdkSeat *) self->seat);
self->keymap = g_object_new (GDK_TYPE_ANDROID_KEYMAP, NULL);
self->drags = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
self->choreographer_source = gdk_android_choreographer_source_new (g_main_context_default ());
if (self->choreographer_source)
{
g_source_set_callback (self->choreographer_source,
gdk_android_display_on_choreographer_vsync,
self, NULL);
g_source_attach (self->choreographer_source, g_main_context_default ());
}
gdk_display_set_composited (display, TRUE );
gdk_display_set_input_shapes (display, TRUE );
gdk_display_set_rgba (display, TRUE );
gdk_display_set_shadow_width (display, FALSE );
}
static GdkAndroidDisplay *gdk_android_display = NULL;
GdkDisplay *
_gdk_android_display_open (const char *display_name)
{
if (gdk_android_display != NULL)
return NULL;
if (!gdk_android_get_env ())
{
g_debug ("Unable to open Android display, as it was unprepared" );
return NULL;
}
display_name = display_name ? display_name : "" ;
GDK_DEBUG (MISC, "opening display %s" , display_name);
gdk_android_display = g_object_new (GDK_TYPE_ANDROID_DISPLAY, NULL);
g_object_add_weak_pointer (G_OBJECT (gdk_android_display), (gpointer *) &gdk_android_display);
gdk_display_emit_opened (GDK_DISPLAY (gdk_android_display));
return GDK_DISPLAY (gdk_android_display);
}
/**
* gdk_android_display_get_env : ( skip )
* @ self : ( transfer none ) : the display
*
* Get the thread - local pointer to the JNI function table that is needed
* to interact with the Java virtual machine .
*
* Returns : pointer to thread - local JNI function table
*
* Since : 4 . 18
*/
JNIEnv *
gdk_android_display_get_env (GdkDisplay *self)
{
return gdk_android_get_env ();
}
GdkAndroidDisplay *
gdk_android_display_get_display_instance (void )
{
return gdk_android_display;
}
GdkAndroidSurface *
gdk_android_display_get_surface_from_identifier (GdkAndroidDisplay *self, glong identifier)
{
g_mutex_lock (&self->surface_lock);
GdkAndroidSurface *ret = g_hash_table_lookup (self->surfaces, (gpointer) identifier);
if (ret)
g_object_ref (ret);
g_mutex_unlock (&self->surface_lock);
return ret;
}
void
gdk_android_display_add_surface (GdkAndroidDisplay *self, GdkAndroidSurface *surface)
{
g_mutex_lock (&self->surface_lock);
g_hash_table_insert (self->surfaces, surface, g_object_ref (surface));
g_mutex_unlock (&self->surface_lock);
}
/**
* gdk_android_display_get_night_mode : ( get - property night - mode )
* @ self : ( transfer none ) : the display
*
* Get the night mode setting of the Android UI configuration .
*
* Returns : current night mode setting
*
* Since : 4 . 18
*/
GdkAndroidDisplayNightMode
gdk_android_display_get_night_mode (GdkAndroidDisplay *self)
{
g_return_val_if_fail (GDK_IS_ANDROID_DISPLAY (self), GDK_ANDROID_DISPLAY_NIGHT_UNDEFINED);
return self->night_mode;
}
/**
* gdk_android_display_get_accent_color : ( get - property accent - color )
* @ self : ( transfer none ) : the display
*
* Get the current [ system_accent1_600 ] ( https : //developer.android.com/reference/android/R.color#system_accent1_600)
* color , generally considered the “ accent color ” .
*
* Returns : current accent color
*
* Since : 4 . 24
*/
const GdkRGBA *
gdk_android_display_get_accent_color (GdkAndroidDisplay *self)
{
g_return_val_if_fail (GDK_IS_ANDROID_DISPLAY (self), NULL);
return &self->accent_color_rgba;
}
void
gdk_android_display_update_configuration (GdkAndroidDisplay *self, jobject context)
{
JNIEnv *env = gdk_android_get_env ();
(*env)->PushLocalFrame (env, 5 );
jobject resources = (*env)->CallObjectMethod (env, context, gdk_android_get_java_cache ()->a_context.get_resources);
jobject configuration = (*env)->CallObjectMethod (env, resources, gdk_android_get_java_cache ()->a_resources.get_configuration);
jint ui = (*env)->GetIntField (env, configuration, gdk_android_get_java_cache ()->a_configuration.ui);
guint32 color = (guint32)(*env)->CallIntMethod (env, context,
gdk_android_get_java_cache ()->a_context.get_color,
gdk_android_get_java_cache ()->a_res_color.system_accent1_600);
(*env)->PopLocalFrame (env, NULL);
GdkAndroidDisplayNightMode night_mode = GDK_ANDROID_DISPLAY_NIGHT_UNDEFINED;
if (ui & gdk_android_get_java_cache ()->a_configuration.ui_night_yes)
night_mode = GDK_ANDROID_DISPLAY_NIGHT_YES;
else if (ui & gdk_android_get_java_cache ()->a_configuration.ui_night_no)
night_mode = GDK_ANDROID_DISPLAY_NIGHT_NO;
if (self->night_mode != night_mode)
{
self->night_mode = night_mode;
g_debug ("night mode changed" );
gdk_display_setting_changed ((GdkDisplay *) self, "gtk-application-prefer-dark-theme" );
gdk_display_setting_changed ((GdkDisplay *) self, "gtk-interface-color-scheme" );
g_object_notify_by_pspec ((GObject *) self, obj_properties[PROP_NIGHT_MODE]);
}
if (self->accent_color != color)
{
self->accent_color = color;
self->accent_color_rgba = GDK_RGBA_INIT_FROM_INT (color);
g_debug ("accent color changed" );
g_object_notify_by_pspec ((GObject *) self, obj_properties[PROP_ACCENT_COLOR]);
}
}
Messung V0.5 in Prozent C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland
2026-07-09