gtk2/gdk/wayland/gdkscreen-wayland.c
Jonas Ådahl f68cf698fc wayland: Don't invent our own unstable protocol semantics
The gtk_shell protocol used some half baked unstable protocol semantics
that worked by only allowing binding the exact version of the
interface. This hack is a bit too confusing and it makes it impossible
to do any compatible changes without breaking things.

So, instead rename it to include a number in the interface names. This
way we can add requests and events without causing compatibility issues,
and we can later remove requests and events by bumping the number in
the interface names.

https://bugzilla.gnome.org/show_bug.cgi?id=763001
2016-03-10 12:31:15 +08:00

1251 lines
36 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <gio/gio.h>
#include "gdkscreenprivate.h"
#include "gdkvisualprivate.h"
#include "gdkdisplay.h"
#include "gdkdisplay-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;
typedef struct _GdkWaylandMonitor GdkWaylandMonitor;
struct _GdkWaylandScreen
{
GdkScreen parent_instance;
GdkDisplay *display;
GdkWindow *root_window;
int width, height;
int width_mm, height_mm;
/* Visual Part */
GdkVisual *visual;
/* Xinerama/RandR 1.2 */
GPtrArray *monitors;
gint primary_monitor;
GHashTable *settings;
GsdXftSettings xft_settings;
guint32 shell_capabilities;
};
struct _GdkWaylandScreenClass
{
GdkScreenClass parent_class;
void (* window_manager_changed) (GdkWaylandScreen *screen_wayland);
};
#define OUTPUT_VERSION_WITH_DONE 2
struct _GdkWaylandMonitor
{
GdkWaylandScreen *screen;
guint32 id;
guint32 version;
struct wl_output *output;
GdkRectangle geometry;
int width_mm;
int height_mm;
char * output_name;
char * manufacturer;
int refresh_rate;
gint scale;
};
GType _gdk_wayland_screen_get_type (void);
G_DEFINE_TYPE (GdkWaylandScreen, _gdk_wayland_screen, GDK_TYPE_SCREEN)
static void
free_monitor (gpointer data)
{
GdkWaylandMonitor *monitor = data;
if (monitor == NULL)
return;
wl_output_destroy (monitor->output);
g_free (monitor->output_name);
g_free (monitor->manufacturer);
g_free (monitor);
}
static void
deinit_multihead (GdkScreen *screen)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
g_ptr_array_free (screen_wayland->monitors, TRUE);
screen_wayland->monitors = NULL;
}
static void
init_multihead (GdkScreen *screen)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
screen_wayland->monitors = g_ptr_array_new_with_free_func (free_monitor);
screen_wayland->primary_monitor = 0;
}
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);
deinit_multihead (GDK_SCREEN (object));
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 gint
gdk_wayland_screen_get_n_monitors (GdkScreen *screen)
{
return GDK_WAYLAND_SCREEN (screen)->monitors->len;
}
static gint
gdk_wayland_screen_get_primary_monitor (GdkScreen *screen)
{
return GDK_WAYLAND_SCREEN (screen)->primary_monitor;
}
static gint
gdk_wayland_screen_get_monitor_width_mm (GdkScreen *screen,
gint monitor_num)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
GdkWaylandMonitor *monitor;
monitor = g_ptr_array_index (screen_wayland->monitors, monitor_num);
return monitor->width_mm;
}
static gint
gdk_wayland_screen_get_monitor_height_mm (GdkScreen *screen,
gint monitor_num)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
GdkWaylandMonitor *monitor;
monitor = g_ptr_array_index (screen_wayland->monitors, monitor_num);
return monitor->height_mm;
}
static gchar *
gdk_wayland_screen_get_monitor_plug_name (GdkScreen *screen,
gint monitor_num)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
GdkWaylandMonitor *monitor;
monitor = g_ptr_array_index (screen_wayland->monitors, monitor_num);
return g_strdup (monitor->output_name);
}
static void
gdk_wayland_screen_get_monitor_geometry (GdkScreen *screen,
gint monitor_num,
GdkRectangle *dest)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
GdkWaylandMonitor *monitor;
monitor = g_ptr_array_index (screen_wayland->monitors, monitor_num);
if (dest)
*dest = monitor->geometry;
}
static gint
gdk_wayland_screen_get_monitor_scale_factor (GdkScreen *screen,
gint monitor_num)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
GdkWaylandMonitor *monitor;
monitor = g_ptr_array_index (screen_wayland->monitors, monitor_num);
return monitor->scale;
}
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)
{
const gchar *name;
name = g_getenv ("WAYLAND_DISPLAY");
if (name == NULL)
name = "wayland-0";
return g_strdup (name);
}
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 ();
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 *wayland_display = GDK_WAYLAND_DISPLAY (GDK_WAYLAND_SCREEN (screen)->display);
gtk_shell1_add_listener (wayland_display->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)
{
return GDK_WAYLAND_SCREEN (screen)->visual;
}
static GdkVisual*
gdk_wayland_screen_visual_get_best_with_type (GdkScreen *screen,
GdkVisualType visual_type)
{
return GDK_WAYLAND_SCREEN (screen)->visual;
}
static GdkVisual*
gdk_wayland_screen_visual_get_best_with_both (GdkScreen *screen,
gint depth,
GdkVisualType visual_type)
{
return GDK_WAYLAND_SCREEN (screen)->visual;
}
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))
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->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);
init_multihead (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_n_monitors = gdk_wayland_screen_get_n_monitors;
screen_class->get_primary_monitor = gdk_wayland_screen_get_primary_monitor;
screen_class->get_monitor_width_mm = gdk_wayland_screen_get_monitor_width_mm;
screen_class->get_monitor_height_mm = gdk_wayland_screen_get_monitor_height_mm;
screen_class->get_monitor_plug_name = gdk_wayland_screen_get_monitor_plug_name;
screen_class->get_monitor_geometry = gdk_wayland_screen_get_monitor_geometry;
screen_class->get_monitor_workarea = gdk_wayland_screen_get_monitor_geometry;
screen_class->get_monitor_scale_factor = gdk_wayland_screen_get_monitor_scale_factor;
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)
{
gboolean emit_changed = FALSE;
gint width, height;
gint width_mm, height_mm;
gint i;
width = height = 0;
width_mm = height_mm = 0;
for (i = 0; i < screen_wayland->monitors->len; i++)
{
GdkWaylandMonitor *monitor = screen_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");
}
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, manufacturer %s, model %s",
monitor->id, x, y, physical_width, physical_height, make, model));
monitor->geometry.x = x;
monitor->geometry.y = y;
monitor->width_mm = physical_width;
monitor->height_mm = physical_height;
monitor->manufacturer = g_strdup (make);
monitor->output_name = g_strdup (model);
if (monitor->geometry.width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE)
{
g_signal_emit_by_name (monitor->screen, "monitors-changed");
update_screen_size (monitor->screen);
}
}
static void
output_handle_done (void *data,
struct wl_output *wl_output)
{
GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data;
GDK_NOTE (MISC,
g_message ("handle done output %d", monitor->id));
g_signal_emit_by_name (monitor->screen, "monitors-changed");
update_screen_size (monitor->screen);
}
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));
monitor->scale = scale;
if (monitor->geometry.width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE)
g_signal_emit_by_name (monitor->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;
monitor->geometry.width = width;
monitor->geometry.height = height;
monitor->refresh_rate = refresh;
if (monitor->geometry.width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE)
{
g_signal_emit_by_name (monitor->screen, "monitors-changed");
update_screen_size (monitor->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)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
GdkWaylandMonitor *monitor;
monitor = g_new0 (GdkWaylandMonitor, 1);
monitor->id = id;
monitor->output = output;
monitor->version = version;
monitor->screen = screen_wayland;
monitor->scale = 1;
g_ptr_array_add (screen_wayland->monitors, monitor);
wl_output_add_listener (output, &output_listener, monitor);
}
struct wl_output *
_gdk_wayland_screen_get_wl_output (GdkScreen *screen,
gint monitor_num)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
GdkWaylandMonitor *monitor = g_ptr_array_index (screen_wayland->monitors, monitor_num);
return monitor->output;
}
void
_gdk_wayland_screen_remove_output (GdkScreen *screen,
guint32 id)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
int i;
for (i = 0; i < screen_wayland->monitors->len; i++)
{
GdkWaylandMonitor *monitor = screen_wayland->monitors->pdata[i];
if (monitor->id == id)
{
g_ptr_array_remove (screen_wayland->monitors, monitor);
g_signal_emit_by_name (screen_wayland, "monitors-changed");
update_screen_size (screen_wayland);
break;
}
}
}
int
_gdk_wayland_screen_get_output_refresh_rate (GdkScreen *screen,
struct wl_output *output)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
int i;
for (i = 0; i < screen_wayland->monitors->len; i++)
{
GdkWaylandMonitor *monitor = screen_wayland->monitors->pdata[i];
if (monitor->output == output)
return monitor->refresh_rate;
}
return 0;
}
guint32
_gdk_wayland_screen_get_output_scale (GdkScreen *screen,
struct wl_output *output)
{
GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (screen);
int i;
for (i = 0; i < screen_wayland->monitors->len; i++)
{
GdkWaylandMonitor *monitor = screen_wayland->monitors->pdata[i];
if (monitor->output == output)
return monitor->scale;
}
return 0;
}