/* * Copyright © 2010 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library. If not, see . */ #include "config.h" #include #include #include #include #include "gdkscreenprivate.h" #include "gdkvisualprivate.h" #include "gdkdisplay.h" #include "gdkdisplay-wayland.h" #include "gdkmonitor-wayland.h" #include "gdkwayland.h" #include "gdkprivate-wayland.h" #include "wm-button-layout-translation.h" typedef struct _GdkWaylandScreen GdkWaylandScreen; typedef struct _GdkWaylandScreenClass GdkWaylandScreenClass; #define GDK_TYPE_WAYLAND_SCREEN (_gdk_wayland_screen_get_type ()) #define GDK_WAYLAND_SCREEN(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WAYLAND_SCREEN, GdkWaylandScreen)) #define GDK_WAYLAND_SCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WAYLAND_SCREEN, GdkWaylandScreenClass)) #define GDK_IS_WAYLAND_SCREEN(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WAYLAND_SCREEN)) #define GDK_IS_WAYLAND_SCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WAYLAND_SCREEN)) #define GDK_WAYLAND_SCREEN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_SCREEN, GdkWaylandScreenClass)) typedef struct { gboolean antialias; gboolean hinting; gint dpi; const gchar *rgba; const gchar *hintstyle; } GsdXftSettings; struct _GdkWaylandScreen { GdkScreen parent_instance; GdkDisplay *display; GdkWindow *root_window; int width, height; int width_mm, height_mm; /* Visual Part */ GdkVisual *visual; GHashTable *settings; GsdXftSettings xft_settings; guint32 shell_capabilities; }; struct _GdkWaylandScreenClass { GdkScreenClass parent_class; }; #define OUTPUT_VERSION_WITH_DONE 2 GType _gdk_wayland_screen_get_type (void); G_DEFINE_TYPE (GdkWaylandScreen, _gdk_wayland_screen, GDK_TYPE_SCREEN) static void gdk_wayland_screen_dispose (GObject *object) { GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (object); if (screen_wayland->root_window) _gdk_window_destroy (screen_wayland->root_window, FALSE); G_OBJECT_CLASS (_gdk_wayland_screen_parent_class)->dispose (object); } static void gdk_wayland_screen_finalize (GObject *object) { GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (object); if (screen_wayland->root_window) g_object_unref (screen_wayland->root_window); g_object_unref (screen_wayland->visual); g_hash_table_destroy (screen_wayland->settings); G_OBJECT_CLASS (_gdk_wayland_screen_parent_class)->finalize (object); } static GdkDisplay * gdk_wayland_screen_get_display (GdkScreen *screen) { return GDK_WAYLAND_SCREEN (screen)->display; } static gint gdk_wayland_screen_get_width (GdkScreen *screen) { return GDK_WAYLAND_SCREEN (screen)->width; } static gint gdk_wayland_screen_get_height (GdkScreen *screen) { return GDK_WAYLAND_SCREEN (screen)->height; } static gint gdk_wayland_screen_get_width_mm (GdkScreen *screen) { return GDK_WAYLAND_SCREEN (screen)->width_mm; } static gint gdk_wayland_screen_get_height_mm (GdkScreen *screen) { return GDK_WAYLAND_SCREEN (screen)->height_mm; } static gint gdk_wayland_screen_get_number (GdkScreen *screen) { return 0; } static GdkWindow * gdk_wayland_screen_get_root_window (GdkScreen *screen) { return GDK_WAYLAND_SCREEN (screen)->root_window; } static GdkVisual * gdk_wayland_screen_get_system_visual (GdkScreen * screen) { return (GdkVisual *) GDK_WAYLAND_SCREEN (screen)->visual; } static GdkVisual * gdk_wayland_screen_get_rgba_visual (GdkScreen *screen) { return (GdkVisual *) GDK_WAYLAND_SCREEN (screen)->visual; } static gboolean gdk_wayland_screen_is_composited (GdkScreen *screen) { return TRUE; } static gchar * gdk_wayland_screen_make_display_name (GdkScreen *screen) { return g_strdup (gdk_display_get_name (GDK_WAYLAND_SCREEN (screen)->display)); } static GdkWindow * gdk_wayland_screen_get_active_window (GdkScreen *screen) { return NULL; } static GList * gdk_wayland_screen_get_window_stack (GdkScreen *screen) { return NULL; } static void gdk_wayland_screen_broadcast_client_message (GdkScreen *screen, GdkEvent *event) { } static void notify_setting (GdkScreen *screen, const gchar *setting) { GdkEvent event; event.type = GDK_SETTING; event.setting.window = gdk_screen_get_root_window (screen); event.setting.send_event = FALSE; event.setting.action = GDK_SETTING_ACTION_CHANGED; event.setting.name = (gchar *)setting; gdk_event_put (&event); } typedef enum { GSD_FONT_ANTIALIASING_MODE_NONE, GSD_FONT_ANTIALIASING_MODE_GRAYSCALE, GSD_FONT_ANTIALIASING_MODE_RGBA } GsdFontAntialiasingMode; typedef enum { GSD_FONT_HINTING_NONE, GSD_FONT_HINTING_SLIGHT, GSD_FONT_HINTING_MEDIUM, GSD_FONT_HINTING_FULL } GsdFontHinting; typedef enum { GSD_FONT_RGBA_ORDER_RGBA, GSD_FONT_RGBA_ORDER_RGB, GSD_FONT_RGBA_ORDER_BGR, GSD_FONT_RGBA_ORDER_VRGB, GSD_FONT_RGBA_ORDER_VBGR } GsdFontRgbaOrder; static gdouble get_dpi_from_gsettings (GdkWaylandScreen *screen_wayland) { GSettings *settings; gdouble factor; settings = g_hash_table_lookup (screen_wayland->settings, "org.gnome.desktop.interface"); if (settings != NULL) factor = g_settings_get_double (settings, "text-scaling-factor"); else factor = 1.0; return 96.0 * factor; } static void update_xft_settings (GdkScreen *screen) { GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen); GSettings *settings; GsdFontAntialiasingMode antialiasing; GsdFontHinting hinting; GsdFontRgbaOrder order; gboolean use_rgba = FALSE; GsdXftSettings xft_settings; settings = g_hash_table_lookup (screen_wayland->settings, "org.gnome.settings-daemon.plugins.xsettings"); if (settings) { antialiasing = g_settings_get_enum (settings, "antialiasing"); hinting = g_settings_get_enum (settings, "hinting"); order = g_settings_get_enum (settings, "rgba-order"); } else { antialiasing = GSD_FONT_ANTIALIASING_MODE_GRAYSCALE; hinting = GSD_FONT_HINTING_MEDIUM; order = GSD_FONT_RGBA_ORDER_RGB; } xft_settings.antialias = (antialiasing != GSD_FONT_ANTIALIASING_MODE_NONE); xft_settings.hinting = (hinting != GSD_FONT_HINTING_NONE); xft_settings.dpi = get_dpi_from_gsettings (screen_wayland) * 1024; /* Xft wants 1/1024ths of an inch */ xft_settings.rgba = "rgb"; xft_settings.hintstyle = "hintfull"; switch (hinting) { case GSD_FONT_HINTING_NONE: xft_settings.hintstyle = "hintnone"; break; case GSD_FONT_HINTING_SLIGHT: xft_settings.hintstyle = "hintslight"; break; case GSD_FONT_HINTING_MEDIUM: xft_settings.hintstyle = "hintmedium"; break; case GSD_FONT_HINTING_FULL: xft_settings.hintstyle = "hintfull"; break; } switch (order) { case GSD_FONT_RGBA_ORDER_RGBA: xft_settings.rgba = "rgba"; break; case GSD_FONT_RGBA_ORDER_RGB: xft_settings.rgba = "rgb"; break; case GSD_FONT_RGBA_ORDER_BGR: xft_settings.rgba = "bgr"; break; case GSD_FONT_RGBA_ORDER_VRGB: xft_settings.rgba = "vrgb"; break; case GSD_FONT_RGBA_ORDER_VBGR: xft_settings.rgba = "vbgr"; break; } switch (antialiasing) { case GSD_FONT_ANTIALIASING_MODE_NONE: xft_settings.antialias = FALSE; break; case GSD_FONT_ANTIALIASING_MODE_GRAYSCALE: xft_settings.antialias = TRUE; break; case GSD_FONT_ANTIALIASING_MODE_RGBA: xft_settings.antialias = TRUE; use_rgba = TRUE; } if (!use_rgba) xft_settings.rgba = "none"; if (screen_wayland->xft_settings.antialias != xft_settings.antialias) { screen_wayland->xft_settings.antialias = xft_settings.antialias; notify_setting (screen, "gtk-xft-antialias"); } if (screen_wayland->xft_settings.hinting != xft_settings.hinting) { screen_wayland->xft_settings.hinting = xft_settings.hinting; notify_setting (screen, "gtk-xft-hinting"); } if (screen_wayland->xft_settings.hintstyle != xft_settings.hintstyle) { screen_wayland->xft_settings.hintstyle = xft_settings.hintstyle; notify_setting (screen, "gtk-xft-hintstyle"); } if (screen_wayland->xft_settings.rgba != xft_settings.rgba) { screen_wayland->xft_settings.rgba = xft_settings.rgba; notify_setting (screen, "gtk-xft-rgba"); } if (screen_wayland->xft_settings.dpi != xft_settings.dpi) { double dpi = xft_settings.dpi / 1024.; const char *scale_env; double scale; screen_wayland->xft_settings.dpi = xft_settings.dpi; scale_env = g_getenv ("GDK_DPI_SCALE"); if (scale_env) { scale = g_ascii_strtod (scale_env, NULL); if (scale != 0 && dpi > 0) dpi *= scale; } _gdk_screen_set_resolution (screen, dpi); notify_setting (screen, "gtk-xft-dpi"); } } #define WM_SETTINGS_SCHEMA "org.gnome.desktop.wm.preferences" #define CLASSIC_WM_SETTINGS_SCHEMA "org.gnome.shell.extensions.classic-overrides" typedef struct _TranslationEntry TranslationEntry; struct _TranslationEntry { const gchar *schema; const gchar *key; const gchar *setting; GType type; union { const gchar *s; gint i; gboolean b; } fallback; }; static TranslationEntry translations[] = { { "org.gnome.desktop.interface", "gtk-theme", "gtk-theme-name" , G_TYPE_STRING, { .s = "Adwaita" } }, { "org.gnome.desktop.interface", "icon-theme", "gtk-icon-theme-name", G_TYPE_STRING, { .s = "gnome" } }, { "org.gnome.desktop.interface", "cursor-theme", "gtk-cursor-theme-name", G_TYPE_STRING, { .s = "Adwaita" } }, { "org.gnome.desktop.interface", "cursor-size", "gtk-cursor-theme-size", G_TYPE_INT, { .i = 32 } }, { "org.gnome.desktop.interface", "font-name", "gtk-font-name", G_TYPE_STRING, { .s = "Cantarell 11" } }, { "org.gnome.desktop.interface", "cursor-blink", "gtk-cursor-blink", G_TYPE_BOOLEAN, { .b = TRUE } }, { "org.gnome.desktop.interface", "cursor-blink-time", "gtk-cursor-blink-time", G_TYPE_INT, { .i = 1200 } }, { "org.gnome.desktop.interface", "cursor-blink-timeout", "gtk-cursor-blink-timeout", G_TYPE_INT, { .i = 3600 } }, { "org.gnome.desktop.interface", "gtk-im-module", "gtk-im-module", G_TYPE_STRING, { .s = "simple" } }, { "org.gnome.desktop.interface", "enable-animations", "gtk-enable-animations", G_TYPE_BOOLEAN, { .b = TRUE } }, { "org.gnome.settings-daemon.peripherals.mouse", "double-click", "gtk-double-click-time", G_TYPE_INT, { .i = 400 } }, { "org.gnome.settings-daemon.peripherals.mouse", "drag-threshold", "gtk-dnd-drag-threshold", G_TYPE_INT, {.i = 8 } }, { "org.gnome.desktop.sound", "theme-name", "gtk-sound-theme-name", G_TYPE_STRING, { .s = "freedesktop" } }, { "org.gnome.desktop.sound", "event-sounds", "gtk-enable-event-sounds", G_TYPE_BOOLEAN, { .b = TRUE } }, { "org.gnome.desktop.sound", "input-feedback-sounds", "gtk-enable-input-feedback-sounds", G_TYPE_BOOLEAN, { . b = FALSE } }, { "org.gnome.desktop.privacy", "recent-files-max-age", "gtk-recent-files-max-age", G_TYPE_INT, { .i = 30 } }, { "org.gnome.desktop.privacy", "remember-recent-files", "gtk-recent-files-enabled", G_TYPE_BOOLEAN, { .b = TRUE } }, { WM_SETTINGS_SCHEMA, "button-layout", "gtk-decoration-layout", G_TYPE_STRING, { .s = "menu:close" } }, { CLASSIC_WM_SETTINGS_SCHEMA, "button-layout", "gtk-decoration-layout", G_TYPE_STRING, { .s = "menu:close" } }, { "org.gnome.settings-daemon.plugins.xsettings", "antialiasing", "gtk-xft-antialias", G_TYPE_NONE, { .i = 0 } }, { "org.gnome.settings-daemon.plugins.xsettings", "hinting", "gtk-xft-hinting", G_TYPE_NONE, { .i = 0 } }, { "org.gnome.settings-daemon.plugins.xsettings", "hinting", "gtk-xft-hintstyle", G_TYPE_NONE, { .i = 0 } }, { "org.gnome.settings-daemon.plugins.xsettings", "rgba-order", "gtk-xft-rgba", G_TYPE_NONE, { .i = 0 } }, { "org.gnome.desktop.interface", "text-scaling-factor", "gtk-xft-dpi" , G_TYPE_NONE, { .i = 0 } }, { "org.gnome.desktop.wm.preferences", "action-double-click-titlebar", "gtk-titlebar-double-click", G_TYPE_STRING, { .s = "toggle-maximize" } }, { "org.gnome.desktop.wm.preferences", "action-middle-click-titlebar", "gtk-titlebar-middle-click", G_TYPE_STRING, { .s = "none" } }, { "org.gnome.desktop.wm.preferences", "action-right-click-titlebar", "gtk-titlebar-right-click", G_TYPE_STRING, { .s = "menu" } }, { "org.gnome.desktop.a11y", "always-show-text-caret", "gtk-keynav-use-caret", G_TYPE_BOOLEAN, { .b = FALSE } } }; static TranslationEntry * find_translation_entry_by_key (GSettings *settings, const gchar *key) { guint i; gchar *schema; g_object_get (settings, "schema", &schema, NULL); for (i = 0; i < G_N_ELEMENTS (translations); i++) { if (g_str_equal (schema, translations[i].schema) && g_str_equal (key, translations[i].key)) { g_free (schema); return &translations[i]; } } g_free (schema); return NULL; } static TranslationEntry * find_translation_entry_by_setting (const gchar *setting) { guint i; for (i = 0; i < G_N_ELEMENTS (translations); i++) { if (g_str_equal (setting, translations[i].setting)) return &translations[i]; } return NULL; } static void settings_changed (GSettings *settings, const gchar *key, GdkScreen *screen) { TranslationEntry *entry; entry = find_translation_entry_by_key (settings, key); if (entry != NULL) { if (entry->type != G_TYPE_NONE) notify_setting (screen, entry->setting); else update_xft_settings (screen); } } static void init_settings (GdkScreen *screen) { GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen); GSettingsSchemaSource *source; GSettingsSchema *schema; GSettings *settings; gint i; screen_wayland->settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); source = g_settings_schema_source_get_default (); if (source == NULL) return; for (i = 0; i < G_N_ELEMENTS (translations); i++) { if (g_hash_table_lookup (screen_wayland->settings, (gpointer)translations[i].schema) != NULL) continue; schema = g_settings_schema_source_lookup (source, translations[i].schema, TRUE); if (schema != NULL) { settings = g_settings_new_full (schema, NULL, NULL); g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), screen); g_hash_table_insert (screen_wayland->settings, (gpointer)translations[i].schema, settings); g_settings_schema_unref (schema); } } update_xft_settings (screen); } static void gtk_shell_handle_capabilities (void *data, struct gtk_shell1 *shell, uint32_t capabilities) { GdkScreen *screen = data; GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (data); screen_wayland->shell_capabilities = capabilities; notify_setting (screen, "gtk-shell-shows-app-menu"); notify_setting (screen, "gtk-shell-shows-menubar"); notify_setting (screen, "gtk-shell-shows-desktop"); } struct gtk_shell1_listener gdk_screen_gtk_shell_listener = { gtk_shell_handle_capabilities }; void _gdk_wayland_screen_set_has_gtk_shell (GdkScreen *screen) { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (GDK_WAYLAND_SCREEN (screen)->display); gtk_shell1_add_listener (display_wayland->gtk_shell, &gdk_screen_gtk_shell_listener, screen); } static void set_value_from_entry (GdkScreen *screen, TranslationEntry *entry, GValue *value) { GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen); GSettings *settings; settings = (GSettings *)g_hash_table_lookup (screen_wayland->settings, entry->schema); switch (entry->type) { case G_TYPE_STRING: if (settings) { gchar *s; s = g_settings_get_string (settings, entry->key); g_value_set_string (value, s); g_free (s); } else { g_value_set_static_string (value, entry->fallback.s); } break; case G_TYPE_INT: g_value_set_int (value, settings != NULL ? g_settings_get_int (settings, entry->key) : entry->fallback.i); break; case G_TYPE_BOOLEAN: g_value_set_boolean (value, settings != NULL ? g_settings_get_boolean (settings, entry->key) : entry->fallback.b); break; case G_TYPE_NONE: if (g_str_equal (entry->setting, "gtk-xft-antialias")) g_value_set_int (value, screen_wayland->xft_settings.antialias); else if (g_str_equal (entry->setting, "gtk-xft-hinting")) g_value_set_int (value, screen_wayland->xft_settings.hinting); else if (g_str_equal (entry->setting, "gtk-xft-hintstyle")) g_value_set_static_string (value, screen_wayland->xft_settings.hintstyle); else if (g_str_equal (entry->setting, "gtk-xft-rgba")) g_value_set_static_string (value, screen_wayland->xft_settings.rgba); else if (g_str_equal (entry->setting, "gtk-xft-dpi")) g_value_set_int (value, screen_wayland->xft_settings.dpi); else g_assert_not_reached (); break; default: g_assert_not_reached (); } } static void set_decoration_layout_from_entry (GdkScreen *screen, TranslationEntry *entry, GValue *value) { GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen); GSettings *settings = NULL; const char *session; /* Hack: until we get session-dependent defaults in GSettings, * swap out the usual schema for the "classic" one when * running in classic mode */ session = g_getenv ("XDG_CURRENT_DESKTOP"); if (session && strstr (session, "GNOME-Classic")) settings = (GSettings *)g_hash_table_lookup (screen_wayland->settings, CLASSIC_WM_SETTINGS_SCHEMA); if (settings == NULL) settings = (GSettings *)g_hash_table_lookup (screen_wayland->settings, WM_SETTINGS_SCHEMA); if (settings) { gchar *s = g_settings_get_string (settings, entry->key); translate_wm_button_layout_to_gtk (s); g_value_set_string (value, s); g_free (s); } else { g_value_set_static_string (value, entry->fallback.s); } } static gboolean set_capability_setting (GdkScreen *screen, GValue *value, enum gtk_shell1_capability test) { GdkWaylandScreen *wayland_screen = GDK_WAYLAND_SCREEN (screen); g_value_set_boolean (value, (wayland_screen->shell_capabilities & test) == test); return TRUE; } static gboolean gdk_wayland_screen_get_setting (GdkScreen *screen, const gchar *name, GValue *value) { TranslationEntry *entry; g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); entry = find_translation_entry_by_setting (name); if (entry != NULL) { if (strcmp (name, "gtk-decoration-layout") == 0) set_decoration_layout_from_entry (screen, entry, value); else set_value_from_entry (screen, entry, value); return TRUE; } if (strcmp (name, "gtk-shell-shows-app-menu") == 0) return set_capability_setting (screen, value, GTK_SHELL1_CAPABILITY_GLOBAL_APP_MENU); if (strcmp (name, "gtk-shell-shows-menubar") == 0) return set_capability_setting (screen, value, GTK_SHELL1_CAPABILITY_GLOBAL_MENU_BAR); if (strcmp (name, "gtk-shell-shows-desktop") == 0) return set_capability_setting (screen, value, GTK_SHELL1_CAPABILITY_DESKTOP_ICONS); if (strcmp (name, "gtk-dialogs-use-header") == 0) { g_value_set_boolean (value, TRUE); return TRUE; } return FALSE; } typedef struct _GdkWaylandVisual GdkWaylandVisual; typedef struct _GdkWaylandVisualClass GdkWaylandVisualClass; struct _GdkWaylandVisual { GdkVisual visual; }; struct _GdkWaylandVisualClass { GdkVisualClass parent_class; }; GType _gdk_wayland_visual_get_type (void); G_DEFINE_TYPE (GdkWaylandVisual, _gdk_wayland_visual, GDK_TYPE_VISUAL) static void _gdk_wayland_visual_class_init (GdkWaylandVisualClass *klass) { } static void _gdk_wayland_visual_init (GdkWaylandVisual *visual) { } static gint gdk_wayland_screen_visual_get_best_depth (GdkScreen *screen) { return 32; } static GdkVisualType gdk_wayland_screen_visual_get_best_type (GdkScreen *screen) { return GDK_VISUAL_TRUE_COLOR; } static GdkVisual* gdk_wayland_screen_visual_get_best (GdkScreen *screen) { return GDK_WAYLAND_SCREEN (screen)->visual; } static GdkVisual* gdk_wayland_screen_visual_get_best_with_depth (GdkScreen *screen, gint depth) { if (depth == 32) return GDK_WAYLAND_SCREEN (screen)->visual; else return NULL; } static GdkVisual* gdk_wayland_screen_visual_get_best_with_type (GdkScreen *screen, GdkVisualType visual_type) { if (visual_type == GDK_VISUAL_TRUE_COLOR) return GDK_WAYLAND_SCREEN (screen)->visual; else return NULL; } static GdkVisual* gdk_wayland_screen_visual_get_best_with_both (GdkScreen *screen, gint depth, GdkVisualType visual_type) { if (depth == 32 && visual_type == GDK_VISUAL_TRUE_COLOR) return GDK_WAYLAND_SCREEN (screen)->visual; else return NULL; } static void gdk_wayland_screen_query_depths (GdkScreen *screen, gint **depths, gint *count) { static gint static_depths[] = { 32 }; *count = G_N_ELEMENTS(static_depths); *depths = static_depths; } static void gdk_wayland_screen_query_visual_types (GdkScreen *screen, GdkVisualType **visual_types, gint *count) { static GdkVisualType static_visual_types[] = { GDK_VISUAL_TRUE_COLOR }; *count = G_N_ELEMENTS(static_visual_types); *visual_types = static_visual_types; } static GList * gdk_wayland_screen_list_visuals (GdkScreen *screen) { GList *list; GdkWaylandScreen *screen_wayland; g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); screen_wayland = GDK_WAYLAND_SCREEN (screen); list = g_list_append (NULL, screen_wayland->visual); return list; } #define GDK_TYPE_WAYLAND_VISUAL (_gdk_wayland_visual_get_type ()) #define GDK_WAYLAND_VISUAL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WAYLAND_VISUAL, GdkWaylandVisual)) /* Currently, the Wayland backend only ever uses ARGB8888. */ static GdkVisual * gdk_wayland_visual_new (GdkScreen *screen) { GdkVisual *visual; visual = g_object_new (GDK_TYPE_WAYLAND_VISUAL, NULL); visual->screen = GDK_SCREEN (screen); visual->type = GDK_VISUAL_TRUE_COLOR; visual->depth = 32; visual->red_mask = 0xff0000; visual->green_mask = 0x00ff00; visual->blue_mask = 0x0000ff; visual->bits_per_rgb = 8; return visual; } GdkScreen * _gdk_wayland_screen_new (GdkDisplay *display) { GdkScreen *screen; GdkWaylandScreen *screen_wayland; screen = g_object_new (GDK_TYPE_WAYLAND_SCREEN, NULL); screen_wayland = GDK_WAYLAND_SCREEN (screen); screen_wayland->display = display; screen_wayland->width = 0; screen_wayland->height = 0; screen_wayland->visual = gdk_wayland_visual_new (screen); screen_wayland->root_window = _gdk_wayland_screen_create_root_window (screen, screen_wayland->width, screen_wayland->height); init_settings (screen); return screen; } static void _gdk_wayland_screen_class_init (GdkWaylandScreenClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkScreenClass *screen_class = GDK_SCREEN_CLASS (klass); object_class->dispose = gdk_wayland_screen_dispose; object_class->finalize = gdk_wayland_screen_finalize; screen_class->get_display = gdk_wayland_screen_get_display; screen_class->get_width = gdk_wayland_screen_get_width; screen_class->get_height = gdk_wayland_screen_get_height; screen_class->get_width_mm = gdk_wayland_screen_get_width_mm; screen_class->get_height_mm = gdk_wayland_screen_get_height_mm; screen_class->get_number = gdk_wayland_screen_get_number; screen_class->get_root_window = gdk_wayland_screen_get_root_window; screen_class->get_system_visual = gdk_wayland_screen_get_system_visual; screen_class->get_rgba_visual = gdk_wayland_screen_get_rgba_visual; screen_class->is_composited = gdk_wayland_screen_is_composited; screen_class->make_display_name = gdk_wayland_screen_make_display_name; screen_class->get_active_window = gdk_wayland_screen_get_active_window; screen_class->get_window_stack = gdk_wayland_screen_get_window_stack; screen_class->broadcast_client_message = gdk_wayland_screen_broadcast_client_message; screen_class->get_setting = gdk_wayland_screen_get_setting; screen_class->visual_get_best_depth = gdk_wayland_screen_visual_get_best_depth; screen_class->visual_get_best_type = gdk_wayland_screen_visual_get_best_type; screen_class->visual_get_best = gdk_wayland_screen_visual_get_best; screen_class->visual_get_best_with_depth = gdk_wayland_screen_visual_get_best_with_depth; screen_class->visual_get_best_with_type = gdk_wayland_screen_visual_get_best_with_type; screen_class->visual_get_best_with_both = gdk_wayland_screen_visual_get_best_with_both; screen_class->query_depths = gdk_wayland_screen_query_depths; screen_class->query_visual_types = gdk_wayland_screen_query_visual_types; screen_class->list_visuals = gdk_wayland_screen_list_visuals; } static void _gdk_wayland_screen_init (GdkWaylandScreen *screen_wayland) { } static void update_screen_size (GdkWaylandScreen *screen_wayland) { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (screen_wayland->display); gboolean emit_changed = FALSE; gint width, height; gint width_mm, height_mm; int i; width = height = 0; width_mm = height_mm = 0; for (i = 0; i < display_wayland->monitors->len; i++) { GdkMonitor *monitor = display_wayland->monitors->pdata[i]; /* XXX: Largely assuming here that monitor areas * are contiguous and never overlap. */ if (monitor->geometry.x > 0) width_mm += monitor->width_mm; else width_mm = MAX (width_mm, monitor->width_mm); if (monitor->geometry.y > 0) height_mm += monitor->height_mm; else height_mm = MAX (height_mm, monitor->height_mm); width = MAX (width, monitor->geometry.x + monitor->geometry.width); height = MAX (height, monitor->geometry.y + monitor->geometry.height); } if (screen_wayland->width_mm != width_mm || screen_wayland->height_mm != height_mm) { emit_changed = TRUE; screen_wayland->width_mm = width_mm; screen_wayland->height_mm = height_mm; } if (screen_wayland->width != width || screen_wayland->height != height) { emit_changed = TRUE; screen_wayland->width = width; screen_wayland->height = height; } if (emit_changed) g_signal_emit_by_name (screen_wayland, "size-changed"); } #ifdef G_ENABLE_DEBUG static const char * subpixel_to_string (int layout) { int i; struct { int layout; const char *name; } layouts[] = { { WL_OUTPUT_SUBPIXEL_UNKNOWN, "unknown" }, { WL_OUTPUT_SUBPIXEL_NONE, "none" }, { WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB, "rgb" }, { WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR, "bgr" }, { WL_OUTPUT_SUBPIXEL_VERTICAL_RGB, "vrgb" }, { WL_OUTPUT_SUBPIXEL_VERTICAL_BGR, "vbgr" }, { 0xffffffff, NULL } }; for (i = 0; layouts[i].name; i++) { if (layouts[i].layout == layout) return layouts[i].name; } return NULL; } static const char * transform_to_string (int transform) { int i; struct { int transform; const char *name; } transforms[] = { { WL_OUTPUT_TRANSFORM_NORMAL, "normal" }, { WL_OUTPUT_TRANSFORM_90, "90" }, { WL_OUTPUT_TRANSFORM_180, "180" }, { WL_OUTPUT_TRANSFORM_270, "270" }, { WL_OUTPUT_TRANSFORM_FLIPPED, "flipped" }, { WL_OUTPUT_TRANSFORM_FLIPPED_90, "flipped 90" }, { WL_OUTPUT_TRANSFORM_FLIPPED_180, "flipped 180" }, { WL_OUTPUT_TRANSFORM_FLIPPED_270, "flipped 270" }, { 0xffffffff, NULL } }; for (i = 0; transforms[i].name; i++) { if (transforms[i].transform == transform) return transforms[i].name; } return NULL; } #endif static void output_handle_geometry (void *data, struct wl_output *wl_output, int x, int y, int physical_width, int physical_height, int subpixel, const char *make, const char *model, int32_t transform) { GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data; GDK_NOTE (MISC, g_message ("handle geometry output %d, position %d %d, phys. size %d %d, subpixel layout %s, manufacturer %s, model %s, transform %s", monitor->id, x, y, physical_width, physical_height, subpixel_to_string (subpixel), make, model, transform_to_string (transform))); gdk_monitor_set_position (GDK_MONITOR (monitor), x, y); gdk_monitor_set_physical_size (GDK_MONITOR (monitor), physical_width, physical_height); gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor), subpixel); gdk_monitor_set_manufacturer (GDK_MONITOR (monitor), make); gdk_monitor_set_model (GDK_MONITOR (monitor), model); if (GDK_MONITOR (monitor)->geometry.width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE) { GdkDisplay *display = GDK_MONITOR (monitor)->display; GdkWaylandScreen *screen = GDK_WAYLAND_SCREEN (gdk_display_get_default_screen (display)); g_signal_emit_by_name (screen, "monitors-changed"); update_screen_size (screen); } } static void output_handle_done (void *data, struct wl_output *wl_output) { GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data; GdkDisplay *display = gdk_monitor_get_display (GDK_MONITOR (monitor)); GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (gdk_display_get_default_screen (display)); GDK_NOTE (MISC, g_message ("handle done output %d", monitor->id)); if (!monitor->added) { monitor->added = TRUE; g_ptr_array_add (GDK_WAYLAND_DISPLAY (display)->monitors, monitor); gdk_display_monitor_added (display, GDK_MONITOR (monitor)); } g_signal_emit_by_name (screen_wayland, "monitors-changed"); update_screen_size (screen_wayland); } static void output_handle_scale (void *data, struct wl_output *wl_output, int32_t scale) { GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data; GDK_NOTE (MISC, g_message ("handle scale output %d, scale %d", monitor->id, scale)); gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), scale); if (GDK_MONITOR (monitor)->geometry.width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE) { GdkScreen *screen = gdk_display_get_default_screen (GDK_MONITOR (monitor)->display); g_signal_emit_by_name (screen, "monitors-changed"); } } static void output_handle_mode (void *data, struct wl_output *wl_output, uint32_t flags, int width, int height, int refresh) { GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data; GDK_NOTE (MISC, g_message ("handle mode output %d, size %d %d, rate %d", monitor->id, width, height, refresh)); if ((flags & WL_OUTPUT_MODE_CURRENT) == 0) return; gdk_monitor_set_size (GDK_MONITOR (monitor), width, height); gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh); if (width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE) { GdkScreen *screen = gdk_display_get_default_screen (GDK_MONITOR (monitor)->display); g_signal_emit_by_name (screen, "monitors-changed"); update_screen_size (GDK_WAYLAND_SCREEN (screen)); } } static const struct wl_output_listener output_listener = { output_handle_geometry, output_handle_mode, output_handle_done, output_handle_scale, }; void _gdk_wayland_screen_add_output (GdkScreen *screen, guint32 id, struct wl_output *output, guint32 version) { GdkDisplay *display = gdk_screen_get_display (screen); GdkWaylandMonitor *monitor; monitor = g_object_new (GDK_TYPE_WAYLAND_MONITOR, "display", display, NULL); monitor->id = id; monitor->output = output; monitor->version = version; if (monitor->version < OUTPUT_VERSION_WITH_DONE) { g_ptr_array_add (GDK_WAYLAND_DISPLAY (display)->monitors, monitor); gdk_display_monitor_added (display, GDK_MONITOR (monitor)); } wl_output_add_listener (output, &output_listener, monitor); } struct wl_output * _gdk_wayland_screen_get_wl_output (GdkScreen *screen, gint monitor_num) { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (GDK_WAYLAND_SCREEN (screen)->display); GdkWaylandMonitor *monitor; monitor = display_wayland->monitors->pdata[monitor_num]; return monitor->output; } static GdkWaylandMonitor * get_monitor_for_id (GdkWaylandScreen *screen_wayland, guint32 id) { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (screen_wayland->display); int i; for (i = 0; i < display_wayland->monitors->len; i++) { GdkWaylandMonitor *monitor = display_wayland->monitors->pdata[i]; if (monitor->id == id) return monitor; } return NULL; } static GdkWaylandMonitor * get_monitor_for_output (GdkWaylandScreen *screen_wayland, struct wl_output *output) { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (screen_wayland->display); int i; for (i = 0; i < display_wayland->monitors->len; i++) { GdkWaylandMonitor *monitor = display_wayland->monitors->pdata[i]; if (monitor->output == output) return monitor; } return NULL; } void _gdk_wayland_screen_remove_output (GdkScreen *screen, guint32 id) { GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen); GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (screen_wayland->display); GdkWaylandMonitor *monitor; monitor = get_monitor_for_id (screen_wayland, id); if (monitor != NULL) { g_object_ref (monitor); g_ptr_array_remove (display_wayland->monitors, monitor); gdk_display_monitor_removed (GDK_DISPLAY (display_wayland), GDK_MONITOR (monitor)); g_object_unref (monitor); g_signal_emit_by_name (screen_wayland, "monitors-changed"); update_screen_size (screen_wayland); } } int _gdk_wayland_screen_get_output_refresh_rate (GdkScreen *screen, struct wl_output *output) { GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen); GdkWaylandMonitor *monitor; monitor = get_monitor_for_output (screen_wayland, output); if (monitor != NULL) return gdk_monitor_get_refresh_rate (GDK_MONITOR (monitor)); return 0; } guint32 _gdk_wayland_screen_get_output_scale (GdkScreen *screen, struct wl_output *output) { GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen); GdkWaylandMonitor *monitor; monitor = get_monitor_for_output (screen_wayland, output); if (monitor != NULL) return gdk_monitor_get_scale_factor (GDK_MONITOR (monitor)); return 0; }