diff --git a/debian/libcinnamon-desktop4.symbols b/debian/libcinnamon-desktop4.symbols deleted file mode 100644 index ece145e..0000000 --- a/debian/libcinnamon-desktop4.symbols +++ /dev/null @@ -1,248 +0,0 @@ -libcinnamon-desktop.so.4 libcinnamon-desktop4 #MINVER# - _gnome_datetime_source_new@Base 3.8.1 - _gnome_desktop_init_i18n@Base 3.8.1 - _gnome_rr_output_name_is_laptop@Base 3.8.1 - decode_edid@Base 3.8.1 - gnome_bg_changes_with_time@Base 2.0.4 - gnome_bg_create_and_set_gtk_image@Base 3.4.2 - gnome_bg_create_and_set_surface_as_root@Base 2.0.4 - gnome_bg_create_frame_thumbnail@Base 2.0.4 - gnome_bg_create_surface@Base 2.0.4 - gnome_bg_create_thumbnail@Base 2.0.4 - gnome_bg_crossfade_get_type@Base 2.0.4 - gnome_bg_crossfade_is_started@Base 2.0.4 - gnome_bg_crossfade_new@Base 2.0.4 - gnome_bg_crossfade_set_end_surface@Base 2.0.4 - gnome_bg_crossfade_set_start_surface@Base 2.0.4 - gnome_bg_crossfade_start@Base 2.0.4 - gnome_bg_crossfade_stop@Base 2.0.4 - gnome_bg_draw@Base 2.0.4 - gnome_bg_get_color@Base 2.0.4 - gnome_bg_get_filename@Base 2.0.4 - gnome_bg_get_image_size@Base 2.0.4 - gnome_bg_get_placement@Base 2.0.4 - gnome_bg_get_surface_from_root@Base 2.0.4 - gnome_bg_get_type@Base 2.0.4 - gnome_bg_has_multiple_sizes@Base 2.0.4 - gnome_bg_is_dark@Base 2.0.4 - gnome_bg_load_from_preferences@Base 2.0.4 - gnome_bg_new@Base 2.0.4 - gnome_bg_save_to_preferences@Base 2.0.4 - gnome_bg_set_accountsservice_background@Base 2.2.0 - gnome_bg_set_color@Base 2.0.4 - gnome_bg_set_filename@Base 2.0.4 - gnome_bg_set_placement@Base 2.0.4 - gnome_bg_set_surface_as_root@Base 2.0.4 - gnome_bg_set_surface_as_root_with_crossfade@Base 2.0.4 - gnome_desktop_get_media_key_string@Base 2.4.2 - gnome_desktop_get_session_user_pwent@Base 3.4.2 - gnome_desktop_prepend_terminal_to_vector@Base 2.0.4 - gnome_desktop_thumbnail_cache_check_permissions@Base 3.4.2 - gnome_desktop_thumbnail_cache_fix_permissions@Base 3.4.2 - gnome_desktop_thumbnail_factory_can_thumbnail@Base 2.0.4 - gnome_desktop_thumbnail_factory_create_failed_thumbnail@Base 2.0.4 - gnome_desktop_thumbnail_factory_generate_thumbnail@Base 2.0.4 - gnome_desktop_thumbnail_factory_get_type@Base 2.0.4 - gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail@Base 2.0.4 - gnome_desktop_thumbnail_factory_lookup@Base 2.0.4 - gnome_desktop_thumbnail_factory_new@Base 2.0.4 - gnome_desktop_thumbnail_factory_save_thumbnail@Base 2.0.4 - gnome_desktop_thumbnail_has_uri@Base 2.0.4 - gnome_desktop_thumbnail_is_valid@Base 2.0.4 - gnome_desktop_thumbnail_md5@Base 2.0.4 - gnome_desktop_thumbnail_path_for_uri@Base 2.0.4 - gnome_desktop_thumbnail_scale_down_pixbuf@Base 2.0.4 - gnome_idle_monitor_add_idle_watch@Base 6.0.0 - gnome_idle_monitor_add_user_active_watch@Base 6.0.0 - gnome_idle_monitor_get_idletime@Base 6.0.0 - gnome_idle_monitor_get_type@Base 6.0.0 - gnome_idle_monitor_new@Base 6.0.0 - gnome_idle_monitor_remove_watch@Base 6.0.0 - gnome_installer_check_for_packages@Base 3.4.2 - gnome_installer_install_packages@Base 3.4.2 - gnome_pnp_ids_get_pnp_id@Base 2.0.4 - gnome_pnp_ids_get_type@Base 2.0.4 - gnome_pnp_ids_new@Base 2.0.4 - gnome_rr_config_applicable@Base 2.0.4 - gnome_rr_config_apply_from_filename_with_time@Base 2.0.4 - gnome_rr_config_apply_with_time@Base 2.0.4 - gnome_rr_config_ensure_primary@Base 2.0.4 - gnome_rr_config_equal@Base 2.0.4 - gnome_rr_config_get_auto_scale@Base 4.6.4 - gnome_rr_config_get_backup_filename@Base 2.0.4 - gnome_rr_config_get_base_scale@Base 4.6.4 - gnome_rr_config_get_clone@Base 2.0.4 - gnome_rr_config_get_intended_filename@Base 2.0.4 - gnome_rr_config_get_legacy_filename@Base 4.6.4 - gnome_rr_config_get_outputs@Base 2.0.4 - gnome_rr_config_get_type@Base 2.0.4 - gnome_rr_config_load_current@Base 2.0.4 - gnome_rr_config_load_filename@Base 2.0.4 - gnome_rr_config_match@Base 2.0.4 - gnome_rr_config_new_current@Base 2.0.4 - gnome_rr_config_new_stored@Base 2.0.4 - gnome_rr_config_sanitize@Base 2.0.4 - gnome_rr_config_save@Base 2.0.4 - gnome_rr_config_set_auto_scale@Base 4.6.4 - gnome_rr_config_set_base_scale@Base 4.6.4 - gnome_rr_config_set_clone@Base 2.0.4 - gnome_rr_crtc_can_drive_output@Base 2.0.4 - gnome_rr_crtc_get_current_mode@Base 2.0.4 - gnome_rr_crtc_get_current_rotation@Base 2.0.4 - gnome_rr_crtc_get_gamma@Base 2.0.4 - gnome_rr_crtc_get_id@Base 2.0.4 - gnome_rr_crtc_get_position@Base 2.0.4 - gnome_rr_crtc_get_rotations@Base 2.0.4 - gnome_rr_crtc_get_scale@Base 4.6.4 - gnome_rr_crtc_get_type@Base 2.0.4 - gnome_rr_crtc_set_config_with_time@Base 2.0.4 - gnome_rr_crtc_set_gamma@Base 2.0.4 - gnome_rr_crtc_supports_rotation@Base 2.0.4 - gnome_rr_error_quark@Base 2.0.4 - gnome_rr_labeler_get_rgba_for_output@Base 2.0.4 - gnome_rr_labeler_get_type@Base 2.0.4 - gnome_rr_labeler_hide@Base 2.0.4 - gnome_rr_labeler_new@Base 2.0.4 - gnome_rr_labeler_show@Base 2.0.4 - gnome_rr_mode_get_flags@Base 4.6.4 - gnome_rr_mode_get_freq@Base 2.0.4 - gnome_rr_mode_get_freq_f@Base 4.6.4 - gnome_rr_mode_get_height@Base 2.0.4 - gnome_rr_mode_get_id@Base 2.0.4 - gnome_rr_mode_get_type@Base 2.0.4 - gnome_rr_mode_get_width@Base 2.0.4 - gnome_rr_output_can_clone@Base 2.0.4 - gnome_rr_output_get_backlight@Base 2.0.4 - gnome_rr_output_get_backlight_max@Base 2.0.4 - gnome_rr_output_get_backlight_min@Base 2.0.4 - gnome_rr_output_get_connector_type@Base 2.0.4 - gnome_rr_output_get_crtc@Base 2.0.4 - gnome_rr_output_get_current_mode@Base 2.0.4 - gnome_rr_output_get_edid_data@Base 2.0.4 - gnome_rr_output_get_height_mm@Base 2.0.4 - gnome_rr_output_get_id@Base 2.0.4 - gnome_rr_output_get_ids_from_edid@Base 2.0.4 - gnome_rr_output_get_is_primary@Base 2.0.4 - gnome_rr_output_get_name@Base 2.0.4 - gnome_rr_output_get_position@Base 2.0.4 - gnome_rr_output_get_preferred_mode@Base 2.0.4 - gnome_rr_output_get_type@Base 2.0.4 - gnome_rr_output_get_width_mm@Base 2.0.4 - gnome_rr_output_info_get_aspect_ratio@Base 2.0.4 - gnome_rr_output_info_get_display_name@Base 2.0.4 - gnome_rr_output_info_get_flags@Base 4.6.4 - gnome_rr_output_info_get_geometry@Base 2.0.4 - gnome_rr_output_info_get_name@Base 2.0.4 - gnome_rr_output_info_get_preferred_height@Base 2.0.4 - gnome_rr_output_info_get_preferred_width@Base 2.0.4 - gnome_rr_output_info_get_primary@Base 2.0.4 - gnome_rr_output_info_get_product@Base 2.0.4 - gnome_rr_output_info_get_refresh_rate@Base 2.0.4 - gnome_rr_output_info_get_refresh_rate_f@Base 4.6.4 - gnome_rr_output_info_get_rotation@Base 2.0.4 - gnome_rr_output_info_get_scale@Base 4.6.4 - gnome_rr_output_info_get_serial@Base 2.0.4 - gnome_rr_output_info_get_type@Base 2.0.4 - gnome_rr_output_info_get_vendor@Base 2.0.4 - gnome_rr_output_info_is_active@Base 2.0.4 - gnome_rr_output_info_is_connected@Base 2.0.4 - gnome_rr_output_info_set_active@Base 2.0.4 - gnome_rr_output_info_set_flags@Base 4.6.4 - gnome_rr_output_info_set_geometry@Base 2.0.4 - gnome_rr_output_info_set_primary@Base 2.0.4 - gnome_rr_output_info_set_refresh_rate@Base 2.0.4 - gnome_rr_output_info_set_refresh_rate_f@Base 4.6.4 - gnome_rr_output_info_set_rotation@Base 2.0.4 - gnome_rr_output_info_set_scale@Base 4.6.4 - gnome_rr_output_is_connected@Base 2.0.4 - gnome_rr_output_is_laptop@Base 2.0.4 - gnome_rr_output_list_modes@Base 2.0.4 - gnome_rr_output_set_backlight@Base 2.0.4 - gnome_rr_output_supports_mode@Base 2.0.4 - gnome_rr_screen_calculate_best_global_scale@Base 4.6.4 - gnome_rr_screen_calculate_supported_scales@Base 4.6.4 - gnome_rr_screen_get_crtc_by_id@Base 2.0.4 - gnome_rr_screen_get_dpms_mode@Base 2.0.4 - gnome_rr_screen_get_global_scale@Base 4.6.4 - gnome_rr_screen_get_global_scale_setting@Base 4.6.4 - gnome_rr_screen_get_output_by_id@Base 2.0.4 - gnome_rr_screen_get_output_by_name@Base 2.0.4 - gnome_rr_screen_get_ranges@Base 2.0.4 - gnome_rr_screen_get_timestamps@Base 2.0.4 - gnome_rr_screen_get_type@Base 2.0.4 - gnome_rr_screen_list_clone_modes@Base 2.0.4 - gnome_rr_screen_list_crtcs@Base 2.0.4 - gnome_rr_screen_list_modes@Base 2.0.4 - gnome_rr_screen_list_outputs@Base 2.0.4 - gnome_rr_screen_new@Base 2.0.4 - gnome_rr_screen_refresh@Base 2.0.4 - gnome_rr_screen_set_dpms_mode@Base 2.0.4 - gnome_rr_screen_set_global_scale_setting@Base 4.6.4 - gnome_rr_screen_set_primary_output@Base 2.0.4 - gnome_rr_screen_set_size@Base 2.0.4 - gnome_start_systemd_scope@Base 6.0.0 - gnome_start_systemd_scope_finish@Base 6.0.0 - gnome_wall_clock_get_clock@Base 2.0.4 - gnome_wall_clock_get_clock_for_format@Base 3.8.1 - gnome_wall_clock_get_default_date_format@Base 3.8.1 - gnome_wall_clock_get_default_time_format@Base 3.8.1 - gnome_wall_clock_get_type@Base 2.0.4 - gnome_wall_clock_lctime_format@Base 4.6.4 - gnome_wall_clock_new@Base 3.4.2 - gnome_wall_clock_set_format_string@Base 3.8.1 - gnome_xkb_info_description_for_option@Base 2.0.4 - gnome_xkb_info_free_var_defs@Base 2.0.4 - gnome_xkb_info_get_all_layouts@Base 2.0.4 - gnome_xkb_info_get_all_option_groups@Base 2.0.4 - gnome_xkb_info_get_layout_info@Base 2.0.4 - gnome_xkb_info_get_layout_info_for_language@Base 2.0.4 - gnome_xkb_info_get_options_for_group@Base 2.0.4 - gnome_xkb_info_get_type@Base 2.0.4 - gnome_xkb_info_get_var_defs@Base 2.0.4 - gnome_xkb_info_new@Base 2.0.4 - make_display_name@Base 3.8.1 - meta_dbus_idle_monitor_call_add_idle_watch@Base 6.0.0 - meta_dbus_idle_monitor_call_add_idle_watch_finish@Base 6.0.0 - meta_dbus_idle_monitor_call_add_idle_watch_sync@Base 6.0.0 - meta_dbus_idle_monitor_call_add_user_active_watch@Base 6.0.0 - meta_dbus_idle_monitor_call_add_user_active_watch_finish@Base 6.0.0 - meta_dbus_idle_monitor_call_add_user_active_watch_sync@Base 6.0.0 - meta_dbus_idle_monitor_call_get_idletime@Base 6.0.0 - meta_dbus_idle_monitor_call_get_idletime_finish@Base 6.0.0 - meta_dbus_idle_monitor_call_get_idletime_sync@Base 6.0.0 - meta_dbus_idle_monitor_call_remove_watch@Base 6.0.0 - meta_dbus_idle_monitor_call_remove_watch_finish@Base 6.0.0 - meta_dbus_idle_monitor_call_remove_watch_sync@Base 6.0.0 - meta_dbus_idle_monitor_complete_add_idle_watch@Base 6.0.0 - meta_dbus_idle_monitor_complete_add_user_active_watch@Base 6.0.0 - meta_dbus_idle_monitor_complete_get_idletime@Base 6.0.0 - meta_dbus_idle_monitor_complete_remove_watch@Base 6.0.0 - meta_dbus_idle_monitor_emit_watch_fired@Base 6.0.0 - meta_dbus_idle_monitor_get_type@Base 6.0.0 - meta_dbus_idle_monitor_interface_info@Base 6.0.0 - meta_dbus_idle_monitor_override_properties@Base 6.0.0 - meta_dbus_idle_monitor_proxy_get_type@Base 6.0.0 - meta_dbus_idle_monitor_proxy_new@Base 6.0.0 - meta_dbus_idle_monitor_proxy_new_finish@Base 6.0.0 - meta_dbus_idle_monitor_proxy_new_for_bus@Base 6.0.0 - meta_dbus_idle_monitor_proxy_new_for_bus_finish@Base 6.0.0 - meta_dbus_idle_monitor_proxy_new_for_bus_sync@Base 6.0.0 - meta_dbus_idle_monitor_proxy_new_sync@Base 6.0.0 - meta_dbus_idle_monitor_skeleton_get_type@Base 6.0.0 - meta_dbus_idle_monitor_skeleton_new@Base 6.0.0 - meta_dbus_object_get_idle_monitor@Base 6.0.0 - meta_dbus_object_get_type@Base 6.0.0 - meta_dbus_object_manager_client_get_proxy_type@Base 6.0.0 - meta_dbus_object_manager_client_get_type@Base 6.0.0 - meta_dbus_object_manager_client_new@Base 6.0.0 - meta_dbus_object_manager_client_new_finish@Base 6.0.0 - meta_dbus_object_manager_client_new_for_bus@Base 6.0.0 - meta_dbus_object_manager_client_new_for_bus_finish@Base 6.0.0 - meta_dbus_object_manager_client_new_for_bus_sync@Base 6.0.0 - meta_dbus_object_manager_client_new_sync@Base 6.0.0 - meta_dbus_object_peek_idle_monitor@Base 6.0.0 - meta_dbus_object_proxy_get_type@Base 6.0.0 - meta_dbus_object_proxy_new@Base 6.0.0 - meta_dbus_object_skeleton_get_type@Base 6.0.0 - meta_dbus_object_skeleton_new@Base 6.0.0 - meta_dbus_object_skeleton_set_idle_monitor@Base 6.0.0 diff --git a/libcinnamon-desktop/default-input-sources.h b/libcinnamon-desktop/default-input-sources.h new file mode 100644 index 0000000..ca4700b --- /dev/null +++ b/libcinnamon-desktop/default-input-sources.h @@ -0,0 +1,60 @@ +typedef struct +{ + const gchar *const locale; + const gchar *const type; + const gchar *const id; +} DefaultInputSource; + +static DefaultInputSource default_input_sources[] = +{ + { "ar_DZ", "xkb", "ara+azerty" }, + { "as_IN", "ibus", "m17n:as:phonetic" }, + { "ast_ES", "xkb", "es+ast" }, + { "az_AZ", "xkb", "az" }, + { "be_BY", "xkb", "by" }, + { "bg_BG", "xkb", "bg+phonetic" }, + { "bn_IN", "ibus", "m17n:bn:inscript" }, + { "cat_ES", "xkb", "es+cat" }, + { "cs_CZ", "xkb", "cz" }, + { "de_CH", "xkb", "ch" }, + { "de_DE", "xkb", "de" }, + { "el_CY", "xkb", "gr" }, + { "el_GR", "xkb", "gr" }, + { "en_GB", "xkb", "gb" }, + { "en_US", "xkb", "us" }, + { "en_ZA", "xkb", "za" }, + { "es_ES", "xkb", "es" }, + { "es_GT", "xkb", "latam" }, + { "es_MX", "xkb", "latam" }, + { "fr_BE", "xkb", "be" }, + { "fr_CH", "xkb", "ch+fr" }, + { "fr_FR", "xkb", "fr+oss" }, + { "gl_ES", "xkb", "es" }, + { "gu_IN", "ibus", "m17n:gu:inscript" }, + { "he_IL", "xkb", "il" }, + { "hi_IN", "ibus", "m17n:hi:inscript" }, + { "id_ID", "xkb", "us" }, + { "it_IT", "xkb", "it" }, + { "ja_JP", "ibus", "kkc" }, + { "kn_IN", "ibus", "m17n:kn:kgp" }, + { "ko_KR", "ibus", "hangul" }, + { "mai_IN", "ibus", "m17n:mai:inscript" }, + { "ml_IN", "ibus", "m17n:ml:inscript" }, + { "mr_IN", "ibus", "m17n:mr:inscript" }, + { "nl_NL", "xkb", "us+altgr-intl" }, + { "or_IN", "ibus", "m17n:or:inscript" }, + { "pa_IN", "ibus", "m17n:pa:inscript" }, + { "pl_PL", "xkb", "pl" }, + { "pt_BR", "xkb", "br" }, + { "pt_PT", "xkb", "pt" }, + { "ru_RU", "xkb", "ru" }, + { "sd_IN", "ibus", "m17n:sd:inscript" }, + { "sk_SK", "xkb", "sk" }, + { "ta_IN", "ibus", "m17n:ta:tamil99" }, + { "te_IN", "ibus", "m17n:te:inscript" }, + { "ur_IN", "ibus", "m17n:ur:phonetic" }, + { "zh_CN", "ibus", "libpinyin" }, + { "zh_HK", "ibus", "cangjie" }, + { "zh_TW", "ibus", "libzhuyin" }, + { NULL, NULL, NULL } +}; diff --git a/libcinnamon-desktop/gnome-gettext-portable.c b/libcinnamon-desktop/gnome-gettext-portable.c new file mode 100644 index 0000000..c8bd611 --- /dev/null +++ b/libcinnamon-desktop/gnome-gettext-portable.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2021 Dan Cîrnaț + * + * This program 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ +#include "config.h" + +#include +#ifdef HAVE_XLOCALE +#include +#endif + +#include +#include + +#ifndef HAVE_USELOCALE + +/* + * FIXME: This function does nothing if there's no thread-safe + * alternative to uselocale on some systems (NetBSD). Replace it + * when an implementation becomes available. + */ +locale_t +uselocale (locale_t newloc) +{ + return (locale_t) 0; +} +#endif + +char * +dgettext_l (locale_t locale, + const char *domain, + const char *msgid) +{ + locale_t old_locale = uselocale (locale); + char *ret = dgettext (domain, msgid); + uselocale (old_locale); + return ret; +} + +const gchar * +g_dgettext_l (locale_t locale, + const gchar *domain, + const gchar *msgid) +{ + locale_t old_locale = uselocale (locale); + const gchar *ret = g_dgettext (domain, msgid); + uselocale (old_locale); + return ret; +} + +const gchar * +g_dpgettext_l (locale_t locale, + const gchar *domain, + const gchar *msgctxtid, + gsize msgidoffset) +{ + locale_t old_locale = uselocale (locale); + const gchar *ret = g_dpgettext (domain, msgctxtid, msgidoffset); + uselocale (old_locale); + return ret; +} diff --git a/libcinnamon-desktop/gnome-gettext-portable.h b/libcinnamon-desktop/gnome-gettext-portable.h new file mode 100644 index 0000000..6909888 --- /dev/null +++ b/libcinnamon-desktop/gnome-gettext-portable.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2021 Dan Cîrnaț + * + * This program 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include + +#include +#include + +#ifndef GETTEXT_PACKAGE +#error "You must define GETTEXT_PACKAGE before including gnome-gettext-portable.h; did you forget to include config.h?" +#endif + +G_BEGIN_DECLS + +const char * +dgettext_l (locale_t locale, + const char *domain, + const char *msgid); + +const gchar * +g_dgettext_l (locale_t locale, + const char *domain, + const char *msgid); + +const gchar * +g_dpgettext_l (locale_t locale, + const char *domain, + const char *msgctxtid, + gsize msgidoffset); + +#define L_(Locale,String) ((char *) g_dgettext_l (Locale, GETTEXT_PACKAGE, String)) + +G_END_DECLS diff --git a/libcinnamon-desktop/gnome-languages.c b/libcinnamon-desktop/gnome-languages.c new file mode 100644 index 0000000..5bf1749 --- /dev/null +++ b/libcinnamon-desktop/gnome-languages.c @@ -0,0 +1,1446 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2008 Red Hat, Inc, + * 2007 William Jon McCann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by : William Jon McCann + * Ray Strode + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAVE_XLOCALE +#include +#endif + +#include "gnome-gettext-portable.h" + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include "gnome-languages.h" + +#include +#ifndef __LC_LAST +#define __LC_LAST 13 +#endif + +#define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes" +#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale" + +#include "default-input-sources.h" + +typedef struct _GnomeLocale { + char *id; + char *name; + char *language_code; + char *territory_code; + char *codeset; + char *modifier; +} GnomeLocale; + +static GHashTable *gnome_languages_map; +static GHashTable *gnome_territories_map; +static GHashTable *gnome_available_locales_map; +static GHashTable *gnome_language_count_map; +static GHashTable *gnome_territory_count_map; + +static char * construct_language_name (const char *language, + const char *territory, + const char *codeset, + const char *modifier); + +static gboolean language_name_is_valid (const char *language_name); + +static void +gnome_locale_free (GnomeLocale *locale) +{ + if (locale == NULL) { + return; + } + + g_free (locale->id); + g_free (locale->name); + g_free (locale->codeset); + g_free (locale->modifier); + g_free (locale->language_code); + g_free (locale->territory_code); + g_free (locale); +} + +static char * +normalize_codeset (const char *codeset) +{ + if (codeset == NULL) + return NULL; + + if (g_str_equal (codeset, "UTF-8") || + g_str_equal (codeset, "utf8")) + return g_strdup ("UTF-8"); + + return g_strdup (codeset); +} + +/* Returns TRUE if value was non-empty */ + +static gboolean +match_info_fetch_named_non_empty (GMatchInfo *match_info, + const char *match_name, + char **variable) +{ + g_autofree char *value = NULL; + + value = g_match_info_fetch_named (match_info, match_name); + if (!value || *value == '\0') + return FALSE; + if (variable) + *variable = g_steal_pointer (&value); + return TRUE; +} + +/** + * gnome_parse_locale: + * @locale: a locale string + * @language_codep: (out) (optional) (transfer full): location to + * store the language code, or %NULL + * @country_codep: (out) (optional) (nullable) (transfer full): location to + * store the country code, or %NULL + * @codesetp: (out) (optional) (nullable) (transfer full): location to + * store the codeset, or %NULL + * @modifierp: (out) (optional) (nullable) (transfer full): location to + * store the modifier, or %NULL + * + * Extracts the various components of a locale string in XPG format. + * ([language[_country][.codeset][@modifier]]). See + * http://en.wikipedia.org/wiki/Locale. + * + * Return value: %TRUE if parsing was successful. + * + * Since: 3.8 + */ +gboolean +gnome_parse_locale (const char *locale, + char **language_codep, + char **country_codep, + char **codesetp, + char **modifierp) +{ + g_autoptr(GRegex) regex = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + gboolean ret = FALSE; + + if (locale == NULL) + return ret; + + regex = g_regex_new ("^(?P[A-Za-z][a-z]?[a-z]?)" + "(_(?P[A-Z][A-Z]))?" + "(\\.(?P[A-Za-z0-9][A-Za-z-0-9]*))?" + "(@(?P[a-z]*))?$", + 0, 0, NULL); + g_assert (regex); + + if (!g_regex_match (regex, locale, 0, &match_info)) + return ret; + + if (match_info_fetch_named_non_empty (match_info, "language", language_codep)) + ret = TRUE; + match_info_fetch_named_non_empty (match_info, "territory", country_codep); + match_info_fetch_named_non_empty (match_info, "codeset", codesetp); + match_info_fetch_named_non_empty (match_info, "modifier", modifierp); + + if (codesetp != NULL && *codesetp != NULL) { + g_autofree gchar *normalized_codeset = NULL; + g_autofree gchar *normalized_name = NULL; + + normalized_codeset = normalize_codeset (*codesetp); + normalized_name = construct_language_name (language_codep ? *language_codep : NULL, + country_codep ? *country_codep : NULL, + normalized_codeset, + modifierp ? *modifierp : NULL); + + if (language_name_is_valid (normalized_name)) { + g_free (*codesetp); + *codesetp = g_steal_pointer (&normalized_codeset); + } + } + + return ret; +} + +static char * +construct_language_name (const char *language, + const char *territory, + const char *codeset, + const char *modifier) +{ + char *name; + + g_assert (language != NULL && language[0] != 0); + g_assert (territory == NULL || territory[0] != 0); + g_assert (codeset == NULL || codeset[0] != 0); + g_assert (modifier == NULL || modifier[0] != 0); + + name = g_strdup_printf ("%s%s%s%s%s%s%s", + language, + territory != NULL? "_" : "", + territory != NULL? territory : "", + codeset != NULL? "." : "", + codeset != NULL? codeset : "", + modifier != NULL? "@" : "", + modifier != NULL? modifier : ""); + + return name; +} + +/** + * gnome_normalize_locale: + * @locale: a locale string + * + * Gets the normalized locale string in the form + * [language[_country][.codeset][@modifier]] for @name. + * + * Return value: (transfer full): normalized locale string. Caller + * takes ownership. + * + * Since: 3.8 + */ +char * +gnome_normalize_locale (const char *locale) +{ + char *normalized_name; + gboolean valid; + g_autofree char *language_code = NULL; + g_autofree char *territory_code = NULL; + g_autofree char *codeset = NULL; + g_autofree char *modifier = NULL; + + if (locale[0] == '\0') { + return NULL; + } + + valid = gnome_parse_locale (locale, + &language_code, + &territory_code, + &codeset, &modifier); + if (!valid) + return NULL; + + normalized_name = construct_language_name (language_code, + territory_code, + codeset, modifier); + return normalized_name; +} + +static gboolean +language_name_is_valid (const char *language_name) +{ + locale_t locale; + + if (language_name == NULL) + return FALSE; + + locale = newlocale (LC_MESSAGES_MASK, language_name, (locale_t) 0); + if (locale != (locale_t) 0) { + freelocale (locale); + return TRUE; + } + + return FALSE; +} + +static void +language_name_get_codeset_details (const char *language_name, + char **pcodeset, + gboolean *is_utf8) +{ + locale_t locale; + const char *codeset = NULL; + + if (language_name == NULL) { + language_name = setlocale (LC_MESSAGES, NULL); + } + locale = newlocale (LC_CTYPE_MASK, language_name, (locale_t) 0); + if (locale == (locale_t) 0) + return; + + + codeset = nl_langinfo_l (CODESET, locale); + + if (pcodeset != NULL) { + *pcodeset = g_strdup (codeset); + } + + if (is_utf8 != NULL) { + g_autofree char *normalized_codeset = normalize_codeset (codeset); + + *is_utf8 = strcmp (normalized_codeset, "UTF-8") == 0; + } + + freelocale (locale); +} + +/** + * gnome_language_has_translations: + * @code: an ISO 639 code string + * + * Returns %TRUE if there are translations for language @code. + * + * Return value: %TRUE if there are translations for language @code. + * + * Since: 3.8 + */ +gboolean +gnome_language_has_translations (const char *code) +{ + GDir *dir; + const char *name; + gboolean has_translations; + g_autofree char *path = NULL; + + path = g_build_filename (GNOMELOCALEDIR, code, "LC_MESSAGES", NULL); + + has_translations = FALSE; + dir = g_dir_open (path, 0, NULL); + + if (dir == NULL) { + goto out; + } + + do { + name = g_dir_read_name (dir); + + if (name == NULL) { + break; + } + + if (g_str_has_suffix (name, ".mo")) { + has_translations = TRUE; + break; + } + } while (name != NULL); + g_dir_close (dir); + + out: + return has_translations; +} + +static gboolean +add_locale (const char *language_name, + gboolean utf8_only) +{ + GnomeLocale *locale; + GnomeLocale *old_locale; + g_autofree char *name = NULL; + gboolean is_utf8 = FALSE; + gboolean valid = FALSE; + + g_return_val_if_fail (language_name != NULL, FALSE); + g_return_val_if_fail (*language_name != '\0', FALSE); + + language_name_get_codeset_details (language_name, NULL, &is_utf8); + + if (is_utf8) { + /* If the locale is UTF-8, but it's not explicit in the name, + * append it to avoid compatibility issues, e.g: muslc + * defaults to UTF-8 when missing, while glibc defaults to ISO + */ + if (strchr (language_name, '.') == NULL) + name = g_strdup_printf ("%s.UTF-8", language_name); + else + name = g_strdup (language_name); + } else if (utf8_only) { + + if (strchr (language_name, '.')) + return FALSE; + + /* If the locale name has no dot, assume that its + * encoding part is missing and try again after adding + * ".UTF-8". This catches locale names like "de_DE". + */ + name = g_strdup_printf ("%s.UTF-8", language_name); + + language_name_get_codeset_details (name, NULL, &is_utf8); + if (!is_utf8) + return FALSE; + } else { + name = g_strdup (language_name); + } + + if (!language_name_is_valid (name)) { + g_debug ("Ignoring '%s' as a locale, since it's invalid", name); + return FALSE; + } + + locale = g_new0 (GnomeLocale, 1); + valid = gnome_parse_locale (name, + &locale->language_code, + &locale->territory_code, + &locale->codeset, + &locale->modifier); + if (!valid) { + gnome_locale_free (locale); + return FALSE; + } + + locale->id = construct_language_name (locale->language_code, locale->territory_code, + NULL, locale->modifier); + locale->name = construct_language_name (locale->language_code, locale->territory_code, + locale->codeset, locale->modifier); + + if (!gnome_language_has_translations (locale->name) && + !gnome_language_has_translations (locale->id) && + !gnome_language_has_translations (locale->language_code) && + utf8_only) { + g_debug ("Ignoring '%s' as a locale, since it lacks translations", locale->name); + gnome_locale_free (locale); + return FALSE; + } + + if (!utf8_only) { + g_free (locale->id); + locale->id = g_strdup (locale->name); + } + + old_locale = g_hash_table_lookup (gnome_available_locales_map, locale->id); + if (old_locale != NULL) { + if (strlen (old_locale->name) > strlen (locale->name)) { + gnome_locale_free (locale); + return FALSE; + } + } + + g_hash_table_insert (gnome_available_locales_map, g_strdup (locale->id), locale); + + return TRUE; +} + +static int +select_dirs (const struct dirent *dirent) +{ + int result = 0; + + if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0) { + mode_t mode = 0; + +#ifdef _DIRENT_HAVE_D_TYPE + if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK) { + mode = DTTOIF (dirent->d_type); + } else +#endif + { + struct stat st; + g_autofree char *path = NULL; + + path = g_build_filename (LIBLOCALEDIR, dirent->d_name, NULL); + if (g_stat (path, &st) == 0) { + mode = st.st_mode; + } + } + + result = S_ISDIR (mode); + } + + return result; +} + +static gboolean +collect_locales_from_directory (void) +{ + gboolean found_locales = FALSE; + struct dirent **dirents; + int ndirents; + int cnt; + + ndirents = scandir (LIBLOCALEDIR, &dirents, select_dirs, alphasort); + + for (cnt = 0; cnt < ndirents; ++cnt) { + if (add_locale (dirents[cnt]->d_name, TRUE)) + found_locales = TRUE; + } + + if (ndirents > 0) { + free (dirents); + } + return found_locales; +} + +static gboolean +collect_locales_from_localebin (void) +{ + gboolean found_locales = FALSE; + const gchar *argv[] = { "locale", "-a", NULL }; + gchar **linep; + g_auto (GStrv) lines = NULL; + g_autofree gchar *output = NULL; + + if (g_spawn_sync (NULL, (gchar **) argv, NULL, + G_SPAWN_SEARCH_PATH|G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, &output, NULL, NULL, NULL) == FALSE) + return FALSE; + + g_return_val_if_fail (output != NULL, FALSE); + + lines = g_strsplit (output, "\n", 0); + if (lines) { + linep = lines; + while (*linep) { + if (*linep[0] && add_locale (*linep, TRUE)) + found_locales = TRUE; + linep++; + } + } + + return found_locales; +} + +static void +count_languages_and_territories (void) +{ + gpointer value; + GHashTableIter iter; + + gnome_language_count_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + gnome_territory_count_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + g_hash_table_iter_init (&iter, gnome_available_locales_map); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + GnomeLocale *locale; + + locale = (GnomeLocale *) value; + + if (locale->language_code != NULL) { + int count; + + count = GPOINTER_TO_INT (g_hash_table_lookup (gnome_language_count_map, locale->language_code)); + count++; + g_hash_table_insert (gnome_language_count_map, g_strdup (locale->language_code), GINT_TO_POINTER (count)); + } + + if (locale->territory_code != NULL) { + int count; + + count = GPOINTER_TO_INT (g_hash_table_lookup (gnome_territory_count_map, locale->territory_code)); + count++; + g_hash_table_insert (gnome_territory_count_map, g_strdup (locale->territory_code), GINT_TO_POINTER (count)); + } + } +} + +static void +collect_locales (void) +{ + gboolean found_localebin_locales = FALSE; + gboolean found_dir_locales = FALSE; + + if (gnome_available_locales_map == NULL) { + gnome_available_locales_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gnome_locale_free); + } + + found_localebin_locales = collect_locales_from_localebin (); + + found_dir_locales = collect_locales_from_directory (); + + if (!(found_localebin_locales || found_dir_locales)) { + g_warning ("Could not read list of available locales from libc, " + "guessing possible locales from available translations, " + "but list may be incomplete!"); + } + + count_languages_and_territories (); +} + +static gint +get_language_count (const char *language) +{ + if (gnome_language_count_map == NULL) { + collect_locales (); + } + + return GPOINTER_TO_INT (g_hash_table_lookup (gnome_language_count_map, language)); +} + +static gboolean +is_unique_language (const char *language) +{ + return get_language_count (language) == 1; +} + +static gint +get_territory_count (const char *territory) +{ + if (gnome_territory_count_map == NULL) { + collect_locales (); + } + + return GPOINTER_TO_INT (g_hash_table_lookup (gnome_territory_count_map, territory)); +} + +static gboolean +is_unique_territory (const char *territory) +{ + return get_territory_count (territory) == 1; +} + +static gboolean +is_fallback_language (const char *code) +{ + const char *fallback_language_names[] = { "C", "POSIX", NULL }; + int i; + + for (i = 0; fallback_language_names[i] != NULL; i++) { + if (strcmp (code, fallback_language_names[i]) == 0) { + return TRUE; + } + } + + return FALSE; +} + +static const char * +get_language (const char *code) +{ + const char *name; + int len; + + g_assert (code != NULL); + + if (is_fallback_language (code)) { + return "Unspecified"; + } + + len = strlen (code); + if (len != 2 && len != 3) { + return NULL; + } + + name = (const char *) g_hash_table_lookup (gnome_languages_map, code); + + return name; +} + +static char * +get_first_item_in_semicolon_list (const char *list) +{ + char **items; + char *item; + + /* Some entries in iso codes have multiple values, separated + * by semicolons. Not really sure which one to pick, so + * we just arbitrarily pick the first one. + */ + items = g_strsplit (list, "; ", 2); + + item = g_strdup (items[0]); + g_strfreev (items); + + return item; +} + +static char * +capitalize_utf8_string (const char *str) +{ + char first[8] = { 0 }; + + if (!str) + return NULL; + + g_unichar_to_utf8 (g_unichar_totitle (g_utf8_get_char (str)), first); + + return g_strconcat (first, g_utf8_offset_to_pointer (str, 1), NULL); +} + +static char * +get_translated_language (const char *code, + const char *locale) +{ + const char *language; + char *name; + + language = get_language (code); + + name = NULL; + if (language != NULL) { + const char *translated_name; + locale_t loc = 0; + + if (locale == NULL) { + locale = setlocale (LC_MESSAGES, NULL); + } + loc = newlocale (LC_MESSAGES_MASK, locale, (locale_t) 0); + if (loc == (locale_t) 0) + return NULL; + + if (is_fallback_language (code)) { + name = g_strdup (_("Unspecified")); + } else { + g_autofree char *tmp = NULL; + if (strlen (code) == 2) { + translated_name = dgettext_l (loc, "iso_639", language); + } else { + translated_name = dgettext_l (loc, "iso_639_3", language); + } + tmp = get_first_item_in_semicolon_list (translated_name); + name = capitalize_utf8_string (tmp); + } + + freelocale (loc); + } + + return name; +} + +static const char * +get_territory (const char *code) +{ + const char *name; + int len; + + g_assert (code != NULL); + + len = strlen (code); + if (len != 2 && len != 3) { + return NULL; + } + + name = (const char *) g_hash_table_lookup (gnome_territories_map, code); + + return name; +} + +static char * +get_translated_territory (const char *code, + const char *locale) +{ + const char *territory; + char *name; + + territory = get_territory (code); + + name = NULL; + if (territory != NULL) { + const char *translated_territory; + locale_t loc; + g_autofree char *tmp = NULL; + + if (locale == NULL) { + locale = setlocale (LC_MESSAGES, NULL); + } + loc = newlocale (LC_MESSAGES_MASK, locale, (locale_t) 0); + if (loc == (locale_t) 0) + return NULL; + + translated_territory = dgettext_l (loc, "iso_3166", territory); + tmp = get_first_item_in_semicolon_list (translated_territory); + name = capitalize_utf8_string (tmp); + + freelocale (loc); + } + + return name; +} + +static void +languages_parse_start_tag (GMarkupParseContext *ctx, + const char *element_name, + const char **attr_names, + const char **attr_values, + gpointer user_data, + GError **error) +{ + const char *ccode_longB; + const char *ccode_longT; + const char *ccode; + const char *ccode_id; + const char *lang_common_name; + const char *lang_name; + + if (! (g_str_equal (element_name, "iso_639_entry") || g_str_equal (element_name, "iso_639_3_entry")) + || attr_names == NULL || attr_values == NULL) { + return; + } + + ccode = NULL; + ccode_longB = NULL; + ccode_longT = NULL; + ccode_id = NULL; + lang_common_name = NULL; + lang_name = NULL; + + while (*attr_names && *attr_values) { + if (g_str_equal (*attr_names, "iso_639_1_code")) { + /* skip if empty */ + if (**attr_values) { + if (strlen (*attr_values) != 2) { + return; + } + ccode = *attr_values; + } + } else if (g_str_equal (*attr_names, "iso_639_2B_code")) { + /* skip if empty */ + if (**attr_values) { + if (strlen (*attr_values) != 3) { + return; + } + ccode_longB = *attr_values; + } + } else if (g_str_equal (*attr_names, "iso_639_2T_code")) { + /* skip if empty */ + if (**attr_values) { + if (strlen (*attr_values) != 3) { + return; + } + ccode_longT = *attr_values; + } + } else if (g_str_equal (*attr_names, "id")) { + /* skip if empty */ + if (**attr_values) { + if (strlen (*attr_values) != 2 && + strlen (*attr_values) != 3) { + return; + } + ccode_id = *attr_values; + } + } else if (g_str_equal (*attr_names, "common_name")) { + /* skip if empty */ + if (**attr_values) { + lang_common_name = *attr_values; + } + } else if (g_str_equal (*attr_names, "name")) { + lang_name = *attr_values; + } + + ++attr_names; + ++attr_values; + } + + if (lang_common_name != NULL) { + lang_name = lang_common_name; + } + + if (lang_name == NULL) { + return; + } + + if (ccode != NULL) { + g_hash_table_insert (gnome_languages_map, + g_strdup (ccode), + g_strdup (lang_name)); + } + if (ccode_longB != NULL) { + g_hash_table_insert (gnome_languages_map, + g_strdup (ccode_longB), + g_strdup (lang_name)); + } + if (ccode_longT != NULL) { + g_hash_table_insert (gnome_languages_map, + g_strdup (ccode_longT), + g_strdup (lang_name)); + } + if (ccode_id != NULL) { + g_hash_table_insert (gnome_languages_map, + g_strdup (ccode_id), + g_strdup (lang_name)); + } +} + +static void +territories_parse_start_tag (GMarkupParseContext *ctx, + const char *element_name, + const char **attr_names, + const char **attr_values, + gpointer user_data, + GError **error) +{ + const char *acode_2; + const char *acode_3; + const char *ncode; + const char *territory_common_name; + const char *territory_name; + + if (! g_str_equal (element_name, "iso_3166_entry") || attr_names == NULL || attr_values == NULL) { + return; + } + + acode_2 = NULL; + acode_3 = NULL; + ncode = NULL; + territory_common_name = NULL; + territory_name = NULL; + + while (*attr_names && *attr_values) { + if (g_str_equal (*attr_names, "alpha_2_code")) { + /* skip if empty */ + if (**attr_values) { + if (strlen (*attr_values) != 2) { + return; + } + acode_2 = *attr_values; + } + } else if (g_str_equal (*attr_names, "alpha_3_code")) { + /* skip if empty */ + if (**attr_values) { + if (strlen (*attr_values) != 3) { + return; + } + acode_3 = *attr_values; + } + } else if (g_str_equal (*attr_names, "numeric_code")) { + /* skip if empty */ + if (**attr_values) { + if (strlen (*attr_values) != 3) { + return; + } + ncode = *attr_values; + } + } else if (g_str_equal (*attr_names, "common_name")) { + /* skip if empty */ + if (**attr_values) { + territory_common_name = *attr_values; + } + } else if (g_str_equal (*attr_names, "name")) { + territory_name = *attr_values; + } + + ++attr_names; + ++attr_values; + } + + if (territory_common_name != NULL) { + territory_name = territory_common_name; + } + + if (territory_name == NULL) { + return; + } + + if (acode_2 != NULL) { + g_hash_table_insert (gnome_territories_map, + g_strdup (acode_2), + g_strdup (territory_name)); + } + if (acode_3 != NULL) { + g_hash_table_insert (gnome_territories_map, + g_strdup (acode_3), + g_strdup (territory_name)); + } + if (ncode != NULL) { + g_hash_table_insert (gnome_territories_map, + g_strdup (ncode), + g_strdup (territory_name)); + } +} + +static void +languages_variant_init (const char *variant) +{ + gboolean res; + gsize buf_len; + g_autofree char *buf = NULL; + g_autofree char *filename = NULL; + g_autoptr (GError) error = NULL; + + bindtextdomain (variant, ISO_CODES_LOCALESDIR); + bind_textdomain_codeset (variant, "UTF-8"); + + error = NULL; + filename = g_strdup_printf (ISO_CODES_DATADIR "/%s.xml", variant); + res = g_file_get_contents (filename, + &buf, + &buf_len, + &error); + if (res) { + g_autoptr (GMarkupParseContext) ctx = NULL; + GMarkupParser parser = { languages_parse_start_tag, NULL, NULL, NULL, NULL }; + + ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL); + + error = NULL; + res = g_markup_parse_context_parse (ctx, buf, buf_len, &error); + + if (! res) { + g_warning ("Failed to parse '%s': %s\n", + filename, + error->message); + } + } else { + g_warning ("Failed to load '%s': %s\n", + filename, + error->message); + } +} + +static void +languages_init (void) +{ + if (gnome_languages_map) + return; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + + gnome_languages_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + languages_variant_init ("iso_639"); + languages_variant_init ("iso_639_3"); +} + +static void +territories_init (void) +{ + gboolean res; + gsize buf_len; + g_autofree char *buf = NULL; + g_autoptr (GError) error = NULL; + + if (gnome_territories_map) + return; + + bindtextdomain ("iso_3166", ISO_CODES_LOCALESDIR); + bind_textdomain_codeset ("iso_3166", "UTF-8"); + + gnome_territories_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + error = NULL; + res = g_file_get_contents (ISO_CODES_DATADIR "/iso_3166.xml", + &buf, + &buf_len, + &error); + if (res) { + g_autoptr (GMarkupParseContext) ctx = NULL; + GMarkupParser parser = { territories_parse_start_tag, NULL, NULL, NULL, NULL }; + + ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL); + + error = NULL; + res = g_markup_parse_context_parse (ctx, buf, buf_len, &error); + + if (! res) { + g_warning ("Failed to parse '%s': %s\n", + ISO_CODES_DATADIR "/iso_3166.xml", + error->message); + } + } else { + g_warning ("Failed to load '%s': %s\n", + ISO_CODES_DATADIR "/iso_3166.xml", + error->message); + } +} + +/** + * gnome_get_language_from_locale: + * @locale: a locale string + * @translation: (nullable): a locale string + * + * Gets the language description for @locale. If @translation is + * provided the returned string is translated accordingly. + * + * Return value: (transfer full): the language description. Caller + * takes ownership. + * + * Since: 3.8 + */ +char * +gnome_get_language_from_locale (const char *locale, + const char *translation) +{ + GString *full_language; + g_autofree char *language_code = NULL; + g_autofree char *territory_code = NULL; + g_autofree char *codeset_code = NULL; + g_autofree char *langinfo_codeset = NULL; + g_autofree char *translated_language = NULL; + g_autofree char *translated_territory = NULL; + g_autofree char *modifier = NULL; + g_autofree char *translated_modifier = NULL; + gboolean is_utf8 = TRUE; + + g_return_val_if_fail (locale != NULL, NULL); + g_return_val_if_fail (*locale != '\0', NULL); + + full_language = g_string_new (NULL); + + languages_init (); + territories_init (); + + gnome_parse_locale (locale, + &language_code, + &territory_code, + &codeset_code, + &modifier); + + if (language_code == NULL) { + goto out; + } + + translated_language = get_translated_language (language_code, translation); + if (translated_language == NULL) { + goto out; + } + + full_language = g_string_append (full_language, translated_language); + + if (is_unique_language (language_code)) { + goto out; + } + + if (modifier != NULL) + translated_modifier = gnome_get_translated_modifier (modifier, translation); + if (translated_modifier != NULL) + g_string_append_printf (full_language, " — %s", translated_modifier); + + if (territory_code != NULL) { + translated_territory = get_translated_territory (territory_code, translation); + } + if (translated_territory != NULL) { + g_string_append_printf (full_language, + " (%s)", + translated_territory); + } + + language_name_get_codeset_details (locale, &langinfo_codeset, &is_utf8); + + if (codeset_code == NULL && langinfo_codeset != NULL) { + codeset_code = g_strdup (langinfo_codeset); + } + + if (!is_utf8 && codeset_code) { + g_string_append_printf (full_language, + " [%s]", + codeset_code); + } + + out: + if (full_language->len == 0) { + g_string_free (full_language, TRUE); + return NULL; + } + + return g_string_free (full_language, FALSE); +} + +/** + * gnome_get_country_from_locale: + * @locale: a locale string + * @translation: (nullable): a locale string + * + * Gets the country description for @locale. If @translation is + * provided the returned string is translated accordingly. + * + * Return value: (transfer full): the country description. Caller + * takes ownership. + * + * Since: 3.8 + */ +char * +gnome_get_country_from_locale (const char *locale, + const char *translation) +{ + GString *full_name; + g_autofree char *language_code = NULL; + g_autofree char *territory_code = NULL; + g_autofree char *codeset_code = NULL; + g_autofree char *langinfo_codeset = NULL; + g_autofree char *translated_language = NULL; + g_autofree char *translated_territory = NULL; + g_autofree char *modifier = NULL; + g_autofree char *translated_modifier = NULL; + gboolean is_utf8 = TRUE; + + g_return_val_if_fail (locale != NULL, NULL); + g_return_val_if_fail (*locale != '\0', NULL); + + full_name = g_string_new (NULL); + + languages_init (); + territories_init (); + + gnome_parse_locale (locale, + &language_code, + &territory_code, + &codeset_code, + &modifier); + + if (territory_code == NULL) { + goto out; + } + + translated_territory = get_translated_territory (territory_code, translation); + if (translated_territory == NULL) { + goto out; + } + + g_string_append (full_name, translated_territory); + + if (is_unique_territory (territory_code)) { + goto out; + } + + if (language_code != NULL) { + translated_language = get_translated_language (language_code, translation); + } + if (translated_language != NULL) { + g_string_append_printf (full_name, + " (%s", + translated_language); + } + + if (modifier != NULL) + translated_modifier = gnome_get_translated_modifier (modifier, translation); + if (translated_modifier != NULL) + g_string_append_printf (full_name, " — %s", translated_modifier); + + if (translated_language != NULL) + g_string_append_printf (full_name, ")"); + + language_name_get_codeset_details (translation, &langinfo_codeset, &is_utf8); + + if (codeset_code == NULL && langinfo_codeset != NULL) { + codeset_code = g_strdup (langinfo_codeset); + } + + if (!is_utf8 && codeset_code) { + g_string_append_printf (full_name, + " [%s]", + codeset_code); + } + + out: + if (full_name->len == 0) { + g_string_free (full_name, TRUE); + return NULL; + } + + return g_string_free (full_name, FALSE); +} + +/** + * gnome_get_all_locales: + * + * Gets all locales. + * + * Return value: (array zero-terminated=1) (element-type utf8) (transfer full): + * a newly allocated %NULL-terminated string array containing the + * all locales. Free with g_strfreev(). + * + * Since: 3.8 + */ +char ** +gnome_get_all_locales (void) +{ + GHashTableIter iter; + gpointer key, value; + GPtrArray *array; + + if (gnome_available_locales_map == NULL) { + collect_locales (); + } + + array = g_ptr_array_new (); + g_hash_table_iter_init (&iter, gnome_available_locales_map); + while (g_hash_table_iter_next (&iter, &key, &value)) { + GnomeLocale *locale; + + locale = (GnomeLocale *) value; + + g_ptr_array_add (array, g_strdup (locale->name)); + } + g_ptr_array_add (array, NULL); + + return (char **) g_ptr_array_free (array, FALSE); +} + +/** + * gnome_get_language_from_code: + * @code: an ISO 639 code string + * @translation: (nullable): a locale string + * + * Gets the language name for @code. If @translation is provided the + * returned string is translated accordingly. + * + * Return value: (transfer full): the language name. Caller takes + * ownership. + * + * Since: 3.8 + */ +char * +gnome_get_language_from_code (const char *code, + const char *translation) +{ + g_return_val_if_fail (code != NULL, NULL); + + languages_init (); + + return get_translated_language (code, translation); +} + +/** + * gnome_get_country_from_code: + * @code: an ISO 3166 code string + * @translation: (nullable): a locale string + * + * Gets the country name for @code. If @translation is provided the + * returned string is translated accordingly. + * + * Return value: (transfer full): the country name. Caller takes + * ownership. + * + * Since: 3.8 + */ +char * +gnome_get_country_from_code (const char *code, + const char *translation) +{ + g_return_val_if_fail (code != NULL, NULL); + + territories_init (); + + return get_translated_territory (code, translation); +} + +/** + * gnome_get_translated_modifier: + * @modifier: the modifier part of a locale name + * @translation: (nullable): a locale string + * + * Gets a translation of the raw @modifier string. If @translation + * is provided the returned string is translated accordingly. + * + * Return value: (transfer full): the translated modifier string. + * Caller takes ownership. + * + * Since: 3.34 + */ +char * +gnome_get_translated_modifier (const char *modifier, + const char *translation) +{ + char *retval; + GHashTable *modifiers_map; + locale_t loc; + + g_return_val_if_fail (modifier != NULL, NULL); + + if (translation == NULL) { + translation = setlocale (LC_MESSAGES, NULL); + } + loc = newlocale (LC_MESSAGES_MASK, translation, (locale_t) 0); + if (loc == (locale_t) 0) { + return NULL; + } + + /* Modifiers as listed in glibc's SUPPORTED file: + * https://sourceware.org/git/?p=glibc.git;a=blob;f=localedata/SUPPORTED;hb=HEAD + * (except for @euro, which would be superfluous in this context). */ + modifiers_map = g_hash_table_new (g_str_hash, g_str_equal); + + /* TRANSLATORS: Used to distinguish the labels representing the gez_ER + and gez_ET locales from gez_ER@abegede respective gez_ET@abegede. The + difference is related to collation. */ + g_hash_table_insert (modifiers_map, "abegede", L_(loc, "Abegede")); + /* TRANSLATORS: Used to distinguish Cyrillic from Latin written language variants. */ + g_hash_table_insert (modifiers_map, "cyrillic", L_(loc, "Cyrillic")); + /* TRANSLATORS: Also known as "Nagari", a written variant for many languages + of the Indian subcontinent. See: + https://en.wikipedia.org/wiki/Devanagari */ + g_hash_table_insert (modifiers_map, "devanagari", L_(loc, "Devanagari")); + /* TRANSLATORS: Used to distinguish the label representing the tt_RU + locale from tt_RU@iqtelif. It's a special alphabet for Tatar. */ + g_hash_table_insert (modifiers_map, "iqtelif", L_(loc, "IQTElif")); + /* TRANSLATORS: The alphabet/script, not the language. Used to distinguish + Latin from Cyrillic written language variants. */ + g_hash_table_insert (modifiers_map, "latin", L_(loc, "Latin")); + /* TRANSLATORS: "Saho" is a variant of the Afar language. Used to + distinguish the label representing the aa_ER locale from aa_ER@saaho. */ + g_hash_table_insert (modifiers_map, "saaho", L_(loc, "Saho")); + /* TRANSLATORS: "Valencia" is a dialect of the Catalan language spoken + in Valencia. Used to distinguish the label representing the ca_ES + locale from ca_ES@valencia. */ + g_hash_table_insert (modifiers_map, "valencia", L_(loc, "Valencia")); + + if (g_hash_table_contains (modifiers_map, modifier)) + retval = g_strdup (g_hash_table_lookup (modifiers_map, modifier)); + else + retval = g_strdup (modifier); + + g_hash_table_destroy (modifiers_map); + + freelocale (loc); + + return retval; +} + +/** + * gnome_get_input_source_from_locale: + * @locale: a locale string + * @type: (out) (transfer none): location to store the input source + * type + * @id: (out) (transfer none): location to store the input source + * identifier + * + * Gets the default input source's type and identifier for a given + * locale. + * + * Return value: %TRUE if a input source exists or %FALSE otherwise. + * + * Since: 3.8 + */ +gboolean +gnome_get_input_source_from_locale (const char *locale, + const char **type, + const char **id) +{ + static GHashTable *table = NULL; + DefaultInputSource *dis; + g_autofree gchar *l_code = NULL; + g_autofree gchar *c_code = NULL; + g_autofree gchar *key = NULL; + gint i; + + g_return_val_if_fail (locale != NULL, FALSE); + g_return_val_if_fail (type != NULL, FALSE); + g_return_val_if_fail (id != NULL, FALSE); + + if (!table) { + table = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; default_input_sources[i].id; ++i) { + dis = &default_input_sources[i]; + g_hash_table_insert (table, (gpointer) dis->locale, dis); + } + } + + if (!gnome_parse_locale (locale, &l_code, &c_code, NULL, NULL)) + return FALSE; + + key = g_strconcat (l_code, "_", c_code, NULL); + + dis = g_hash_table_lookup (table, key); + if (dis) { + *type = dis->type; + *id = dis->id; + } + return dis != NULL; +} \ No newline at end of file diff --git a/libcinnamon-desktop/gnome-languages.h b/libcinnamon-desktop/gnome-languages.h new file mode 100644 index 0000000..97f4af5 --- /dev/null +++ b/libcinnamon-desktop/gnome-languages.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2008 Red Hat, Inc. + * Copyright 2007 William Jon McCann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: Ray Strode + * William Jon McCann + */ + +#ifndef __GNOME_LANGUAGES_H +#define __GNOME_LANGUAGES_H + +#ifndef GNOME_DESKTOP_USE_UNSTABLE_API +#error This is unstable API. You must define GNOME_DESKTOP_USE_UNSTABLE_API before including gnome-languages.h +#endif + +#include + +G_BEGIN_DECLS + +char * gnome_get_language_from_locale (const char *locale, + const char *translation); +char * gnome_get_country_from_locale (const char *locale, + const char *translation); +char ** gnome_get_all_locales (void); +gboolean gnome_parse_locale (const char *locale, + char **language_codep, + char **country_codep, + char **codesetp, + char **modifierp); +char * gnome_normalize_locale (const char *locale); +gboolean gnome_language_has_translations (const char *code); +char * gnome_get_language_from_code (const char *code, + const char *translation); +char * gnome_get_country_from_code (const char *code, + const char *translation); +char * gnome_get_translated_modifier (const char *modifier, + const char *translation); +gboolean gnome_get_input_source_from_locale (const char *locale, + const char **type, + const char **id); + +G_END_DECLS + +#endif /* __GNOME_LANGUAGES_H */ diff --git a/libcinnamon-desktop/gnome-pnp-ids.c b/libcinnamon-desktop/gnome-pnp-ids.c index 34465f4..e3a9c45 100644 --- a/libcinnamon-desktop/gnome-pnp-ids.c +++ b/libcinnamon-desktop/gnome-pnp-ids.c @@ -20,226 +20,25 @@ #include "config.h" #include - -#include "gnome-pnp-ids.h" +#include +#ifdef HAVE_UDEV +#include +#endif static void gnome_pnp_ids_finalize (GObject *object); struct _GnomePnpIdsPrivate { - gchar *table_data; - GHashTable *pnp_table; +#ifdef HAVE_UDEV + struct udev *udev; + struct udev_hwdb *hwdb; +#else + char *placeholder; +#endif /* HAVE_UDEV */ }; -static gpointer gnome_pnp_ids_object = NULL; - G_DEFINE_TYPE_WITH_PRIVATE (GnomePnpIds, gnome_pnp_ids, G_TYPE_OBJECT) -typedef struct Vendor Vendor; -struct Vendor -{ - const char vendor_id[4]; - const char vendor_name[28]; -}; - -/* This list of vendor codes derived from lshw - * - * http://ezix.org/project/wiki/HardwareLiSter - * - * Note: we now prefer to use data coming from hwdata (and shipped with - * gnome-desktop). See - * http://git.fedorahosted.org/git/?p=hwdata.git;a=blob_plain;f=pnp.ids;hb=HEAD - * All contributions to the list of vendors should go there. - */ -static const struct Vendor vendors[] = -{ - { "AIC", "AG Neovo" }, - { "ACR", "Acer" }, - { "DEL", "DELL" }, - { "SAM", "SAMSUNG" }, - { "SNY", "SONY" }, - { "SEC", "Epson" }, - { "WAC", "Wacom" }, - { "NEC", "NEC" }, - { "CMO", "CMO" }, /* Chi Mei */ - { "BNQ", "BenQ" }, - - { "ABP", "Advansys" }, - { "ACC", "Accton" }, - { "ACE", "Accton" }, - { "ADP", "Adaptec" }, - { "ADV", "AMD" }, - { "AIR", "AIR" }, - { "AMI", "AMI" }, - { "ASU", "ASUS" }, - { "ATI", "ATI" }, - { "ATK", "Allied Telesyn" }, - { "AZT", "Aztech" }, - { "BAN", "Banya" }, - { "BRI", "Boca Research" }, - { "BUS", "Buslogic" }, - { "CCI", "Cache Computers Inc." }, - { "CHA", "Chase" }, - { "CMD", "CMD Technology, Inc." }, - { "COG", "Cogent" }, - { "CPQ", "Compaq" }, - { "CRS", "Crescendo" }, - { "CSC", "Crystal" }, - { "CSI", "CSI" }, - { "CTL", "Creative Labs" }, - { "DBI", "Digi" }, - { "DEC", "Digital Equipment" }, - { "DBK", "Databook" }, - { "EGL", "Eagle Technology" }, - { "ELS", "ELSA" }, - { "ESS", "ESS" }, - { "FAR", "Farallon" }, - { "FDC", "Future Domain" }, - { "HWP", "Hewlett-Packard" }, - { "IBM", "IBM" }, - { "INT", "Intel" }, - { "ISA", "Iomega" }, - { "LEN", "Lenovo" }, - { "MDG", "Madge" }, - { "MDY", "Microdyne" }, - { "MET", "Metheus" }, - { "MIC", "Micronics" }, - { "MLX", "Mylex" }, - { "NVL", "Novell" }, - { "OLC", "Olicom" }, - { "PRO", "Proteon" }, - { "RII", "Racal" }, - { "RTL", "Realtek" }, - { "SCM", "SCM" }, - { "SKD", "SysKonnect" }, - { "SGI", "SGI" }, - { "SMC", "SMC" }, - { "SNI", "Siemens Nixdorf" }, - { "STL", "Stallion Technologies" }, - { "SUN", "Sun" }, - { "SUP", "SupraExpress" }, - { "SVE", "SVEC" }, - { "TCC", "Thomas-Conrad" }, - { "TCI", "Tulip" }, - { "TCM", "3Com" }, - { "TCO", "Thomas-Conrad" }, - { "TEC", "Tecmar" }, - { "TRU", "Truevision" }, - { "TOS", "Toshiba" }, - { "TYN", "Tyan" }, - { "UBI", "Ungermann-Bass" }, - { "USC", "UltraStor" }, - { "VDM", "Vadem" }, - { "VMI", "Vermont" }, - { "WDC", "Western Digital" }, - { "ZDS", "Zeos" }, - - /* From http://faydoc.tripod.com/structures/01/0136.htm */ - { "ACT", "Targa" }, - { "ADI", "ADI" }, - { "AOC", "AOC Intl" }, - { "API", "Acer America" }, - { "APP", "Apple Computer" }, - { "ART", "ArtMedia" }, - { "AST", "AST Research" }, - { "CPL", "Compal" }, - { "CTX", "Chuntex Electronic Co." }, - { "DPC", "Delta Electronics" }, - { "DWE", "Daewoo" }, - { "ECS", "ELITEGROUP" }, - { "EIZ", "EIZO" }, - { "FCM", "Funai" }, - { "GSM", "LG Electronics" }, - { "GWY", "Gateway 2000" }, - { "HEI", "Hyundai" }, - { "HIT", "Hitachi" }, - { "HSL", "Hansol" }, - { "HTC", "Hitachi" }, - { "ICL", "Fujitsu ICL" }, - { "IVM", "Idek Iiyama" }, - { "KFC", "KFC Computek" }, - { "LKM", "ADLAS" }, - { "LNK", "LINK Tech" }, - { "LTN", "Lite-On" }, - { "MAG", "MAG InnoVision" }, - { "MAX", "Maxdata" }, - { "MEI", "Panasonic" }, - { "MEL", "Mitsubishi" }, - { "MIR", "miro" }, - { "MTC", "MITAC" }, - { "NAN", "NANAO" }, - { "NEC", "NEC Tech" }, - { "NOK", "Nokia" }, - { "OQI", "OPTIQUEST" }, - { "PBN", "Packard Bell" }, - { "PGS", "Princeton" }, - { "PHL", "Philips" }, - { "REL", "Relisys" }, - { "SDI", "Samtron" }, - { "SMI", "Smile" }, - { "SPT", "Sceptre" }, - { "SRC", "Shamrock Technology" }, - { "STP", "Sceptre" }, - { "TAT", "Tatung" }, - { "TRL", "Royal Information Company" }, - { "TSB", "Toshiba, Inc." }, - { "UNM", "Unisys" }, - { "VSC", "ViewSonic" }, - { "WTC", "Wen Tech" }, - { "ZCM", "Zenith Data Systems" }, - - { "???", "Unknown" }, -}; - -static gboolean -gnome_pnp_ids_load (GnomePnpIds *pnp_ids, GError **error) -{ - gchar *retval = NULL; - GnomePnpIdsPrivate *priv = pnp_ids->priv; - guint i; - - /* load the contents */ - g_debug ("loading: %s", PNP_IDS); - if (g_file_get_contents (PNP_IDS, &priv->table_data, NULL, error) == FALSE) - return FALSE; - - /* parse into lines */ - retval = priv->table_data; - for (i = 0; priv->table_data[i] != '\0'; i++) { - - /* ignore */ - if (priv->table_data[i] != '\n') - continue; - - /* convert newline to NULL */ - priv->table_data[i] = '\0'; - - /* the ID to text is a fixed offset */ - if (retval[0] && retval[1] && retval[2] && retval[3] == '\t' && retval[4]) { - retval[3] = '\0'; - g_hash_table_insert (priv->pnp_table, - retval, - retval+4); - retval = &priv->table_data[i+1]; - } - } - - return TRUE; -} - -static const char * -find_vendor (const char *pnp_id) -{ - guint i; - - for (i = 0; i < G_N_ELEMENTS (vendors); i++) { - if (g_strcmp0 (vendors[i].vendor_id, pnp_id) == 0) - return vendors[i].vendor_name; - } - - return NULL; -} - /** * gnome_pnp_ids_get_pnp_id: * @pnp_ids: a #GnomePnpIds object @@ -253,29 +52,32 @@ find_vendor (const char *pnp_id) gchar * gnome_pnp_ids_get_pnp_id (GnomePnpIds *pnp_ids, const gchar *pnp_id) { +#ifdef HAVE_UDEV GnomePnpIdsPrivate *priv = pnp_ids->priv; - const char *found; - guint size; - - g_return_val_if_fail (GNOME_IS_PNP_IDSS (pnp_ids), NULL); - g_return_val_if_fail (pnp_id != NULL, NULL); - - /* if table is empty, try to load it */ - size = g_hash_table_size (priv->pnp_table); - if (size == 0) { - if (gnome_pnp_ids_load (pnp_ids, NULL) == FALSE) - return NULL; - } - - /* look this up in the table */ - found = g_hash_table_lookup (priv->pnp_table, pnp_id); - if (found == NULL) { - found = find_vendor (pnp_id); - if (found == NULL) - return NULL; - } - - return g_strdup (found); + struct udev_list_entry *list_entry, *l; + char *modalias; + char *ret = NULL; + + modalias = g_strdup_printf ("acpi:%s:", pnp_id); + list_entry = udev_hwdb_get_properties_list_entry(priv->hwdb, modalias, 0); + g_free (modalias); + if (list_entry == NULL) + return ret; + + /* Try to get the model specific string */ + l = udev_list_entry_get_by_name (list_entry, "ID_MODEL_FROM_DATABASE"); + if (l == NULL) + l = udev_list_entry_get_by_name (list_entry, "ID_VENDOR_FROM_DATABASE"); + + if (l == NULL) + return ret; + + ret = g_strdup (udev_list_entry_get_value (l)); + + return ret; +#else + return g_strdup ("Undefined"); +#endif } static void @@ -289,23 +91,22 @@ static void gnome_pnp_ids_init (GnomePnpIds *pnp_ids) { pnp_ids->priv = gnome_pnp_ids_get_instance_private (pnp_ids); - - /* we don't keep malloc'd data in the hash; instead we read it - * out into priv->table_data and then link to it in the hash */ - pnp_ids->priv->pnp_table = g_hash_table_new_full (g_str_hash, - g_str_equal, - NULL, - NULL); +#ifdef HAVE_UDEV + pnp_ids->priv->udev = udev_new(); + pnp_ids->priv->hwdb = udev_hwdb_new (pnp_ids->priv->udev); +#endif } static void gnome_pnp_ids_finalize (GObject *object) { +#ifdef HAVE_UDEV GnomePnpIds *pnp_ids = GNOME_PNP_IDS (object); GnomePnpIdsPrivate *priv = pnp_ids->priv; - g_free (priv->table_data); - g_hash_table_unref (priv->pnp_table); + g_clear_pointer (&priv->udev, udev_unref); + g_clear_pointer (&priv->hwdb, udev_hwdb_unref); +#endif G_OBJECT_CLASS (gnome_pnp_ids_parent_class)->finalize (object); } @@ -321,12 +122,6 @@ gnome_pnp_ids_finalize (GObject *object) GnomePnpIds * gnome_pnp_ids_new (void) { - if (gnome_pnp_ids_object != NULL) { - g_object_ref (gnome_pnp_ids_object); - } else { - gnome_pnp_ids_object = g_object_new (GNOME_TYPE_PNP_IDSS, NULL); - g_object_add_weak_pointer (gnome_pnp_ids_object, &gnome_pnp_ids_object); - } - return GNOME_PNP_IDS (gnome_pnp_ids_object); + return g_object_new (GNOME_TYPE_PNP_IDS, NULL); } diff --git a/libcinnamon-desktop/gnome-pnp-ids.h b/libcinnamon-desktop/gnome-pnp-ids.h index 6e249a6..e204b21 100644 --- a/libcinnamon-desktop/gnome-pnp-ids.h +++ b/libcinnamon-desktop/gnome-pnp-ids.h @@ -24,12 +24,12 @@ G_BEGIN_DECLS -#define GNOME_TYPE_PNP_IDSS (gnome_pnp_ids_get_type ()) -#define GNOME_PNP_IDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNOME_TYPE_PNP_IDSS, GnomePnpIds)) -#define GNOME_PNP_IDS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GNOME_TYPE_PNP_IDSS, GnomePnpIdsClass)) -#define GNOME_IS_PNP_IDSS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNOME_TYPE_PNP_IDSS)) -#define GNOME_IS_PNP_IDSS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNOME_TYPE_PNP_IDSS)) -#define GNOME_PNP_IDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GNOME_TYPE_PNP_IDSS, GnomePnpIdsClass)) +#define GNOME_TYPE_PNP_IDS (gnome_pnp_ids_get_type ()) +#define GNOME_PNP_IDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNOME_TYPE_PNP_IDS, GnomePnpIds)) +#define GNOME_PNP_IDS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GNOME_TYPE_PNP_IDS, GnomePnpIdsClass)) +#define GNOME_IS_PNP_IDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNOME_TYPE_PNP_IDS)) +#define GNOME_IS_PNP_IDS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNOME_TYPE_PNP_IDS)) +#define GNOME_PNP_IDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GNOME_TYPE_PNP_IDS, GnomePnpIdsClass)) #define GNOME_PNP_IDS_ERROR (gnome_pnp_ids_error_quark ()) typedef struct _GnomePnpIdsPrivate GnomePnpIdsPrivate; @@ -47,6 +47,8 @@ struct _GnomePnpIdsClass GObjectClass parent_class; }; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GnomePnpIds, g_object_unref) + GType gnome_pnp_ids_get_type (void); GnomePnpIds *gnome_pnp_ids_new (void); gchar *gnome_pnp_ids_get_pnp_id (GnomePnpIds *pnp_ids, diff --git a/libcinnamon-desktop/gnome-rr-config.c b/libcinnamon-desktop/gnome-rr-config.c index 35fd948..9f96ca5 100644 --- a/libcinnamon-desktop/gnome-rr-config.c +++ b/libcinnamon-desktop/gnome-rr-config.c @@ -1,8 +1,8 @@ /* gnome-rr-config.c * -*- c-basic-offset: 4 -*- * - * Copyright 2007, 2008, Red Hat, Inc. - * Copyright 2010 Giovanni Campagna + * Copyright 2007, 2008, 2013 Red Hat, Inc. + * Copyright 2010 Giovanni Campagna * * This file is part of the Gnome Library. * @@ -30,23 +30,16 @@ #include #include #include -#include #include #include -#include -#include - #include "gnome-rr-config.h" -#include "edid.h" #include "gnome-rr-private.h" #define CONFIG_INTENDED_BASENAME "cinnamon-monitors.xml" #define CONFIG_BACKUP_BASENAME "cinnamon-monitors.xml.backup" -#define CONFIG_LEGACY_BASENAME "monitors.xml" -#define BASE_SCALE_NOT_CONFIGURED 0 /* Look for DPI_FALLBACK in: * http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/gsd-xsettings-manager.c * for the reasoning */ @@ -77,22 +70,14 @@ * */ -/* A helper wrapper around the GMarkup parser stuff */ -static gboolean parse_file_gmarkup (const gchar *file, - const GMarkupParser *parser, - gpointer data, - GError **err); - typedef struct CrtcAssignment CrtcAssignment; static gboolean crtc_assignment_apply (CrtcAssignment *assign, - guint32 timestamp, - GError **error, - guint *global_scale); -static CrtcAssignment *crtc_assignment_new (GnomeRRConfig *config, - GnomeRRScreen *screen, - GnomeRROutputInfo **outputs, - GError **error); + gboolean persistent, + GError **error); +static CrtcAssignment *crtc_assignment_new (GnomeRRScreen *screen, + GnomeRROutputInfo **outputs, + GError **error); static void crtc_assignment_free (CrtcAssignment *assign); enum { @@ -103,411 +88,12 @@ enum { G_DEFINE_TYPE_WITH_PRIVATE (GnomeRRConfig, gnome_rr_config, G_TYPE_OBJECT) -typedef struct Parser Parser; - -/* Parser for monitor configurations */ -struct Parser -{ - int config_file_version; - GnomeRROutputInfo * output; - GnomeRRConfig * configuration; - GPtrArray * outputs; - GPtrArray * configurations; - GQueue * stack; -}; - -static int -parse_int (const char *text) -{ - return strtol (text, NULL, 0); -} - -static guint64 -parse_uint64 (const char *text) -{ - return strtoul (text, NULL, 0); -} - -static double -parse_double (const char *text) -{ - return strtod (text, NULL); -} - -static gboolean -stack_is (Parser *parser, - const char *s1, - ...) -{ - GList *stack = NULL; - const char *s; - GList *l1, *l2; - va_list args; - - stack = g_list_prepend (stack, (gpointer)s1); - - va_start (args, s1); - - s = va_arg (args, const char *); - while (s) - { - stack = g_list_prepend (stack, (gpointer)s); - s = va_arg (args, const char *); - } - - va_end (args); - - l1 = stack; - l2 = parser->stack->head; - - while (l1 && l2) - { - if (strcmp (l1->data, l2->data) != 0) - { - g_list_free (stack); - return FALSE; - } - - l1 = l1->next; - l2 = l2->next; - } - - g_list_free (stack); - - return (!l1 && !l2); -} - -static void -handle_start_element (GMarkupParseContext *context, - const gchar *name, - const gchar **attr_names, - const gchar **attr_values, - gpointer user_data, - GError **err) -{ - Parser *parser = user_data; - - if (strcmp (name, "output") == 0) - { - int i; - g_assert (parser->output == NULL); - - parser->output = g_object_new (GNOME_TYPE_RR_OUTPUT_INFO, NULL); - parser->output->priv->rotation = 0; - - for (i = 0; attr_names[i] != NULL; ++i) - { - if (strcmp (attr_names[i], "name") == 0) - { - parser->output->priv->name = g_strdup (attr_values[i]); - break; - } - } - - if (!parser->output->priv->name) - { - /* This really shouldn't happen, but it's better to make - * something up than to crash later. - */ - g_warning ("Malformed monitor configuration file"); - - parser->output->priv->name = g_strdup ("default"); - } - parser->output->priv->connected = FALSE; - parser->output->priv->on = FALSE; - parser->output->priv->primary = FALSE; - } - else if (strcmp (name, "configuration") == 0) - { - g_assert (parser->configuration == NULL); - - parser->configuration = g_object_new (GNOME_TYPE_RR_CONFIG, NULL); - parser->configuration->priv->clone = FALSE; - parser->configuration->priv->outputs = NULL; - } - else if (strcmp (name, "monitors") == 0) - { - int i; - - for (i = 0; attr_names[i] != NULL; i++) - { - if (strcmp (attr_names[i], "version") == 0) - { - parser->config_file_version = parse_int (attr_values[i]); - break; - } - } - } - - g_queue_push_tail (parser->stack, g_strdup (name)); -} - -static void -handle_end_element (GMarkupParseContext *context, - const gchar *name, - gpointer user_data, - GError **err) -{ - Parser *parser = user_data; - - if (strcmp (name, "output") == 0) - { - /* If no rotation properties were set, just use GNOME_RR_ROTATION_0 */ - if (parser->output->priv->rotation == 0) - parser->output->priv->rotation = GNOME_RR_ROTATION_0; - - g_ptr_array_add (parser->outputs, parser->output); - - parser->output = NULL; - } - else if (strcmp (name, "configuration") == 0) - { - g_ptr_array_add (parser->outputs, NULL); - parser->configuration->priv->outputs = - (GnomeRROutputInfo **)g_ptr_array_free (parser->outputs, FALSE); - parser->outputs = g_ptr_array_new (); - g_ptr_array_add (parser->configurations, parser->configuration); - parser->configuration = NULL; - } - - g_free (g_queue_pop_tail (parser->stack)); -} - -#define TOPLEVEL_ELEMENT (parser->config_file_version > 0 ? "monitors" : NULL) - -static void -handle_text (GMarkupParseContext *context, - const gchar *text, - gsize text_len, - gpointer user_data, - GError **err) -{ - Parser *parser = user_data; - - if (stack_is (parser, "vendor", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->output->priv->connected = TRUE; - - strncpy ((gchar*) parser->output->priv->vendor, text, 3); - parser->output->priv->vendor[3] = 0; - } - else if (stack_is (parser, "clone", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - if (strcmp (text, "yes") == 0) - parser->configuration->priv->clone = TRUE; - } - else if (stack_is (parser, "base_scale", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->configuration->priv->base_scale = (guint) parse_uint64 (text); - } - else if (stack_is (parser, "product", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->output->priv->connected = TRUE; - - parser->output->priv->product = parse_int (text); - } - else if (stack_is (parser, "serial", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->output->priv->connected = TRUE; - - parser->output->priv->serial = parse_uint64 (text); - } - else if (stack_is (parser, "width", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->output->priv->on = TRUE; - - parser->output->priv->width = parse_int (text); - } - else if (stack_is (parser, "x", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->output->priv->on = TRUE; - - parser->output->priv->x = parse_int (text); - } - else if (stack_is (parser, "y", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->output->priv->on = TRUE; - - parser->output->priv->y = parse_int (text); - } - else if (stack_is (parser, "scale", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->output->priv->on = TRUE; - - parser->output->priv->scale = parse_double (text); - } - else if (stack_is (parser, "height", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->output->priv->on = TRUE; - - parser->output->priv->height = parse_int (text); - } - else if (stack_is (parser, "rate", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - parser->output->priv->on = TRUE; - - parser->output->priv->rate = parse_double (text); - } - else if (stack_is (parser, "rotation", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - if (strcmp (text, "normal") == 0) - { - parser->output->priv->rotation |= GNOME_RR_ROTATION_0; - } - else if (strcmp (text, "left") == 0) - { - parser->output->priv->rotation |= GNOME_RR_ROTATION_90; - } - else if (strcmp (text, "upside_down") == 0) - { - parser->output->priv->rotation |= GNOME_RR_ROTATION_180; - } - else if (strcmp (text, "right") == 0) - { - parser->output->priv->rotation |= GNOME_RR_ROTATION_270; - } - } - else if (stack_is (parser, "reflect_x", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - if (strcmp (text, "yes") == 0) - { - parser->output->priv->rotation |= GNOME_RR_REFLECT_X; - } - } - else if (stack_is (parser, "reflect_y", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - if (strcmp (text, "yes") == 0) - { - parser->output->priv->rotation |= GNOME_RR_REFLECT_Y; - } - } - else if (stack_is (parser, "primary", "output", "configuration", TOPLEVEL_ELEMENT, NULL)) - { - if (strcmp (text, "yes") == 0) - { - parser->output->priv->primary = TRUE; - } - } - else - { - /* Ignore other properties so we can expand the format in the future */ - } -} - -static void -parser_free (Parser *parser) -{ - int i; - GList *list; - - g_assert (parser != NULL); - - if (parser->output) - g_object_unref (parser->output); - - if (parser->configuration) - g_object_unref (parser->configuration); - - for (i = 0; i < parser->outputs->len; ++i) - { - GnomeRROutputInfo *output = parser->outputs->pdata[i]; - - g_object_unref (output); - } - - g_ptr_array_free (parser->outputs, TRUE); - - for (i = 0; i < parser->configurations->len; ++i) - { - GnomeRRConfig *config = parser->configurations->pdata[i]; - - g_object_unref (config); - } - - g_ptr_array_free (parser->configurations, TRUE); - - for (list = parser->stack->head; list; list = list->next) - g_free (list->data); - g_queue_free (parser->stack); - - g_free (parser); -} - -static void -check_auto_scaling (GnomeRRConfig **configs) -{ - GnomeRRConfig *config; - gint i; - - if (configs == NULL) - { - return; - } - - i = 0; - config = configs[i]; - - while (config != NULL) - { - if (config->priv->base_scale == BASE_SCALE_NOT_CONFIGURED) - { - config->priv->auto_scale = TRUE; - config->priv->base_scale = gnome_rr_screen_get_global_scale (NULL); - } - - config = configs[++i]; - } -} - -static GnomeRRConfig ** -configurations_read_from_file (const gchar *filename, GError **error) -{ - Parser *parser = g_new0 (Parser, 1); - GnomeRRConfig **result; - GMarkupParser callbacks = { - handle_start_element, - handle_end_element, - handle_text, - NULL, /* passthrough */ - NULL, /* error */ - }; - - parser->config_file_version = 0; - parser->configurations = g_ptr_array_new (); - parser->outputs = g_ptr_array_new (); - parser->stack = g_queue_new (); - - if (!parse_file_gmarkup (filename, &callbacks, parser, error)) - { - result = NULL; - - g_assert (parser->outputs); - goto out; - } - - g_assert (parser->outputs); - - g_ptr_array_add (parser->configurations, NULL); - result = (GnomeRRConfig **)g_ptr_array_free (parser->configurations, FALSE); - parser->configurations = g_ptr_array_new (); - - g_assert (parser->outputs); -out: - parser_free (parser); - - check_auto_scaling (result); - - return result; -} - static void gnome_rr_config_init (GnomeRRConfig *self) { self->priv = gnome_rr_config_get_instance_private (self); self->priv->clone = FALSE; - self->priv->base_scale = BASE_SCALE_NOT_CONFIGURED; - self->priv->auto_scale = FALSE; self->priv->screen = NULL; self->priv->outputs = NULL; } @@ -563,22 +149,27 @@ gnome_rr_config_load_current (GnomeRRConfig *config, GError **error) rr_outputs = gnome_rr_screen_list_outputs (config->priv->screen); config->priv->clone = FALSE; - config->priv->base_scale = gnome_rr_screen_get_global_scale (config->priv->screen); - if (gnome_rr_screen_get_global_scale_setting (config->priv->screen) == 0) - { - config->priv->auto_scale = TRUE; - } for (i = 0; rr_outputs[i] != NULL; ++i) { GnomeRROutput *rr_output = rr_outputs[i]; GnomeRROutputInfo *output = g_object_new (GNOME_TYPE_RR_OUTPUT_INFO, NULL); GnomeRRMode *mode = NULL; - const guint8 *edid_data = gnome_rr_output_get_edid_data (rr_output, NULL); GnomeRRCrtc *crtc; output->priv->name = g_strdup (gnome_rr_output_get_name (rr_output)); - output->priv->connected = gnome_rr_output_is_connected (rr_output); + output->priv->connected = TRUE; + output->priv->display_name = g_strdup (gnome_rr_output_get_display_name (rr_output)); + output->priv->connector_type = g_strdup (_gnome_rr_output_get_connector_type (rr_output)); + output->priv->config = config; + output->priv->is_tiled = _gnome_rr_output_get_tile_info (rr_output, + &output->priv->tile); + if (output->priv->is_tiled) + { + _gnome_rr_output_get_tiled_display_size (rr_output, NULL, NULL, + &output->priv->total_tiled_width, + &output->priv->total_tiled_height); + } if (!output->priv->connected) { @@ -586,71 +177,37 @@ gnome_rr_config_load_current (GnomeRRConfig *config, GError **error) output->priv->y = -1; output->priv->width = -1; output->priv->height = -1; - output->priv->rate = -1.0f; - output->priv->scale = MINIMUM_LOGICAL_SCALE_FACTOR; - output->priv->rotation = GNOME_RR_ROTATION_0; - output->priv->doublescan = FALSE; - output->priv->interlaced = FALSE; - output->priv->interlaced = FALSE; + output->priv->rate = -1; } else { - MonitorInfo *info = NULL; - - if (edid_data) - info = decode_edid (edid_data); - - if (info) - { - memcpy (output->priv->vendor, info->manufacturer_code, - sizeof (output->priv->vendor)); - - output->priv->product = info->product_code; - output->priv->serial = info->serial_number; - output->priv->aspect = info->aspect_ratio; - } - else - { - strcpy (output->priv->vendor, "???"); - output->priv->product = 0; - output->priv->serial = 0; - } - - if (gnome_rr_output_is_laptop (rr_output)) - output->priv->display_name = g_strdup (_("Laptop")); - else - output->priv->display_name = make_display_name (info); - - g_free (info); + gnome_rr_output_get_ids_from_edid (rr_output, + &output->priv->vendor, + &output->priv->product, + &output->priv->serial); crtc = gnome_rr_output_get_crtc (rr_output); - mode = crtc? gnome_rr_crtc_get_current_mode (crtc) : NULL; - + mode = crtc ? gnome_rr_crtc_get_current_mode (crtc) : NULL; + if (crtc && mode) { output->priv->on = TRUE; - + gnome_rr_crtc_get_position (crtc, &output->priv->x, &output->priv->y); output->priv->width = gnome_rr_mode_get_width (mode); output->priv->height = gnome_rr_mode_get_height (mode); - output->priv->rate = gnome_rr_mode_get_freq_f (mode); + output->priv->rate = gnome_rr_mode_get_freq (mode); output->priv->rotation = gnome_rr_crtc_get_current_rotation (crtc); - - output->priv->scale = gnome_rr_crtc_get_scale (crtc); - - gnome_rr_mode_get_flags (mode, - &output->priv->doublescan, - &output->priv->interlaced, - &output->priv->vsync); + output->priv->available_rotations = gnome_rr_crtc_get_rotations (crtc); if (output->priv->x == 0 && output->priv->y == 0) { - if (clone_width == -1) { - clone_width = output->priv->width; - clone_height = output->priv->height; - } else if (clone_width == output->priv->width && - clone_height == output->priv->height) { - config->priv->clone = TRUE; - } + if (clone_width == -1) { + clone_width = output->priv->width; + clone_height = output->priv->height; + } else if (clone_width == output->priv->width && + clone_height == output->priv->height) { + config->priv->clone = TRUE; + } } } else @@ -661,40 +218,13 @@ gnome_rr_config_load_current (GnomeRRConfig *config, GError **error) /* Get preferred size for the monitor */ mode = gnome_rr_output_get_preferred_mode (rr_output); - - if (!mode) - { - GnomeRRMode **modes = gnome_rr_output_list_modes (rr_output); - - /* FIXME: we should pick the "best" mode here, where best is - * sorted wrt - * - * - closest aspect ratio - * - mode area - * - refresh rate - * - We may want to extend randrwrap so that get_preferred - * returns that - although that could also depend on - * the crtc. - */ - if (modes[0]) - mode = modes[0]; - } - - if (mode) - { - output->priv->pref_width = gnome_rr_mode_get_width (mode); - output->priv->pref_height = gnome_rr_mode_get_height (mode); - } - else - { - /* Pick some random numbers. This should basically never happen */ - output->priv->pref_width = 1024; - output->priv->pref_height = 768; - } + output->priv->pref_width = gnome_rr_mode_get_width (mode); + output->priv->pref_height = gnome_rr_mode_get_height (mode); } output->priv->primary = gnome_rr_output_get_is_primary (rr_output); - + output->priv->underscanning = gnome_rr_output_get_is_underscanning (rr_output); + g_ptr_array_add (a, output); } @@ -726,7 +256,7 @@ gnome_rr_config_load_current (GnomeRRConfig *config, GError **error) if (output->priv->connected && !output->priv->on) { output->priv->x = last_x; - last_x = output->priv->x + (output->priv->width * config->priv->base_scale); + last_x = output->priv->x + output->priv->width; } } @@ -735,98 +265,6 @@ gnome_rr_config_load_current (GnomeRRConfig *config, GError **error) return TRUE; } -static void -ensure_scale_factor (GnomeRRConfig *config_from_file, - GnomeRROutputInfo *info_from_file) -{ - /* Loading an old config for the first time, there probably won't be any - * scale factor. If this happens, give it the matching current output's - * scale factor (what actual 'is' right now) - to maintain their existing - * configuration. */ - int k; - - for (k = 0; config_from_file->priv->outputs[k] != NULL; ++k) - { - if (config_from_file->priv->auto_scale) - { - info_from_file->priv->scale = (float) config_from_file->priv->base_scale; - continue; - } - - gchar *current_output_name, *new_output_name; - - current_output_name = config_from_file->priv->outputs[k]->priv->name; - new_output_name = info_from_file->priv->name; - - if (g_strcmp0 (current_output_name, new_output_name) == 0) - { - info_from_file->priv->scale = config_from_file->priv->outputs[k]->priv->scale; - } - } - - if (info_from_file->priv->scale == 0) - { - info_from_file->priv->scale = MINIMUM_LOGICAL_SCALE_FACTOR; - } -} - -gboolean -gnome_rr_config_load_filename (GnomeRRConfig *result, const char *filename, GError **error) -{ - GnomeRRConfig *current; - GnomeRRConfig **configs; - gboolean found = FALSE; - - g_return_val_if_fail (GNOME_IS_RR_CONFIG (result), FALSE); - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - current = gnome_rr_config_new_current (result->priv->screen, error); - - configs = configurations_read_from_file (filename, error); - - if (configs) - { - int i; - - for (i = 0; configs[i] != NULL; ++i) - { - if (gnome_rr_config_match (configs[i], current)) - { - int j; - GPtrArray *array; - result->priv->clone = configs[i]->priv->clone; - result->priv->auto_scale = configs[i]->priv->auto_scale; - result->priv->base_scale = configs[i]->priv->base_scale; - - array = g_ptr_array_new (); - for (j = 0; configs[i]->priv->outputs[j] != NULL; j++) { - g_object_ref (configs[i]->priv->outputs[j]); - g_ptr_array_add (array, configs[i]->priv->outputs[j]); - - ensure_scale_factor (configs[i], configs[i]->priv->outputs[j]); - } - g_ptr_array_add (array, NULL); - result->priv->outputs = (GnomeRROutputInfo **) g_ptr_array_free (array, FALSE); - - found = TRUE; - break; - } - - g_object_unref (configs[i]); - } - - g_free (configs); - - if (!found) - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_MATCHING_CONFIG, - _("none of the saved display configurations matched the active configuration")); - } - - g_object_unref (current); - return found; -} - static void gnome_rr_config_class_init (GnomeRRConfigClass *klass) { @@ -854,98 +292,22 @@ gnome_rr_config_new_current (GnomeRRScreen *screen, GError **error) } } -GnomeRRConfig * -gnome_rr_config_new_stored (GnomeRRScreen *screen, GError **error) -{ - GnomeRRConfig *self = g_object_new (GNOME_TYPE_RR_CONFIG, "screen", screen, NULL); - char *filename; - gboolean success; - - filename = gnome_rr_config_get_intended_filename (); - - success = gnome_rr_config_load_filename (self, filename, error); - - if (!success) - { - g_clear_error (error); - g_debug ("existing monitor config (%s) not found. Looking for legacy configuration (monitors.xml)", filename); - g_free (filename); - filename = gnome_rr_config_get_legacy_filename (); - - success = gnome_rr_config_load_filename (self, filename, error); - } - - g_free (filename); - - if (success) - return self; - else - { - g_object_unref (self); - return NULL; - } -} - -static gboolean -parse_file_gmarkup (const gchar *filename, - const GMarkupParser *parser, - gpointer data, - GError **err) -{ - GMarkupParseContext *context = NULL; - gchar *contents = NULL; - gboolean result = TRUE; - gsize len; - - if (!g_file_get_contents (filename, &contents, &len, err)) - { - result = FALSE; - goto out; - } - - context = g_markup_parse_context_new (parser, 0, data, NULL); - - if (!g_markup_parse_context_parse (context, contents, len, err)) - { - result = FALSE; - goto out; - } - - if (!g_markup_parse_context_end_parse (context, err)) - { - result = FALSE; - goto out; - } - -out: - if (contents) - g_free (contents); - - if (context) - g_markup_parse_context_free (context); - - return result; -} - static gboolean output_match (GnomeRROutputInfo *output1, GnomeRROutputInfo *output2) { g_assert (GNOME_IS_RR_OUTPUT_INFO (output1)); g_assert (GNOME_IS_RR_OUTPUT_INFO (output2)); - if (strcmp (output1->priv->name, output2->priv->name) != 0) - return FALSE; - - if (strcmp (output1->priv->vendor, output2->priv->vendor) != 0) + if (g_strcmp0 (output1->priv->name, output2->priv->name) != 0) return FALSE; - if (output1->priv->product != output2->priv->product) + if (g_strcmp0 (output1->priv->vendor, output2->priv->vendor) != 0) return FALSE; - if (output1->priv->serial != output2->priv->serial) + if (g_strcmp0 (output1->priv->product, output2->priv->product) != 0) return FALSE; - if (output1->priv->connected != output2->priv->connected) + if (g_strcmp0 (output1->priv->serial, output2->priv->serial) != 0) return FALSE; return TRUE; @@ -983,8 +345,8 @@ output_equal (GnomeRROutputInfo *output1, GnomeRROutputInfo *output2) if (output1->priv->rotation != output2->priv->rotation) return FALSE; - if (output1->priv->scale != output2->priv->scale) - return FALSE; + if (output1->priv->underscanning != output2->priv->underscanning) + return FALSE; } return TRUE; @@ -1040,16 +402,6 @@ gnome_rr_config_equal (GnomeRRConfig *c1, g_return_val_if_fail (GNOME_IS_RR_CONFIG (c1), FALSE); g_return_val_if_fail (GNOME_IS_RR_CONFIG (c2), FALSE); - if (c1->priv->auto_scale != c2->priv->auto_scale) - { - return FALSE; - } - - if (c1->priv->base_scale != c2->priv->base_scale) - { - return FALSE; - } - for (i = 0; c1->priv->outputs[i] != NULL; ++i) { GnomeRROutputInfo *output1 = c1->priv->outputs[i]; @@ -1079,10 +431,13 @@ make_outputs (GnomeRRConfig *config) GnomeRROutputInfo *old = config->priv->outputs[i]; GnomeRROutputInfo *new = g_object_new (GNOME_TYPE_RR_OUTPUT_INFO, NULL); *(new->priv) = *(old->priv); - if (old->priv->name) - new->priv->name = g_strdup (old->priv->name); - if (old->priv->display_name) - new->priv->display_name = g_strdup (old->priv->display_name); + + new->priv->name = g_strdup (old->priv->name); + new->priv->display_name = g_strdup (old->priv->display_name); + new->priv->connector_type = g_strdup (old->priv->connector_type); + new->priv->vendor = g_strdup (old->priv->vendor); + new->priv->product = g_strdup (old->priv->product); + new->priv->serial = g_strdup (old->priv->serial); if (old->priv->on && !first_on) first_on = old; @@ -1096,8 +451,6 @@ make_outputs (GnomeRRConfig *config) new->priv->rotation = first_on->priv->rotation; new->priv->x = 0; new->priv->y = 0; - new->priv->scale = first_on->priv->scale; - new->priv->rate = 60.0f; } g_ptr_array_add (outputs, new); @@ -1123,155 +476,29 @@ gnome_rr_config_applicable (GnomeRRConfig *configuration, g_return_val_if_fail (error == NULL || *error == NULL, FALSE); outputs = make_outputs (configuration); - assign = crtc_assignment_new (configuration, screen, outputs, error); + assign = crtc_assignment_new (screen, outputs, error); if (assign) { result = TRUE; crtc_assignment_free (assign); - } - else - { - result = FALSE; - } - - for (i = 0; outputs[i] != NULL; i++) { - g_object_unref (outputs[i]); - } - - return result; -} - -/* Database management */ - -static void -ensure_config_directory (void) -{ - g_mkdir_with_parents (g_get_user_config_dir (), 0700); -} - -char * -gnome_rr_config_get_backup_filename (void) -{ - ensure_config_directory (); - return g_build_filename (g_get_user_config_dir (), CONFIG_BACKUP_BASENAME, NULL); -} - -char * -gnome_rr_config_get_intended_filename (void) -{ - ensure_config_directory (); - return g_build_filename (g_get_user_config_dir (), CONFIG_INTENDED_BASENAME, NULL); -} - -char * -gnome_rr_config_get_legacy_filename (void) -{ - ensure_config_directory (); - return g_build_filename (g_get_user_config_dir (), CONFIG_LEGACY_BASENAME, NULL); -} - -static const char * -get_rotation_name (GnomeRRRotation r) -{ - if (r & GNOME_RR_ROTATION_0) - return "normal"; - if (r & GNOME_RR_ROTATION_90) - return "left"; - if (r & GNOME_RR_ROTATION_180) - return "upside_down"; - if (r & GNOME_RR_ROTATION_270) - return "right"; - - return "normal"; -} - -static const char * -yes_no (int x) -{ - return x? "yes" : "no"; -} - -static const char * -get_reflect_x (GnomeRRRotation r) -{ - return yes_no (r & GNOME_RR_REFLECT_X); -} - -static const char * -get_reflect_y (GnomeRRRotation r) -{ - return yes_no (r & GNOME_RR_REFLECT_Y); -} - -static void -emit_configuration (GnomeRRConfig *config, - GString *string) -{ - int j; - - g_string_append_printf (string, " \n"); - - g_string_append_printf (string, " %s\n", yes_no (config->priv->clone)); - - if (!config->priv->auto_scale) - { - g_string_append_printf (string, " %d\n", config->priv->base_scale); - } - - for (j = 0; config->priv->outputs[j] != NULL; ++j) - { - GnomeRROutputInfo *output = config->priv->outputs[j]; - - g_string_append_printf ( - string, " \n", output->priv->name); - - if (output->priv->connected && *output->priv->vendor != '\0') - { - g_string_append_printf ( - string, " %s\n", output->priv->vendor); - g_string_append_printf ( - string, " 0x%04x\n", output->priv->product); - g_string_append_printf ( - string, " 0x%08x\n", output->priv->serial); - } - - /* An unconnected output which is on does not make sense */ - if (output->priv->connected && output->priv->on) - { - g_string_append_printf ( - string, " %d\n", output->priv->width); - g_string_append_printf ( - string, " %d\n", output->priv->height); - g_string_append_printf ( - string, " %f\n", output->priv->rate); - g_string_append_printf ( - string, " %d\n", output->priv->x); - g_string_append_printf ( - string, " %d\n", output->priv->y); - - if (!config->priv->auto_scale) - { - g_string_append_printf ( - string, " %f\n", output->priv->scale); - } + } + else + { + result = FALSE; + } - g_string_append_printf ( - string, " %s\n", get_rotation_name (output->priv->rotation)); - g_string_append_printf ( - string, " %s\n", get_reflect_x (output->priv->rotation)); - g_string_append_printf ( - string, " %s\n", get_reflect_y (output->priv->rotation)); - g_string_append_printf ( - string, " %s\n", yes_no (output->priv->primary)); - } - - g_string_append_printf (string, " \n"); + for (i = 0; outputs[i] != NULL; i++) { + g_object_unref (outputs[i]); } - - g_string_append_printf (string, " \n"); + + g_free (outputs); + + return result; } +/* Database management */ + void gnome_rr_config_sanitize (GnomeRRConfig *config) { @@ -1284,23 +511,26 @@ gnome_rr_config_sanitize (GnomeRRConfig *config) */ x_offset = y_offset = G_MAXINT; for (i = 0; config->priv->outputs[i]; ++i) - { - GnomeRROutputInfo *output = config->priv->outputs[i]; + { + GnomeRROutputInfo *output = config->priv->outputs[i]; - if (output->priv->on) - { - x_offset = MIN (x_offset, output->priv->x); - y_offset = MIN (y_offset, output->priv->y); - } - } + if (output->priv->on) + { + x_offset = MIN (x_offset, output->priv->x); + y_offset = MIN (y_offset, output->priv->y); + } + } for (i = 0; config->priv->outputs[i]; ++i) - { - GnomeRROutputInfo *output = config->priv->outputs[i]; - - output->priv->x -= x_offset; - output->priv->y -= y_offset; - } + { + GnomeRROutputInfo *output = config->priv->outputs[i]; + + if (output->priv->on) + { + output->priv->x -= x_offset; + output->priv->y -= y_offset; + } + } /* Only one primary, please */ found = FALSE; @@ -1324,14 +554,14 @@ gboolean gnome_rr_config_ensure_primary (GnomeRRConfig *configuration) { int i; - GnomeRROutputInfo *laptop; + GnomeRROutputInfo *builtin_display; GnomeRROutputInfo *top_left; gboolean found; GnomeRRConfigPrivate *priv; g_return_val_if_fail (GNOME_IS_RR_CONFIG (configuration), FALSE); - laptop = NULL; + builtin_display = NULL; top_left = NULL; found = FALSE; priv = configuration->priv; @@ -1358,17 +588,15 @@ gnome_rr_config_ensure_primary (GnomeRRConfig *configuration) && info->priv->y < top_left->priv->y)) { top_left = info; } - if (laptop == NULL - && _gnome_rr_output_name_is_laptop (info->priv->name)) { - /* shame we can't find the connector type - as with gnome_rr_output_is_laptop */ - laptop = info; + if (builtin_display == NULL + && _gnome_rr_output_connector_type_is_builtin_display (info->priv->connector_type)) { + builtin_display = info; } } if (!found) { - if (laptop != NULL) { - laptop->priv->primary = TRUE; + if (builtin_display != NULL) { + builtin_display->priv->primary = TRUE; } else if (top_left != NULL) { /* Note: top_left can be NULL if all outputs are off */ top_left->priv->primary = TRUE; @@ -1378,160 +606,53 @@ gnome_rr_config_ensure_primary (GnomeRRConfig *configuration) return !found; } -gboolean -gnome_rr_config_save (GnomeRRConfig *configuration, GError **error) -{ - GnomeRRConfig **configurations; - GString *output; - int i; - gchar *intended_filename; - gchar *backup_filename; - gboolean result; - - g_return_val_if_fail (GNOME_IS_RR_CONFIG (configuration), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - output = g_string_new (""); - - backup_filename = gnome_rr_config_get_backup_filename (); - intended_filename = gnome_rr_config_get_intended_filename (); - - configurations = configurations_read_from_file (intended_filename, NULL); /* NULL-GError */ - - g_string_append_printf (output, "\n"); - - if (configurations) - { - for (i = 0; configurations[i] != NULL; ++i) - { - if (!gnome_rr_config_match (configurations[i], configuration)) - emit_configuration (configurations[i], output); - g_object_unref (configurations[i]); - } - - g_free (configurations); - } - - emit_configuration (configuration, output); - - g_string_append_printf (output, "\n"); - - /* backup the file first */ - rename (intended_filename, backup_filename); /* no error checking because the intended file may not even exist */ - - result = g_file_set_contents (intended_filename, output->str, -1, error); - - if (!result) - rename (backup_filename, intended_filename); /* no error checking because the backup may not even exist */ - - g_free (backup_filename); - g_free (intended_filename); - g_string_free (output, TRUE); - - return result; -} - -gboolean -gnome_rr_config_apply_with_time (GnomeRRConfig *config, - GnomeRRScreen *screen, - guint32 timestamp, - GError **error) +static gboolean +gnome_rr_config_apply_helper (GnomeRRConfig *config, + GnomeRRScreen *screen, + gboolean persistent, + GError **error) { CrtcAssignment *assignment; GnomeRROutputInfo **outputs; gboolean result = FALSE; int i; - guint global_scale; g_return_val_if_fail (GNOME_IS_RR_CONFIG (config), FALSE); g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), FALSE); - gdk_error_trap_push (); - outputs = make_outputs (config); - assignment = crtc_assignment_new (config, screen, outputs, error); - - for (i = 0; outputs[i] != NULL; i++) - g_object_unref (outputs[i]); - g_free (outputs); - - global_scale = config->priv->base_scale; + assignment = crtc_assignment_new (screen, outputs, error); if (assignment) { - if (crtc_assignment_apply (assignment, timestamp, error, &global_scale)) + if (crtc_assignment_apply (assignment, persistent, error)) result = TRUE; crtc_assignment_free (assignment); - - - gdk_flush (); - gdk_error_trap_pop_ignored (); // ignore errors } - if (result == TRUE) - { - gnome_rr_screen_set_global_scale_setting (screen, - config->priv->auto_scale ? 0 : global_scale); - } + for (i = 0; outputs[i] != NULL; i++) + g_object_unref (outputs[i]); + g_free (outputs); return result; } -/* gnome_rr_config_apply_from_filename_with_time: - * @screen: A #GnomeRRScreen - * @filename: Path of the file to look in for stored RANDR configurations. - * @timestamp: X server timestamp from the event that causes the screen configuration to change (a user's button press, for example) - * @error: Location to store error, or %NULL - * - * Loads the file in @filename and looks for suitable matching RANDR - * configurations in the file; if one is found, that configuration will be - * applied to the current set of RANDR outputs. - * - * Typically, @filename is the result of gnome_rr_config_get_intended_filename() or - * gnome_rr_config_get_backup_filename(). - * - * Returns: TRUE if the RANDR configuration was loaded and applied from - * the specified file, or FALSE otherwise: - * - * If the file in question is loaded successfully but the configuration cannot - * be applied, the @error will have a domain of #GNOME_RR_ERROR. Note that an - * error code of #GNOME_RR_ERROR_NO_MATCHING_CONFIG is not a real error; it - * simply means that there were no stored configurations that match the current - * set of RANDR outputs. - * - * If the file in question cannot be loaded, the @error will have a domain of - * #G_FILE_ERROR. Note that an error code of G_FILE_ERROR_NOENT is not really - * an error, either; it means that there was no stored configuration file and so - * nothing is changed. - */ gboolean -gnome_rr_config_apply_from_filename_with_time (GnomeRRScreen *screen, const char *filename, guint32 timestamp, GError **error) +gnome_rr_config_apply (GnomeRRConfig *config, + GnomeRRScreen *screen, + GError **error) { - GnomeRRConfig *stored; - - g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), FALSE); - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - stored = g_object_new (GNOME_TYPE_RR_CONFIG, "screen", screen, NULL); - - if (gnome_rr_config_load_filename (stored, filename, error)) - { - gboolean result; - - gnome_rr_config_ensure_primary (stored); - result = gnome_rr_config_apply_with_time (stored, screen, timestamp, error); + return gnome_rr_config_apply_helper (config, screen, FALSE, error); +} - g_object_unref (stored); - return result; - } - else - { - g_object_unref (stored); - return FALSE; - } +gboolean +gnome_rr_config_apply_persistent (GnomeRRConfig *config, + GnomeRRScreen *screen, + GError **error) +{ + return gnome_rr_config_apply_helper (config, screen, TRUE, error); } /** @@ -1570,45 +691,6 @@ gnome_rr_config_set_clone (GnomeRRConfig *self, gboolean clone) self->priv->clone = clone; } -guint -gnome_rr_config_get_base_scale (GnomeRRConfig *self) -{ - g_return_val_if_fail (GNOME_IS_RR_CONFIG (self), MINIMUM_GLOBAL_SCALE_FACTOR); - - if (self->priv->auto_scale) - { - return gnome_rr_screen_get_global_scale (self->priv->screen); - } - - return self->priv->base_scale; -} - -void -gnome_rr_config_set_base_scale (GnomeRRConfig *self, - guint base_scale) -{ - g_return_if_fail (GNOME_IS_RR_CONFIG (self) || base_scale < MINIMUM_GLOBAL_SCALE_FACTOR); - - self->priv->base_scale = base_scale; -} - -gboolean -gnome_rr_config_get_auto_scale (GnomeRRConfig *self) -{ - g_return_val_if_fail (GNOME_IS_RR_CONFIG (self), TRUE); - - return self->priv->auto_scale; -} - -void -gnome_rr_config_set_auto_scale (GnomeRRConfig *self, - gboolean auto_scale) -{ - g_return_if_fail (GNOME_IS_RR_CONFIG (self)); - - self->priv->auto_scale = auto_scale; -} - /* * CRTC assignment */ @@ -1619,13 +701,13 @@ struct CrtcInfo GnomeRRMode *mode; int x; int y; - float scale; GnomeRRRotation rotation; GPtrArray *outputs; }; struct CrtcAssignment { + GnomeRROutputInfo **outputs; GnomeRRScreen *screen; GHashTable *info; GnomeRROutput *primary; @@ -1635,7 +717,7 @@ static gboolean can_clone (CrtcInfo *info, GnomeRROutput *output) { - int i; + guint i; for (i = 0; i < info->outputs->len; ++i) { @@ -1655,108 +737,101 @@ crtc_assignment_assign (CrtcAssignment *assign, int x, int y, GnomeRRRotation rotation, - gboolean primary, - float scale, + gboolean primary, GnomeRROutput *output, GError **error) { CrtcInfo *info = g_hash_table_lookup (assign->info, crtc); guint32 crtc_id; const char *output_name; + crtc_id = gnome_rr_crtc_get_id (crtc); output_name = gnome_rr_output_get_name (output); if (!gnome_rr_crtc_can_drive_output (crtc, output)) { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, - _("CRTC %d cannot drive output %s"), crtc_id, output_name); - return FALSE; + g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, + _("CRTC %d cannot drive output %s"), crtc_id, output_name); + return FALSE; } if (!gnome_rr_output_supports_mode (output, mode)) { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, - _("output %s does not support mode %dx%d@%dHz"), - output_name, - gnome_rr_mode_get_width (mode), - gnome_rr_mode_get_height (mode), - gnome_rr_mode_get_freq (mode)); - return FALSE; + g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, + _("output %s does not support mode %dx%d@%dHz"), + output_name, + gnome_rr_mode_get_width (mode), + gnome_rr_mode_get_height (mode), + gnome_rr_mode_get_freq (mode)); + return FALSE; } if (!gnome_rr_crtc_supports_rotation (crtc, rotation)) { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, - _("CRTC %d does not support rotation=%s"), - crtc_id, - get_rotation_name (rotation)); - return FALSE; + g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, + _("CRTC %d does not support rotation=%d"), + crtc_id, rotation); + return FALSE; } if (info) { - if (!(info->mode == mode && - info->x == x && - info->y == y && - info->scale == scale && - info->rotation == rotation)) - { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, - _("output %s does not have the same parameters as another cloned output:\n" - "existing mode = %d, new mode = %d\n" - "existing coordinates = (%d, %d), new coordinates = (%d, %d)\n" - "existing rotation = %s, new rotation = %s" - "existing scale = %.2f, new scale = %.2f"), - output_name, - gnome_rr_mode_get_id (info->mode), gnome_rr_mode_get_id (mode), - info->x, info->y, - x, y, - get_rotation_name (info->rotation), get_rotation_name (rotation), - info->scale, scale); - return FALSE; - } + if (!(info->mode == mode && + info->x == x && + info->y == y && + info->rotation == rotation)) + { + g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, + _("output %s does not have the same parameters as another cloned output:\n" + "existing mode = %d, new mode = %d\n" + "existing coordinates = (%d, %d), new coordinates = (%d, %d)\n" + "existing rotation = %d, new rotation = %d"), + output_name, + gnome_rr_mode_get_id (info->mode), gnome_rr_mode_get_id (mode), + info->x, info->y, + x, y, + info->rotation, rotation); + return FALSE; + } - if (!can_clone (info, output)) - { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, - _("cannot clone to output %s"), - output_name); - return FALSE; - } + if (!can_clone (info, output)) + { + g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT, + _("cannot clone to output %s"), + output_name); + return FALSE; + } - g_ptr_array_add (info->outputs, output); + g_ptr_array_add (info->outputs, output); - if (primary && !assign->primary) - { - assign->primary = output; - } + if (primary && !assign->primary) + { + assign->primary = output; + } - return TRUE; + return TRUE; } else - { - CrtcInfo *info = g_new0 (CrtcInfo, 1); - - info->mode = mode; - info->x = x; - info->y = y; - info->rotation = rotation; - info->scale = scale; - info->outputs = g_ptr_array_new (); - - g_ptr_array_add (info->outputs, output); - - g_hash_table_insert (assign->info, crtc, info); - + { + info = g_new0 (CrtcInfo, 1); + + info->mode = mode; + info->x = x; + info->y = y; + info->rotation = rotation; + info->outputs = g_ptr_array_new (); + + g_ptr_array_add (info->outputs, output); + + g_hash_table_insert (assign->info, crtc, info); + if (primary && !assign->primary) { assign->primary = output; } - return TRUE; + return TRUE; } - - return FALSE; } static void @@ -1788,89 +863,6 @@ crtc_assignment_free (CrtcAssignment *assign) g_free (assign); } -typedef struct { - guint32 timestamp; - gboolean has_error; - GError **error; - guint global_scale; -} ConfigureCrtcState; - -// static guint -// get_max_info_scale (CrtcAssignment *assignment) -// { -// GList *infos, *iter; -// float max_scale = 0; - -// infos = g_hash_table_get_values (assignment->info); - -// for (iter = infos; iter != NULL; iter = iter->next) -// { -// CrtcInfo *info = iter->data; - -// if (info->scale > max_scale) -// { -// max_scale = info->scale; -// } -// } - -// g_list_free (infos); - - -// return CLAMP ((guint) ceilf (max_scale), -// MINIMUM_GLOBAL_SCALE_FACTOR, -// MAXIMUM_GLOBAL_SCALE_FACTOR); -// } - -// static guint -// get_min_info_scale (CrtcAssignment *assignment) -// { -// GList *infos, *iter; -// float min_scale = 4.0f; - -// infos = g_hash_table_get_values (assignment->info); - -// for (iter = infos; iter != NULL; iter = iter->next) -// { -// CrtcInfo *info = iter->data; - -// if (info->scale < min_scale) -// { -// min_scale = info->scale; -// } -// } - -// g_list_free (infos); - -// return CLAMP ((guint) floorf (min_scale), -// MINIMUM_GLOBAL_SCALE_FACTOR, -// MAXIMUM_GLOBAL_SCALE_FACTOR); -// } - -static void -configure_crtc (gpointer key, - gpointer value, - gpointer data) -{ - GnomeRRCrtc *crtc = key; - CrtcInfo *info = value; - ConfigureCrtcState *state = data; - - if (state->has_error) - return; - - if (!gnome_rr_crtc_set_config_with_time (crtc, - state->timestamp, - info->x, info->y, - info->mode, - info->rotation, - (GnomeRROutput **)info->outputs->pdata, - info->outputs->len, - info->scale, - state->global_scale, - state->error)) - state->has_error = TRUE; -} - static gboolean mode_is_rotated (CrtcInfo *info) { @@ -1882,20 +874,6 @@ mode_is_rotated (CrtcInfo *info) return FALSE; } -static gboolean -crtc_is_rotated (GnomeRRCrtc *crtc) -{ - GnomeRRRotation r = gnome_rr_crtc_get_current_rotation (crtc); - - if ((r & GNOME_RR_ROTATION_270) || - (r & GNOME_RR_ROTATION_90)) - { - return TRUE; - } - - return FALSE; -} - static void accumulate_error (GString *accumulated_error, GError *error) { @@ -1925,14 +903,13 @@ real_assign_crtcs (GnomeRRScreen *screen, gboolean success; output = *outputs; - if (!output) - return TRUE; + return TRUE; /* It is always allowed for an output to be turned off */ if (!output->priv->on) { - return real_assign_crtcs (screen, outputs + 1, assignment, error); + return real_assign_crtcs (screen, outputs + 1, assignment, error); } success = FALSE; @@ -1941,73 +918,69 @@ real_assign_crtcs (GnomeRRScreen *screen, for (i = 0; crtcs[i] != NULL; ++i) { - GnomeRRCrtc *crtc = crtcs[i]; - int crtc_id = gnome_rr_crtc_get_id (crtc); - int pass; - g_debug (_("Trying modes for CRTC %d"), - crtc_id); - g_string_append_printf (accumulated_error, - _("Trying modes for CRTC %d\n"), - crtc_id); - - /* Make two passes, one where frequencies must match, then - * one where they don't have to - */ - for (pass = 0; pass < 2; ++pass) - { - GnomeRROutput *gnome_rr_output = gnome_rr_screen_get_output_by_name (screen, output->priv->name); - GnomeRRMode **modes = gnome_rr_output_list_modes (gnome_rr_output); - int j; + GnomeRRCrtc *crtc = crtcs[i]; + int crtc_id = gnome_rr_crtc_get_id (crtc); + int pass; - for (j = 0; modes[j] != NULL; ++j) - { - GnomeRRMode *mode = modes[j]; - int mode_width; - int mode_height; - double mode_freq; + g_string_append_printf (accumulated_error, + _("Trying modes for CRTC %d\n"), + crtc_id); + + /* Make two passes, one where frequencies must match, then + * one where they don't have to + */ + for (pass = 0; pass < 2; ++pass) + { + GnomeRROutput *gnome_rr_output = gnome_rr_screen_get_output_by_name (screen, output->priv->name); + GnomeRRMode **modes = gnome_rr_output_list_modes (gnome_rr_output); + int j; + + for (j = 0; modes[j] != NULL; ++j) + { + GnomeRRMode *mode = modes[j]; + int mode_width; + int mode_height; + int mode_freq; - mode_width = gnome_rr_mode_get_width (mode); - mode_height = gnome_rr_mode_get_height (mode); - mode_freq = gnome_rr_mode_get_freq_f (mode); + mode_width = gnome_rr_mode_get_width (mode); + mode_height = gnome_rr_mode_get_height (mode); + mode_freq = gnome_rr_mode_get_freq (mode); - g_string_append_printf (accumulated_error, - _("CRTC %d: trying mode %dx%d@%.2fHz with output at %dx%d@%.2fHz (pass %d)\n"), + g_string_append_printf (accumulated_error, + _("CRTC %d: trying mode %dx%d@%dHz with output at %dx%d@%dHz (pass %d)\n"), crtc_id, mode_width, mode_height, mode_freq, output->priv->width, output->priv->height, output->priv->rate, pass); - if (mode_width == output->priv->width && - mode_height == output->priv->height && - (pass == 1 || mode_freq == output->priv->rate)) - { - tried_mode = TRUE; - - my_error = NULL; - - if (crtc_assignment_assign ( - assignment, crtc, modes[j], - output->priv->x, output->priv->y, - output->priv->rotation, - output->priv->primary, - output->priv->scale, - gnome_rr_output, - &my_error)) - { - my_error = NULL; - - if (real_assign_crtcs (screen, outputs + 1, assignment, &my_error)) { - success = TRUE; - goto out; - } else - accumulate_error (accumulated_error, my_error); - - crtc_assignment_unassign (assignment, crtc, gnome_rr_output); - } else - accumulate_error (accumulated_error, my_error); - } - } - } + if (mode_width == output->priv->width && + mode_height == output->priv->height && + (pass == 1 || mode_freq == output->priv->rate)) + { + tried_mode = TRUE; + + my_error = NULL; + if (crtc_assignment_assign ( + assignment, crtc, modes[j], + output->priv->x, output->priv->y, + output->priv->rotation, + output->priv->primary, + gnome_rr_output, + &my_error)) + { + my_error = NULL; + if (real_assign_crtcs (screen, outputs + 1, assignment, &my_error)) { + success = TRUE; + goto out; + } else + accumulate_error (accumulated_error, my_error); + + crtc_assignment_unassign (assignment, crtc, gnome_rr_output); + } else + accumulate_error (accumulated_error, my_error); + } + } + } } out: @@ -2042,74 +1015,50 @@ crtc_info_free (CrtcInfo *info) } static void -get_required_virtual_size (CrtcAssignment *assign, - GnomeRRScreen *screen, - int *width, - int *height, - float *avg_scale, - guint *global_scale) +get_required_virtual_size (CrtcAssignment *assign, int *width, int *height) { GList *active_crtcs = g_hash_table_get_keys (assign->info); GList *list; - int crtc_count; - float avg_screen_scale; - -/* - if (gnome_rr_screen_get_use_upscaling (screen)) - { - *global_scale = get_min_info_scale (assign); - } - else - { - *global_scale = get_max_info_scale (assign); - } -*/ + int d; + if (!width) + width = &d; + if (!height) + height = &d; + /* Compute size of the screen */ *width = *height = 1; - avg_screen_scale = 0; - crtc_count = 0; for (list = active_crtcs; list != NULL; list = list->next) { - GnomeRRCrtc *crtc = list->data; - CrtcInfo *info = g_hash_table_lookup (assign->info, crtc); - int w, h; - float scale = 1.0f; + GnomeRRCrtc *crtc = list->data; + CrtcInfo *info = g_hash_table_lookup (assign->info, crtc); + int w, h; - scale = *global_scale / info->scale; - - w = gnome_rr_mode_get_width (info->mode); - h = gnome_rr_mode_get_height (info->mode); - - if (mode_is_rotated (info)) - { - int tmp = h; - h = w; - w = tmp; - } - - *width = MAX (*width, info->x + roundf (w * scale)); - *height = MAX (*height, info->y + roundf (h * scale)); - - avg_screen_scale += (info->scale - avg_screen_scale) / (float) (++crtc_count); + w = gnome_rr_mode_get_width (info->mode); + h = gnome_rr_mode_get_height (info->mode); + + if (mode_is_rotated (info)) + { + int tmp = h; + h = w; + w = tmp; + } + + *width = MAX (*width, info->x + w); + *height = MAX (*height, info->y + h); } - *avg_scale = avg_screen_scale; - - g_debug ("Proposed screen size: %dx%d average scale: %.2f, ui scale: %d", - *width, *height, *avg_scale, *global_scale); - g_list_free (active_crtcs); } static CrtcAssignment * -crtc_assignment_new (GnomeRRConfig *config, - GnomeRRScreen *screen, - GnomeRROutputInfo **outputs, - GError **error) +crtc_assignment_new (GnomeRRScreen *screen, + GnomeRROutputInfo **outputs, + GError **error) { CrtcAssignment *assignment = g_new0 (CrtcAssignment, 1); + assignment->outputs = outputs; assignment->info = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GFreeFunc)crtc_info_free); @@ -2117,13 +1066,8 @@ crtc_assignment_new (GnomeRRConfig *config, { int width, height; int min_width, max_width, min_height, max_height; - float scale; - guint global_scale = config->priv->base_scale; - get_required_virtual_size (assignment, - screen, - &width, &height, - &scale, &global_scale); + get_required_virtual_size (assignment, &width, &height); gnome_rr_screen_get_ranges ( screen, &min_width, &max_width, &min_height, &max_height); @@ -2154,117 +1098,98 @@ crtc_assignment_new (GnomeRRConfig *config, return NULL; } +#define ROTATION_MASK 0x7F + +static enum wl_output_transform +rotation_to_transform (GnomeRRRotation rotation) +{ + static const enum wl_output_transform y_reflected_map[] = { + WL_OUTPUT_TRANSFORM_FLIPPED_180, + WL_OUTPUT_TRANSFORM_FLIPPED_90, + WL_OUTPUT_TRANSFORM_FLIPPED, + WL_OUTPUT_TRANSFORM_FLIPPED_270 + }; + enum wl_output_transform ret; + + switch (rotation & ROTATION_MASK) + { + default: + case GNOME_RR_ROTATION_0: + ret = WL_OUTPUT_TRANSFORM_NORMAL; + break; + case GNOME_RR_ROTATION_90: + ret = WL_OUTPUT_TRANSFORM_90; + break; + case GNOME_RR_ROTATION_180: + ret = WL_OUTPUT_TRANSFORM_180; + break; + case GNOME_RR_ROTATION_270: + ret = WL_OUTPUT_TRANSFORM_270; + break; + } + + if (rotation & GNOME_RR_REFLECT_X) + return ret + 4; + else if (rotation & GNOME_RR_REFLECT_Y) + return y_reflected_map[ret]; + else + return ret; +} + static gboolean -crtc_assignment_apply (CrtcAssignment *assign, - guint32 timestamp, - GError **error, - guint *global_scale) +crtc_assignment_apply (CrtcAssignment *assign, gboolean persistent, GError **error) { - GnomeRRCrtc **all_crtcs = gnome_rr_screen_list_crtcs (assign->screen); - int width, height; - int i; - int min_width, max_width, min_height, max_height; - int width_mm, height_mm; - float average_scale; - gboolean success = TRUE; - - /* Compute size of the screen */ - get_required_virtual_size (assign, - assign->screen, - &width, &height, - &average_scale, global_scale); - - gnome_rr_screen_get_ranges ( - assign->screen, &min_width, &max_width, &min_height, &max_height); - - /* We should never get here if the dimensions don't fit in the virtual size, - * but just in case we do, fix it up. - */ - width = MAX (min_width, width); - width = MIN (max_width, width); - height = MAX (min_height, height); - height = MIN (max_height, height); - - /* FMQ: do we need to check the sizes instead of clamping them? */ - - /* Grab the server while we fiddle with the CRTCs and the screen, so that - * apps that listen for RANDR notifications will only receive the final - * status. - */ - - gdk_x11_display_grab (gdk_screen_get_display (assign->screen->priv->gdk_screen)); + GVariantBuilder crtc_builder, output_builder, nested_outputs; + GHashTableIter iter; + GnomeRRCrtc *crtc; + CrtcInfo *info; + unsigned i; - /* Turn off all crtcs that are currently displaying outside the new screen, - * or are not used in the new setup - */ - for (i = 0; all_crtcs[i] != NULL; ++i) - { - GnomeRRCrtc *crtc = all_crtcs[i]; - GnomeRRMode *mode = gnome_rr_crtc_get_current_mode (crtc); - int x, y; + g_variant_builder_init (&crtc_builder, G_VARIANT_TYPE ("a(uiiiuaua{sv})")); + g_variant_builder_init (&output_builder, G_VARIANT_TYPE ("a(ua{sv})")); - if (mode) + g_hash_table_iter_init (&iter, assign->info); + while (g_hash_table_iter_next (&iter, (void*) &crtc, (void*) &info)) { - int w, h; - gnome_rr_crtc_get_position (crtc, &x, &y); + g_variant_builder_init (&nested_outputs, G_VARIANT_TYPE ("au")); + for (i = 0; i < info->outputs->len; i++) + { + GnomeRROutput *output = g_ptr_array_index (info->outputs, i); - w = gnome_rr_mode_get_width (mode) * (*global_scale); - h = gnome_rr_mode_get_height (mode) * (*global_scale); - if (crtc_is_rotated (crtc)) - { - int tmp = h; - h = w; - w = tmp; - } - - if (x + w > width || y + h > height || !g_hash_table_lookup (assign->info, crtc)) - { - if (!gnome_rr_crtc_set_config_with_time (crtc, - timestamp, - 0, 0, - NULL, - GNOME_RR_ROTATION_0, - NULL, - 0, - 1.0f, - 1, - error)) - { - success = FALSE; - break; - } - - } + g_variant_builder_add (&nested_outputs, "u", + gnome_rr_output_get_id (output)); } - } - - /* The 'physical size' of an X screen is meaningless if that screen - * can consist of many monitors. So just pick a size that make the - * dpi 96. - * - * Firefox and Evince apparently believe what X tells them. - */ - width_mm = (width / (DPI_FALLBACK / average_scale)) * 25.4 + 0.5; - height_mm = (height / (DPI_FALLBACK / average_scale)) * 25.4 + 0.5; - - if (success) - { - ConfigureCrtcState state; - - state.timestamp = timestamp; - state.has_error = FALSE; - state.error = error; - state.global_scale = *global_scale; - gnome_rr_screen_set_size (assign->screen, width, height, width_mm, height_mm); - - g_hash_table_foreach (assign->info, configure_crtc, &state); - - success = !state.has_error; - } - - gnome_rr_screen_set_primary_output (assign->screen, assign->primary); - gdk_x11_display_ungrab (gdk_screen_get_display (assign->screen->priv->gdk_screen)); - - return success; + g_variant_builder_add (&crtc_builder, "(uiiiuaua{sv})", + gnome_rr_crtc_get_id (crtc), + info->mode ? + gnome_rr_mode_get_id (info->mode) : (guint32) -1, + info->x, + info->y, + rotation_to_transform (info->rotation), + &nested_outputs, + NULL); + } + + for (i = 0; assign->outputs[i]; i++) + { + GnomeRROutputInfo *output = assign->outputs[i]; + GnomeRROutput *gnome_rr_output = gnome_rr_screen_get_output_by_name (assign->screen, + output->priv->name); + + g_variant_builder_add (&output_builder, "(u@a{sv})", + gnome_rr_output_get_id (gnome_rr_output), + g_variant_new_parsed ("{ 'primary': <%b>," + " 'presentation': <%b>," + " 'underscanning': <%b> }", + output->priv->primary, + FALSE, + output->priv->underscanning)); + } + + return _gnome_rr_screen_apply_configuration (assign->screen, + persistent, + g_variant_builder_end (&crtc_builder), + g_variant_builder_end (&output_builder), + error); } diff --git a/libcinnamon-desktop/gnome-rr-config.h b/libcinnamon-desktop/gnome-rr-config.h index 0d59a95..a6a3411 100644 --- a/libcinnamon-desktop/gnome-rr-config.h +++ b/libcinnamon-desktop/gnome-rr-config.h @@ -34,21 +34,22 @@ #include #include -typedef struct GnomeRROutputInfoPrivate GnomeRROutputInfoPrivate; -typedef struct GnomeRRConfigPrivate GnomeRRConfigPrivate; +typedef struct _GnomeRROutputInfo GnomeRROutputInfo; +typedef struct _GnomeRROutputInfoClass GnomeRROutputInfoClass; +typedef struct _GnomeRROutputInfoPrivate GnomeRROutputInfoPrivate; -typedef struct +struct _GnomeRROutputInfo { GObject parent; /*< private >*/ GnomeRROutputInfoPrivate *priv; -} GnomeRROutputInfo; +}; -typedef struct +struct _GnomeRROutputInfoClass { GObjectClass parent_class; -} GnomeRROutputInfoClass; +}; #define GNOME_TYPE_RR_OUTPUT_INFO (gnome_rr_output_info_get_type()) #define GNOME_RR_OUTPUT_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_RR_OUTPUT_INFO, GnomeRROutputInfo)) @@ -57,9 +58,11 @@ typedef struct #define GNOME_IS_RR_OUTPUT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_RR_OUTPUT_INFO)) #define GNOME_RR_OUTPUT_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_RR_OUTPUT_INFO, GnomeRROutputInfoClass)) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GnomeRROutputInfo, g_object_unref) + GType gnome_rr_output_info_get_type (void); -const char *gnome_rr_output_info_get_name (GnomeRROutputInfo *self); +char *gnome_rr_output_info_get_name (GnomeRROutputInfo *self); gboolean gnome_rr_output_info_is_active (GnomeRROutputInfo *self); void gnome_rr_output_info_set_active (GnomeRROutputInfo *self, gboolean active); @@ -67,24 +70,19 @@ void gnome_rr_output_info_set_active (GnomeRROutputInfo *self, gboolean acti void gnome_rr_output_info_get_geometry (GnomeRROutputInfo *self, int *x, int *y, int *width, int *height); void gnome_rr_output_info_set_geometry (GnomeRROutputInfo *self, int x, int y, int width, int height); -float gnome_rr_output_info_get_scale (GnomeRROutputInfo *self); -void gnome_rr_output_info_set_scale (GnomeRROutputInfo *self, float scale); - -int gnome_rr_output_info_get_refresh_rate (GnomeRROutputInfo *self); +int gnome_rr_output_info_get_refresh_rate (GnomeRROutputInfo *self); void gnome_rr_output_info_set_refresh_rate (GnomeRROutputInfo *self, int rate); -double gnome_rr_output_info_get_refresh_rate_f (GnomeRROutputInfo *self); -void gnome_rr_output_info_set_refresh_rate_f (GnomeRROutputInfo *self, double rate); - GnomeRRRotation gnome_rr_output_info_get_rotation (GnomeRROutputInfo *self); void gnome_rr_output_info_set_rotation (GnomeRROutputInfo *self, GnomeRRRotation rotation); +gboolean gnome_rr_output_info_supports_rotation (GnomeRROutputInfo *self, GnomeRRRotation rotation); gboolean gnome_rr_output_info_is_connected (GnomeRROutputInfo *self); -void gnome_rr_output_info_get_vendor (GnomeRROutputInfo *self, gchar* vendor); -guint gnome_rr_output_info_get_product (GnomeRROutputInfo *self); -guint gnome_rr_output_info_get_serial (GnomeRROutputInfo *self); +const char *gnome_rr_output_info_get_vendor (GnomeRROutputInfo *self); +const char *gnome_rr_output_info_get_product (GnomeRROutputInfo *self); +const char *gnome_rr_output_info_get_serial (GnomeRROutputInfo *self); double gnome_rr_output_info_get_aspect_ratio (GnomeRROutputInfo *self); -const char *gnome_rr_output_info_get_display_name (GnomeRROutputInfo *self); +char *gnome_rr_output_info_get_display_name (GnomeRROutputInfo *self); gboolean gnome_rr_output_info_get_primary (GnomeRROutputInfo *self); void gnome_rr_output_info_set_primary (GnomeRROutputInfo *self, gboolean primary); @@ -92,26 +90,27 @@ void gnome_rr_output_info_set_primary (GnomeRROutputInfo *self, gboolean pri int gnome_rr_output_info_get_preferred_width (GnomeRROutputInfo *self); int gnome_rr_output_info_get_preferred_height (GnomeRROutputInfo *self); -void gnome_rr_output_info_get_flags (GnomeRROutputInfo *self, - gboolean *doublescan, - gboolean *interlaced, - gboolean *vsync); -void gnome_rr_output_info_set_flags (GnomeRROutputInfo *self, - gboolean doublescan, - gboolean interlaced, - gboolean vsync); -typedef struct +gboolean gnome_rr_output_info_get_underscanning (GnomeRROutputInfo *self); +void gnome_rr_output_info_set_underscanning (GnomeRROutputInfo *self, gboolean underscanning); + +gboolean gnome_rr_output_info_is_primary_tile (GnomeRROutputInfo *self); + +typedef struct _GnomeRRConfig GnomeRRConfig; +typedef struct _GnomeRRConfigClass GnomeRRConfigClass; +typedef struct _GnomeRRConfigPrivate GnomeRRConfigPrivate; + +struct _GnomeRRConfig { GObject parent; /*< private >*/ GnomeRRConfigPrivate *priv; -} GnomeRRConfig; +}; -typedef struct +struct _GnomeRRConfigClass { GObjectClass parent_class; -} GnomeRRConfigClass; +}; #define GNOME_TYPE_RR_CONFIG (gnome_rr_config_get_type()) #define GNOME_RR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_RR_CONFIG, GnomeRRConfig)) @@ -120,35 +119,27 @@ typedef struct #define GNOME_IS_RR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_RR_CONFIG)) #define GNOME_RR_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_RR_CONFIG, GnomeRRConfigClass)) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GnomeRRConfig, g_object_unref) + GType gnome_rr_config_get_type (void); GnomeRRConfig *gnome_rr_config_new_current (GnomeRRScreen *screen, GError **error); -GnomeRRConfig *gnome_rr_config_new_stored (GnomeRRScreen *screen, +gboolean gnome_rr_config_load_current (GnomeRRConfig *self, GError **error); -gboolean gnome_rr_config_load_current (GnomeRRConfig *self, - GError **error); -gboolean gnome_rr_config_load_filename (GnomeRRConfig *self, - const gchar *filename, - GError **error); gboolean gnome_rr_config_match (GnomeRRConfig *config1, GnomeRRConfig *config2); gboolean gnome_rr_config_equal (GnomeRRConfig *config1, GnomeRRConfig *config2); -gboolean gnome_rr_config_save (GnomeRRConfig *configuration, - GError **error); void gnome_rr_config_sanitize (GnomeRRConfig *configuration); gboolean gnome_rr_config_ensure_primary (GnomeRRConfig *configuration); -gboolean gnome_rr_config_apply_with_time (GnomeRRConfig *configuration, - GnomeRRScreen *screen, - guint32 timestamp, - GError **error); - -gboolean gnome_rr_config_apply_from_filename_with_time (GnomeRRScreen *screen, - const char *filename, - guint32 timestamp, - GError **error); +gboolean gnome_rr_config_apply (GnomeRRConfig *configuration, + GnomeRRScreen *screen, + GError **error); +gboolean gnome_rr_config_apply_persistent (GnomeRRConfig *configuration, + GnomeRRScreen *screen, + GError **error); gboolean gnome_rr_config_applicable (GnomeRRConfig *configuration, GnomeRRScreen *screen, @@ -156,16 +147,6 @@ gboolean gnome_rr_config_applicable (GnomeRRConfig *configuration, gboolean gnome_rr_config_get_clone (GnomeRRConfig *configuration); void gnome_rr_config_set_clone (GnomeRRConfig *configuration, gboolean clone); -guint gnome_rr_config_get_base_scale (GnomeRRConfig *self); -void gnome_rr_config_set_base_scale (GnomeRRConfig *self, - guint base_scale); -gboolean gnome_rr_config_get_auto_scale (GnomeRRConfig *self); -void gnome_rr_config_set_auto_scale (GnomeRRConfig *self, - gboolean auto_scale); GnomeRROutputInfo **gnome_rr_config_get_outputs (GnomeRRConfig *configuration); -char *gnome_rr_config_get_backup_filename (void); -char *gnome_rr_config_get_intended_filename (void); -char *gnome_rr_config_get_legacy_filename (void); - #endif diff --git a/libcinnamon-desktop/gnome-rr-output-info.c b/libcinnamon-desktop/gnome-rr-output-info.c index 22de789..343e8de 100644 --- a/libcinnamon-desktop/gnome-rr-output-info.c +++ b/libcinnamon-desktop/gnome-rr-output-info.c @@ -27,7 +27,6 @@ #include "gnome-rr-config.h" -#include "edid.h" #include "gnome-rr-private.h" G_DEFINE_TYPE_WITH_PRIVATE (GnomeRROutputInfo, gnome_rr_output_info, G_TYPE_OBJECT) @@ -39,8 +38,9 @@ gnome_rr_output_info_init (GnomeRROutputInfo *self) self->priv->name = NULL; self->priv->on = FALSE; + self->priv->rotation = GNOME_RR_ROTATION_0; self->priv->display_name = NULL; - self->priv->scale = 1; + self->priv->connector_type = NULL; } static void @@ -50,6 +50,10 @@ gnome_rr_output_info_finalize (GObject *gobject) g_free (self->priv->name); g_free (self->priv->display_name); + g_free (self->priv->connector_type); + g_free (self->priv->product); + g_free (self->priv->serial); + g_free (self->priv->vendor); G_OBJECT_CLASS (gnome_rr_output_info_parent_class)->finalize (gobject); } @@ -67,7 +71,7 @@ gnome_rr_output_info_class_init (GnomeRROutputInfoClass *klass) * * Returns: (transfer none): the output name */ -const char *gnome_rr_output_info_get_name (GnomeRROutputInfo *self) +char *gnome_rr_output_info_get_name (GnomeRROutputInfo *self) { g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), NULL); @@ -93,6 +97,72 @@ void gnome_rr_output_info_set_active (GnomeRROutputInfo *self, gboolean active) self->priv->on = active; } +static void gnome_rr_output_info_get_tiled_geometry (GnomeRROutputInfo *self, + int *x, + int *y, + int *width, + int *height) +{ + GnomeRROutputInfo **outputs; + gboolean active; + int i; + guint ht, vt; + int total_w = 0, total_h = 0; + + outputs = gnome_rr_config_get_outputs (self->priv->config); + + /* + * iterate over all the outputs from 0,0 -> h,v + * find the output for each tile, + * if it is the 0 tile, store the x/y offsets. + * if the tile is active, add the tile to the total w/h + * for the output if the tile is in the 0 row or 0 column. + */ + for (ht = 0; ht < self->priv->tile.max_horiz_tiles; ht++) + { + for (vt = 0; vt < self->priv->tile.max_vert_tiles; vt++) + { + for (i = 0; outputs[i]; i++) + { + GnomeRRTile *this_tile = &outputs[i]->priv->tile; + + if (!outputs[i]->priv->is_tiled) + continue; + + if (this_tile->group_id != self->priv->tile.group_id) + continue; + + if (this_tile->loc_horiz != ht || + this_tile->loc_vert != vt) + continue; + + if (vt == 0 && ht == 0) + { + if (x) + *x = outputs[i]->priv->x; + if (y) + *y = outputs[i]->priv->y; + } + + active = gnome_rr_output_info_is_active (outputs[i]); + if (!active) + continue; + + if (this_tile->loc_horiz == 0) + total_h += outputs[i]->priv->height; + + if (this_tile->loc_vert == 0) + total_w += outputs[i]->priv->width; + } + } + } + + if (width) + *width = total_w; + if (height) + *height = total_h; +} + /** * gnome_rr_output_info_get_geometry: * @self: a #GnomeRROutputInfo @@ -100,11 +170,20 @@ void gnome_rr_output_info_set_active (GnomeRROutputInfo *self, gboolean active) * @y: (out) (allow-none): * @width: (out) (allow-none): * @height: (out) (allow-none): + * + * Get the geometry for the monitor connected to the specified output. + * If the monitor is a tiled monitor, it returns the geometry for the complete monitor. */ void gnome_rr_output_info_get_geometry (GnomeRROutputInfo *self, int *x, int *y, int *width, int *height) { g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); + if (self->priv->is_tiled) + { + gnome_rr_output_info_get_tiled_geometry (self, x, y, width, height); + return; + } + if (x) *x = self->priv->x; if (y) @@ -115,52 +194,125 @@ void gnome_rr_output_info_get_geometry (GnomeRROutputInfo *self, int *x, int *y, *height = self->priv->height; } -void gnome_rr_output_info_set_geometry (GnomeRROutputInfo *self, int x, int y, int width, int height) +static void gnome_rr_output_info_set_tiled_geometry (GnomeRROutputInfo *self, int x, int y, int width, int height) { - g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); - - self->priv->x = x; - self->priv->y = y; - self->priv->width = width; - self->priv->height = height; + GnomeRROutputInfo **outputs; + gboolean primary_tile_only = FALSE; + guint ht, vt; + int i, x_off; + + primary_tile_only = TRUE; + + if (width == self->priv->total_tiled_width && + height == self->priv->total_tiled_height) + primary_tile_only = FALSE; + + outputs = gnome_rr_config_get_outputs (self->priv->config); + /* + * iterate over all the outputs from 0,0 -> h,v + * find the output for each tile, + * if only the primary tile is being set, disable + * the non-primary tiles, and set the output up + * for tile 0 only. + * if all tiles are being set, then store the + * dimensions for this tile, and increase the offsets. + * y_off is reset per column of tiles, + * addx is incremented for the first row of tiles + * to set the correct x offset. + */ + x_off = 0; + for (ht = 0; ht < self->priv->tile.max_horiz_tiles; ht++) + { + int y_off = 0; + int addx = 0; + for (vt = 0; vt < self->priv->tile.max_vert_tiles; vt++) + { + for (i = 0; outputs[i]; i++) + { + GnomeRRTile *this_tile = &outputs[i]->priv->tile; + + if (!outputs[i]->priv->is_tiled) + continue; + + if (this_tile->group_id != self->priv->tile.group_id) + continue; + + if (this_tile->loc_horiz != ht || + this_tile->loc_vert != vt) + continue; + + /* for primary tile only configs turn off non-primary + tiles - turn them on for tiled ones */ + if (ht != 0 || vt != 0) + { + if (self->priv->on == FALSE) + outputs[i]->priv->on = FALSE; + else + outputs[i]->priv->on = !primary_tile_only; + } + + if (primary_tile_only) + { + if (ht == 0 && vt == 0) + { + outputs[i]->priv->x = x; + outputs[i]->priv->y = y; + outputs[i]->priv->width = width; + outputs[i]->priv->height = height; + } + } + else + { + outputs[i]->priv->x = x + x_off; + outputs[i]->priv->y = y + y_off; + outputs[i]->priv->width = this_tile->width; + outputs[i]->priv->height = this_tile->height; + + y_off += this_tile->height; + if (vt == 0) + addx = this_tile->width; + } + } + } + x_off += addx; + } } -float gnome_rr_output_info_get_scale (GnomeRROutputInfo *self) -{ - g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), MINIMUM_LOGICAL_SCALE_FACTOR); - - return self->priv->scale; -} +/** + * gnome_rr_output_info_set_geometry: + * @self: a #GnomeRROutputInfo + * @x: x offset for monitor + * @y: y offset for monitor + * @width: monitor width + * @height: monitor height + * + * Set the geometry for the monitor connected to the specified output. + * If the monitor is a tiled monitor, it sets the geometry for the complete monitor. + */ -void gnome_rr_output_info_set_scale (GnomeRROutputInfo *self, float scale) +void gnome_rr_output_info_set_geometry (GnomeRROutputInfo *self, int x, int y, int width, int height) { g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); - self->priv->scale = scale; + if (self->priv->is_tiled) + { + gnome_rr_output_info_set_tiled_geometry (self, x, y, width, height); + return; + } + self->priv->x = x; + self->priv->y = y; + self->priv->width = width; + self->priv->height = height; } int gnome_rr_output_info_get_refresh_rate (GnomeRROutputInfo *self) { g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), 0); - return (int) self->priv->rate; -} - -void gnome_rr_output_info_set_refresh_rate (GnomeRROutputInfo *self, int rate) -{ - g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); - - self->priv->rate = rate * 1.0; -} - -double gnome_rr_output_info_get_refresh_rate_f (GnomeRROutputInfo *self) -{ - g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), 0); - return self->priv->rate; } -void gnome_rr_output_info_set_refresh_rate_f (GnomeRROutputInfo *self, double rate) +void gnome_rr_output_info_set_refresh_rate (GnomeRROutputInfo *self, int rate) { g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); @@ -174,38 +326,98 @@ GnomeRRRotation gnome_rr_output_info_get_rotation (GnomeRROutputInfo *self) return self->priv->rotation; } -void gnome_rr_output_info_set_rotation (GnomeRROutputInfo *self, GnomeRRRotation rotation) +static void gnome_rr_output_info_set_tiled_rotation (GnomeRROutputInfo *self, GnomeRRRotation rotation) { - g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); - - self->priv->rotation = rotation; + GnomeRROutputInfo **outputs; + int x_off; + int base_x = 0, base_y = 0; + guint ht, vt; + int i; + + outputs = gnome_rr_config_get_outputs (self->priv->config); + x_off = 0; + /* + * iterate over all the outputs from 0,0 -> h,v + * find the output for each tile, + * for all tiles set the rotation, + * for the 0 tile use the base X/Y offsets + * for non-0 tile, rotate the offsets of each + * tile so things display correctly. + */ + for (ht = 0; ht < self->priv->tile.max_horiz_tiles; ht++) + { + int y_off = 0; + int addx = 0; + for (vt = 0; vt < self->priv->tile.max_vert_tiles; vt++) + { + for (i = 0; outputs[i] != NULL; i++) + { + GnomeRRTile *this_tile = &outputs[i]->priv->tile; + int new_x, new_y; + + if (!outputs[i]->priv->is_tiled) + continue; + + if (this_tile->group_id != self->priv->tile.group_id) + continue; + + if (this_tile->loc_horiz != ht || + this_tile->loc_vert != vt) + continue; + + /* set tile rotation */ + outputs[i]->priv->rotation = rotation; + + /* for non-zero tiles - change the offsets */ + if (ht == 0 && vt == 0) + { + base_x = outputs[i]->priv->x; + base_y = outputs[i]->priv->y; + } + else + { + if ((rotation & GNOME_RR_ROTATION_90) || (rotation & GNOME_RR_ROTATION_270)) + { + new_x = base_x + y_off; + new_y = base_y + x_off; + } + else + { + new_x = base_x + x_off; + new_y = base_y + y_off; + } + outputs[i]->priv->x = new_x; + outputs[i]->priv->y = new_y; + outputs[i]->priv->width = this_tile->width; + outputs[i]->priv->height = this_tile->height; + } + + y_off += this_tile->height; + if (vt == 0) + addx = this_tile->width; + } + } + x_off += addx; + } } -void gnome_rr_output_info_get_flags (GnomeRROutputInfo *self, - gboolean *doublescan, - gboolean *interlaced, - gboolean *vsync) +void gnome_rr_output_info_set_rotation (GnomeRROutputInfo *self, GnomeRRRotation rotation) { g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); - if (doublescan) - *doublescan = self->priv->doublescan; - if (interlaced) - *interlaced = self->priv->interlaced; - if (vsync) - *vsync = self->priv->vsync; + if (self->priv->is_tiled) + { + gnome_rr_output_info_set_tiled_rotation (self, rotation); + return; + } + self->priv->rotation = rotation; } -void gnome_rr_output_info_set_flags (GnomeRROutputInfo *self, - gboolean doublescan, - gboolean interlaced, - gboolean vsync) +gboolean gnome_rr_output_info_supports_rotation (GnomeRROutputInfo *self, GnomeRRRotation rotation) { - g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); + g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), FALSE); - self->priv->doublescan = doublescan; - self->priv->interlaced = interlaced; - self->priv->vsync = vsync; + return (self->priv->available_rotations & rotation); } /** @@ -223,29 +435,27 @@ gboolean gnome_rr_output_info_is_connected (GnomeRROutputInfo *self) /** * gnome_rr_output_info_get_vendor: * @self: a #GnomeRROutputInfo - * @vendor: (out caller-allocates) (array fixed-size=4): */ -void gnome_rr_output_info_get_vendor (GnomeRROutputInfo *self, gchar* vendor) +const char * +gnome_rr_output_info_get_vendor (GnomeRROutputInfo *self) { - g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); - g_return_if_fail (vendor != NULL); + g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), NULL); - vendor[0] = self->priv->vendor[0]; - vendor[1] = self->priv->vendor[1]; - vendor[2] = self->priv->vendor[2]; - vendor[3] = self->priv->vendor[3]; + return self->priv->vendor; } -guint gnome_rr_output_info_get_product (GnomeRROutputInfo *self) +const char * +gnome_rr_output_info_get_product (GnomeRROutputInfo *self) { - g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), 0); + g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), NULL); return self->priv->product; } -guint gnome_rr_output_info_get_serial (GnomeRROutputInfo *self) +const char * +gnome_rr_output_info_get_serial (GnomeRROutputInfo *self) { - g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), 0); + g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), NULL); return self->priv->serial; } @@ -262,7 +472,7 @@ double gnome_rr_output_info_get_aspect_ratio (GnomeRROutputInfo *self) * * Returns: (transfer none): the display name of this output */ -const char *gnome_rr_output_info_get_display_name (GnomeRROutputInfo *self) +char *gnome_rr_output_info_get_display_name (GnomeRROutputInfo *self) { g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), NULL); @@ -296,3 +506,40 @@ int gnome_rr_output_info_get_preferred_height (GnomeRROutputInfo *self) return self->priv->pref_height; } + +gboolean gnome_rr_output_info_get_underscanning (GnomeRROutputInfo *self) +{ + g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), FALSE); + + return self->priv->underscanning; +} + +void gnome_rr_output_info_set_underscanning (GnomeRROutputInfo *self, + gboolean underscanning) +{ + g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self)); + + self->priv->underscanning = underscanning; +} + +/** + * gnome_rr_output_info_is_primary_tile + * @self: a #GnomeRROutputInfo + * + * Returns: %TRUE if the specified output is connected to + * the primary tile of a monitor or to an untiled monitor, + * %FALSE if the output is connected to a secondary tile. + */ +gboolean gnome_rr_output_info_is_primary_tile(GnomeRROutputInfo *self) +{ + g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), FALSE); + + if (!self->priv->is_tiled) + return TRUE; + + if (self->priv->tile.loc_horiz == 0 && + self->priv->tile.loc_vert == 0) + return TRUE; + + return FALSE; +} diff --git a/libcinnamon-desktop/gnome-rr-private.h b/libcinnamon-desktop/gnome-rr-private.h index 8cce026..36ad776 100644 --- a/libcinnamon-desktop/gnome-rr-private.h +++ b/libcinnamon-desktop/gnome-rr-private.h @@ -1,14 +1,23 @@ #ifndef GNOME_RR_PRIVATE_H #define GNOME_RR_PRIVATE_H -#include - -#include +#ifdef GDK_WINDOWING_WAYLAND +#include +#else +enum wl_output_transform { + WL_OUTPUT_TRANSFORM_NORMAL, + WL_OUTPUT_TRANSFORM_90, + WL_OUTPUT_TRANSFORM_180, + WL_OUTPUT_TRANSFORM_270, + WL_OUTPUT_TRANSFORM_FLIPPED, + WL_OUTPUT_TRANSFORM_FLIPPED_90, + WL_OUTPUT_TRANSFORM_FLIPPED_180, + WL_OUTPUT_TRANSFORM_FLIPPED_270 +}; +#endif -#define MINIMUM_LOGICAL_SCALE_FACTOR 0.74f -#define MAXIMUM_LOGICAL_SCALE_FACTOR 3.0f -#define MINIMUM_GLOBAL_SCALE_FACTOR 1 -#define MAXIMUM_GLOBAL_SCALE_FACTOR 3 +#include "meta-xrandr-shared.h" +#include "meta-dbus-xrandr.h" typedef struct ScreenInfo ScreenInfo; @@ -19,7 +28,7 @@ struct ScreenInfo int min_height; int max_height; - XRRScreenResources *resources; + guint serial; GnomeRROutput ** outputs; GnomeRRCrtc ** crtcs; @@ -29,63 +38,87 @@ struct ScreenInfo GnomeRRMode ** clone_modes; - RROutput primary; + GnomeRROutput * primary; }; struct GnomeRRScreenPrivate { GdkScreen * gdk_screen; GdkWindow * gdk_root; - Display * xdisplay; - Screen * xscreen; - Window xroot; ScreenInfo * info; - GSettings * interface_settings; - - int randr_event_base; - int rr_major_version; - int rr_minor_version; - - Atom connector_type_atom; - gboolean dpms_capable; + + int init_name_watch_id; + MetaDBusDisplayConfig *proxy; }; -struct GnomeRROutputInfoPrivate +#define UNDEFINED_GROUP_ID 0 +struct GnomeRRTile { + guint group_id; + guint flags; + guint max_horiz_tiles; + guint max_vert_tiles; + guint loc_horiz; + guint loc_vert; + guint width; + guint height; +}; + +typedef struct GnomeRRTile GnomeRRTile; + +struct _GnomeRROutputInfoPrivate { char * name; gboolean on; int width; int height; - double rate; + int rate; int x; int y; GnomeRRRotation rotation; + GnomeRRRotation available_rotations; gboolean connected; - gchar vendor[4]; - guint product; - guint serial; + char * vendor; + char * product; + char * serial; double aspect; int pref_width; int pref_height; char * display_name; + char * connector_type; gboolean primary; - float scale; - gboolean doublescan; - gboolean interlaced; - gboolean vsync; + gboolean underscanning; + + gboolean is_tiled; + GnomeRRTile tile; + + int total_tiled_width; + int total_tiled_height; + /* ptr back to info */ + GnomeRRConfig *config; }; -struct GnomeRRConfigPrivate +struct _GnomeRRConfigPrivate { gboolean clone; GnomeRRScreen *screen; GnomeRROutputInfo **outputs; - guint base_scale; - gboolean auto_scale; }; -gboolean _gnome_rr_output_name_is_laptop (const char *name); +gboolean _gnome_rr_output_connector_type_is_builtin_display (const char *connector_type); + +gboolean _gnome_rr_screen_apply_configuration (GnomeRRScreen *screen, + gboolean persistent, + GVariant *crtcs, + GVariant *outputs, + GError **error); + +const char * _gnome_rr_output_get_connector_type (GnomeRROutput *output); +gboolean _gnome_rr_output_get_tile_info (GnomeRROutput *output, + GnomeRRTile *tile); +gboolean _gnome_rr_output_get_tiled_display_size (GnomeRROutput *output, + int *tile_w, int *tile_h, + int *width, int *height); #endif diff --git a/libcinnamon-desktop/gnome-rr.c b/libcinnamon-desktop/gnome-rr.c index af33ef0..4632c55 100644 --- a/libcinnamon-desktop/gnome-rr.c +++ b/libcinnamon-desktop/gnome-rr.c @@ -1,6 +1,6 @@ /* gnome-rr.c * - * Copyright 2007, 2008, Red Hat, Inc. + * Copyright 2007, 2008, 2013 Red Hat, Inc. * * This file is part of the Gnome Library. * @@ -20,6 +20,7 @@ * Boston, MA 02110-1301, USA. * * Author: Soren Sandmann + * Giovanni Campagna */ #define GNOME_DESKTOP_USE_UNSTABLE_API @@ -27,35 +28,22 @@ #include #include #include -#include -#include - -#include #include -#include -#include -#include #undef GNOME_DISABLE_DEPRECATED #include "gnome-rr.h" #include "gnome-rr-config.h" -#include "private.h" -#include "edid.h" #include "gnome-rr-private.h" -#define DISPLAY(o) ((o)->info->screen->priv->xdisplay) - -#define SERVERS_RANDR_IS_AT_LEAST_1_3(priv) (priv->rr_major_version > 1 || (priv->rr_major_version == 1 && priv->rr_minor_version >= 3)) - -#define INTERFACE_SETTINGS "org.cinnamon.desktop.interface" -#define GLOBAL_SCALE_FACTOR_KEY "scaling-factor" -#define USE_UPSCALING_KEY "upscale-fractional-scaling" +/* From xf86drmMode.h: it's ABI so it won't change */ +#define DRM_MODE_FLAG_INTERLACE (1<<4) enum { SCREEN_PROP_0, SCREEN_PROP_GDK_SCREEN, + SCREEN_PROP_DPMS_MODE, SCREEN_PROP_LAST, }; @@ -66,91 +54,96 @@ enum { SCREEN_SIGNAL_LAST, }; -static guint screen_signals[SCREEN_SIGNAL_LAST] = { 0 }; +static gint screen_signals[SCREEN_SIGNAL_LAST]; struct GnomeRROutput { ScreenInfo * info; - RROutput id; + guint id; + glong winsys_id; char * name; + char * display_name; + char * connector_type; GnomeRRCrtc * current_crtc; - gboolean connected; - gulong width_mm; - gulong height_mm; GnomeRRCrtc ** possible_crtcs; GnomeRROutput ** clones; GnomeRRMode ** modes; - int n_preferred; - guint8 * edid_data; - gsize edid_size; - char * connector_type; - gint backlight_min; - gint backlight_max; -}; -struct GnomeRROutputWrap -{ - RROutput id; + char * vendor; + char * product; + char * serial; + int width_mm; + int height_mm; + GBytes * edid; + char * edid_file; + + int backlight; + int min_backlight_step; + + gboolean is_primary; + gboolean is_presentation; + gboolean is_underscanning; + gboolean supports_underscanning; + + GnomeRRTile tile_info; }; struct GnomeRRCrtc { ScreenInfo * info; - RRCrtc id; + guint id; + glong winsys_id; GnomeRRMode * current_mode; GnomeRROutput ** current_outputs; GnomeRROutput ** possible_outputs; int x; int y; - float scale; - GnomeRRRotation current_rotation; - GnomeRRRotation rotations; + enum wl_output_transform transform; + int all_transforms; int gamma_size; }; +#define UNDEFINED_MODE_ID 0 struct GnomeRRMode { ScreenInfo * info; - RRMode id; - char * name; + guint id; + glong winsys_id; int width; int height; int freq; /* in mHz */ - gboolean doublescan; - gboolean interlaced; - gboolean vsync; + gboolean tiled; + guint32 flags; }; /* GnomeRRCrtc */ static GnomeRRCrtc * crtc_new (ScreenInfo *info, - RRCrtc id); + guint id); static GnomeRRCrtc * crtc_copy (const GnomeRRCrtc *from); static void crtc_free (GnomeRRCrtc *crtc); -static gboolean crtc_initialize (GnomeRRCrtc *crtc, - XRRScreenResources *res, - GError **error); +static void crtc_initialize (GnomeRRCrtc *crtc, + GVariant *res); /* GnomeRROutput */ static GnomeRROutput *output_new (ScreenInfo *info, - RROutput id); + guint id); -static gboolean output_initialize (GnomeRROutput *output, - XRRScreenResources *res, - GError **error); +static void output_initialize (GnomeRROutput *output, + GVariant *res); static GnomeRROutput *output_copy (const GnomeRROutput *from); static void output_free (GnomeRROutput *output); /* GnomeRRMode */ static GnomeRRMode * mode_new (ScreenInfo *info, - RRMode id); + guint id); static void mode_initialize (GnomeRRMode *mode, - XRRModeInfo *info); + GVariant *info); static GnomeRRMode * mode_copy (const GnomeRRMode *from); static void mode_free (GnomeRRMode *mode); @@ -160,9 +153,11 @@ static void gnome_rr_screen_set_property (GObject*, guint, const GValue*, GParam static void gnome_rr_screen_get_property (GObject*, guint, GValue*, GParamSpec*); static gboolean gnome_rr_screen_initable_init (GInitable*, GCancellable*, GError**); static void gnome_rr_screen_initable_iface_init (GInitableIface *iface); +static void gnome_rr_screen_async_initable_init (GAsyncInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (GnomeRRScreen, gnome_rr_screen, G_TYPE_OBJECT, - G_ADD_PRIVATE (GnomeRRScreen) - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gnome_rr_screen_initable_iface_init)) + G_ADD_PRIVATE (GnomeRRScreen) + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gnome_rr_screen_initable_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gnome_rr_screen_async_initable_init)) G_DEFINE_BOXED_TYPE (GnomeRRCrtc, gnome_rr_crtc, crtc_copy, crtc_free) G_DEFINE_BOXED_TYPE (GnomeRROutput, gnome_rr_output, output_copy, output_free) @@ -186,7 +181,7 @@ gnome_rr_error_quark (void) /* Screen */ static GnomeRROutput * -gnome_rr_output_by_id (ScreenInfo *info, RROutput id) +gnome_rr_output_by_id (ScreenInfo *info, guint id) { GnomeRROutput **output; @@ -202,7 +197,7 @@ gnome_rr_output_by_id (ScreenInfo *info, RROutput id) } static GnomeRRCrtc * -crtc_by_id (ScreenInfo *info, RRCrtc id) +crtc_by_id (ScreenInfo *info, guint id) { GnomeRRCrtc **crtc; @@ -219,7 +214,7 @@ crtc_by_id (ScreenInfo *info, RRCrtc id) } static GnomeRRMode * -mode_by_id (ScreenInfo *info, RRMode id) +mode_by_id (ScreenInfo *info, guint id) { GnomeRRMode **mode; @@ -243,13 +238,6 @@ screen_info_free (ScreenInfo *info) g_assert (info != NULL); - if (info->resources) - { - XRRFreeScreenResources (info->resources); - - info->resources = NULL; - } - if (info->outputs) { for (output = info->outputs; *output; ++output) @@ -285,253 +273,293 @@ has_similar_mode (GnomeRROutput *output, GnomeRRMode *mode) { int i; GnomeRRMode **modes = gnome_rr_output_list_modes (output); - int width = gnome_rr_mode_get_width (mode); - int height = gnome_rr_mode_get_height (mode); + guint width = gnome_rr_mode_get_width (mode); + guint height = gnome_rr_mode_get_height (mode); for (i = 0; modes[i] != NULL; ++i) { - GnomeRRMode *m = modes[i]; + GnomeRRMode *m = modes[i]; - if (gnome_rr_mode_get_width (m) == width && - gnome_rr_mode_get_height (m) == height) - { - return TRUE; - } + if (gnome_rr_mode_get_width (m) == width && + gnome_rr_mode_get_height (m) == height) + { + return TRUE; + } } return FALSE; } +gboolean +_gnome_rr_output_get_tiled_display_size (GnomeRROutput *output, + int *tile_w, int *tile_h, + int *total_width, int *total_height) +{ + GnomeRRTile tile; + guint ht, vt; + int i, total_h = 0, total_w = 0; + + if (!_gnome_rr_output_get_tile_info (output, &tile)) + return FALSE; + + if (tile.loc_horiz != 0 || + tile.loc_vert != 0) + return FALSE; + + if (tile_w) + *tile_w = tile.width; + if (tile_h) + *tile_h = tile.height; + + for (ht = 0; ht < tile.max_horiz_tiles; ht++) + { + for (vt = 0; vt < tile.max_vert_tiles; vt++) + { + for (i = 0; output->info->outputs[i]; i++) + { + GnomeRRTile this_tile; + + if (!_gnome_rr_output_get_tile_info (output->info->outputs[i], &this_tile)) + continue; + + if (this_tile.group_id != tile.group_id) + continue; + + if (this_tile.loc_horiz != ht || + this_tile.loc_vert != vt) + continue; + + if (this_tile.loc_horiz == 0) + total_h += this_tile.height; + + if (this_tile.loc_vert == 0) + total_w += this_tile.width; + } + } + } + + *total_width = total_w; + *total_height = total_h; + return TRUE; +} + static void -gather_clone_modes (ScreenInfo *info) +gather_tile_modes_output (ScreenInfo *info, GnomeRROutput *output) { + GPtrArray *a; + GnomeRRMode *mode; + int width, height; + int tile_w, tile_h; int i; - GPtrArray *result = g_ptr_array_new (); - for (i = 0; info->outputs[i] != NULL; ++i) - { - int j; - GnomeRROutput *output1, *output2; + if (!_gnome_rr_output_get_tiled_display_size (output, &tile_w, &tile_h, + &width, &height)) + return; - output1 = info->outputs[i]; + /* now stick the mode into the modelist */ + a = g_ptr_array_new (); + mode = mode_new (info, UNDEFINED_MODE_ID); + mode->winsys_id = 0; + mode->width = width; + mode->height = height; + mode->freq = 0; + mode->tiled = TRUE; + + g_ptr_array_add (a, mode); + for (i = 0; output->modes[i]; i++) + g_ptr_array_add (a, output->modes[i]); - if (!output1->connected) - continue; + g_ptr_array_add (a, NULL); + output->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE); +} - for (j = 0; output1->modes[j] != NULL; ++j) - { - GnomeRRMode *mode = output1->modes[j]; - gboolean valid; - int k; +static void +gather_tile_modes (ScreenInfo *info) +{ + int i; - valid = TRUE; - for (k = 0; info->outputs[k] != NULL; ++k) - { - output2 = info->outputs[k]; + for (i = 0; info->outputs[i]; i++) + gather_tile_modes_output (info, info->outputs[i]); +} - if (!output2->connected) - continue; +static void +gather_clone_modes (ScreenInfo *info) +{ + int i; + GPtrArray *result = g_ptr_array_new (); - if (!has_similar_mode (output2, mode)) - { - valid = FALSE; - break; - } - } + for (i = 0; info->outputs[i] != NULL; ++i) + { + int j; + GnomeRROutput *output1, *output2; - if (valid) - g_ptr_array_add (result, mode); - } + output1 = info->outputs[i]; + + for (j = 0; output1->modes[j] != NULL; ++j) + { + GnomeRRMode *mode = output1->modes[j]; + gboolean valid; + int k; + + valid = TRUE; + for (k = 0; info->outputs[k] != NULL; ++k) + { + output2 = info->outputs[k]; + + if (!has_similar_mode (output2, mode)) + { + valid = FALSE; + break; + } + } + + if (valid) + g_ptr_array_add (result, mode); + } } g_ptr_array_add (result, NULL); - + info->clone_modes = (GnomeRRMode **)g_ptr_array_free (result, FALSE); } -static gboolean +static void fill_screen_info_from_resources (ScreenInfo *info, - XRRScreenResources *resources, - GError **error) + guint serial, + GVariant *crtcs, + GVariant *outputs, + GVariant *modes, + int max_width, + int max_height) { - int i; + guint i; GPtrArray *a; GnomeRRCrtc **crtc; GnomeRROutput **output; - info->resources = resources; + GnomeRRMode **mode; + guint ncrtc, noutput, nmode; + guint id; + + info->min_width = 312; + info->min_height = 312; + info->max_width = max_width; + info->max_height = max_height; + info->serial = serial; + + ncrtc = g_variant_n_children (crtcs); + noutput = g_variant_n_children (outputs); + nmode = g_variant_n_children (modes); /* We create all the structures before initializing them, so * that they can refer to each other. */ a = g_ptr_array_new (); - - for (i = 0; i < resources->ncrtc; ++i) + for (i = 0; i < ncrtc; ++i) { - GnomeRRCrtc *crtc = crtc_new (info, resources->crtcs[i]); + g_variant_get_child (crtcs, i, META_CRTC_STRUCT, &id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - g_ptr_array_add (a, crtc); + g_ptr_array_add (a, crtc_new (info, id)); } - g_ptr_array_add (a, NULL); info->crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE); a = g_ptr_array_new (); - for (i = 0; i < resources->noutput; ++i) + for (i = 0; i < noutput; ++i) { - GnomeRROutput *output = output_new (info, resources->outputs[i]); + g_variant_get_child (outputs, i, META_OUTPUT_STRUCT, &id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL); - g_ptr_array_add (a, output); + g_ptr_array_add (a, output_new (info, id)); } - g_ptr_array_add (a, NULL); info->outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE); a = g_ptr_array_new (); - for (i = 0; i < resources->nmode; ++i) + for (i = 0; i < nmode; ++i) { - GnomeRRMode *mode = mode_new (info, resources->modes[i].id); + g_variant_get_child (modes, i, META_MONITOR_MODE_STRUCT, &id, + NULL, NULL, NULL, NULL, NULL); - g_ptr_array_add (a, mode); + g_ptr_array_add (a, mode_new (info, id)); } - g_ptr_array_add (a, NULL); info->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE); /* Initialize */ - for (crtc = info->crtcs; *crtc; ++crtc) + for (i = 0, crtc = info->crtcs; *crtc; ++i, ++crtc) { - if (!crtc_initialize (*crtc, resources, error)) - return FALSE; + GVariant *child = g_variant_get_child_value (crtcs, i); + crtc_initialize (*crtc, child); + g_variant_unref (child); } - for (output = info->outputs; *output; ++output) + for (i = 0, output = info->outputs; *output; ++i, ++output) { - if (!output_initialize (*output, resources, error)) - return FALSE; + GVariant *child = g_variant_get_child_value (outputs, i); + output_initialize (*output, child); + g_variant_unref (child); } - for (i = 0; i < resources->nmode; ++i) + for (i = 0, mode = info->modes; *mode; ++i, ++mode) { - GnomeRRMode *mode = mode_by_id (info, resources->modes[i].id); - - mode_initialize (mode, &(resources->modes[i])); + GVariant *child = g_variant_get_child_value (modes, i); + mode_initialize (*mode, child); + g_variant_unref (child); } gather_clone_modes (info); - return TRUE; + gather_tile_modes (info); } static gboolean -fill_out_screen_info (Display *xdisplay, - Window xroot, - ScreenInfo *info, - gboolean needs_reprobe, - GError **error) +fill_out_screen_info (ScreenInfo *info, + GError **error) { - XRRScreenResources *resources; GnomeRRScreenPrivate *priv; - - g_assert (xdisplay != NULL); + guint serial; + GVariant *crtcs, *outputs, *modes; + int max_width, max_height; + g_assert (info != NULL); priv = info->screen->priv; - /* First update the screen resources */ - - if (needs_reprobe) - resources = XRRGetScreenResources (xdisplay, xroot); - else - { - /* XRRGetScreenResourcesCurrent is less expensive than - * XRRGetScreenResources, however it is available only - * in RandR 1.3 or higher - */ - if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv)) - resources = XRRGetScreenResourcesCurrent (xdisplay, xroot); - else - resources = XRRGetScreenResources (xdisplay, xroot); - } - - if (resources) - { - if (!fill_screen_info_from_resources (info, resources, error)) - return FALSE; - } - else - { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR, - /* Translators: a CRTC is a CRT Controller (this is X terminology). */ - _("could not get the screen resources (CRTCs, outputs, modes)")); + if (!meta_dbus_display_config_call_get_resources_sync (priv->proxy, + &serial, + &crtcs, + &outputs, + &modes, + &max_width, + &max_height, + NULL, + error)) return FALSE; - } - - /* Then update the screen size range. We do this after XRRGetScreenResources() so that - * the X server will already have an updated view of the outputs. - */ - - if (needs_reprobe) { - gboolean success; - - gdk_error_trap_push (); - success = XRRGetScreenSizeRange (xdisplay, xroot, - &(info->min_width), - &(info->min_height), - &(info->max_width), - &(info->max_height)); - gdk_flush (); - if (gdk_error_trap_pop ()) { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_UNKNOWN, - _("unhandled X error while getting the range of screen sizes")); - return FALSE; - } - - if (!success) { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR, - _("could not get the range of screen sizes")); - return FALSE; - } - } - else - { - gnome_rr_screen_get_ranges (info->screen, - &(info->min_width), - &(info->max_width), - &(info->min_height), - &(info->max_height)); - } - info->primary = None; - if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv)) { - gdk_error_trap_push (); - info->primary = XRRGetOutputPrimary (xdisplay, xroot); - gdk_error_trap_pop_ignored (); - } + fill_screen_info_from_resources (info, serial, crtcs, outputs, + modes, max_width, max_height); - /* can the screen do DPMS? */ - gdk_error_trap_push (); - priv->dpms_capable = DPMSCapable (priv->xdisplay); - gdk_error_trap_pop_ignored (); + g_variant_unref (crtcs); + g_variant_unref (outputs); + g_variant_unref (modes); return TRUE; } static ScreenInfo * -screen_info_new (GnomeRRScreen *screen, gboolean needs_reprobe, GError **error) +screen_info_new (GnomeRRScreen *screen, GError **error) { ScreenInfo *info = g_new0 (ScreenInfo, 1); - GnomeRRScreenPrivate *priv; g_assert (screen != NULL); - priv = screen->priv; - info->outputs = NULL; info->crtcs = NULL; info->modes = NULL; info->screen = screen; - if (fill_out_screen_info (priv->xdisplay, priv->xroot, info, needs_reprobe, error)) + if (fill_out_screen_info (info, error)) { return info; } @@ -543,13 +571,13 @@ screen_info_new (GnomeRRScreen *screen, gboolean needs_reprobe, GError **error) } static GnomeRROutput * -find_output_by_id (GnomeRROutput **haystack, guint32 id) +find_output_by_winsys_id (GnomeRROutput **haystack, glong winsys_id) { guint i; for (i = 0; haystack[i] != NULL; i++) { - if (gnome_rr_output_get_id (haystack[i]) == id) + if (haystack[i]->winsys_id == winsys_id) return haystack[i]; } return NULL; @@ -559,183 +587,106 @@ static void diff_outputs_and_emit_signals (ScreenInfo *old, ScreenInfo *new) { guint i; - guint32 id_old, id_new; + gulong winsys_id_old, winsys_id_new; GnomeRROutput *output_old; GnomeRROutput *output_new; - /* have any outputs been removed or disconnected */ + /* have any outputs been removed/disconnected */ for (i = 0; old->outputs[i] != NULL; i++) { - id_old = gnome_rr_output_get_id (old->outputs[i]); - output_new = find_output_by_id (new->outputs, id_old); + winsys_id_old = old->outputs[i]->winsys_id; + output_new = find_output_by_winsys_id (new->outputs, winsys_id_old); if (output_new == NULL) { - /* output removed (and disconnected) */ - if (gnome_rr_output_is_connected (old->outputs[i])) - { - g_signal_emit (G_OBJECT (new->screen), - screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0, - old->outputs[i]); - } - continue; - } - if (gnome_rr_output_is_connected (old->outputs[i]) && - !gnome_rr_output_is_connected (output_new)) - { - /* output disconnected */ g_signal_emit (G_OBJECT (new->screen), screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0, old->outputs[i]); - } + } } - /* have any outputs been created or connected */ + /* have any outputs been created/connected */ for (i = 0; new->outputs[i] != NULL; i++) { - id_new = gnome_rr_output_get_id (new->outputs[i]); - output_old = find_output_by_id (old->outputs, id_new); + winsys_id_new = new->outputs[i]->winsys_id; + output_old = find_output_by_winsys_id (old->outputs, winsys_id_new); if (output_old == NULL) { - /* output created */ - if (gnome_rr_output_is_connected (new->outputs[i])) - { - g_signal_emit (G_OBJECT (new->screen), - screen_signals[SCREEN_OUTPUT_CONNECTED], 0, - new->outputs[i]); - } - continue; - } - if (!gnome_rr_output_is_connected (output_old) && - gnome_rr_output_is_connected (new->outputs[i])) - { - /* output connected */ g_signal_emit (G_OBJECT (new->screen), screen_signals[SCREEN_OUTPUT_CONNECTED], 0, new->outputs[i]); - } + } } } +typedef enum { + REFRESH_NONE = 0, + REFRESH_IGNORE_SERIAL = 1, + REFRESH_FORCE_CALLBACK = 2 +} RefreshFlags; + static gboolean -screen_update (GnomeRRScreen *screen, gboolean force_callback, gboolean needs_reprobe, GError **error) +screen_update (GnomeRRScreen *screen, RefreshFlags flags, GError **error) { ScreenInfo *info; gboolean changed = FALSE; g_assert (screen != NULL); - info = screen_info_new (screen, needs_reprobe, error); + info = screen_info_new (screen, error); if (!info) return FALSE; - if (info->resources->configTimestamp != screen->priv->info->resources->configTimestamp) + if ((flags & REFRESH_IGNORE_SERIAL) || info->serial != screen->priv->info->serial) changed = TRUE; /* work out if any outputs have changed connected state */ diff_outputs_and_emit_signals (screen->priv->info, info); screen_info_free (screen->priv->info); - screen->priv->info = info; - if (changed || force_callback) + if (changed || (flags & REFRESH_FORCE_CALLBACK)) g_signal_emit (G_OBJECT (screen), screen_signals[SCREEN_CHANGED], 0); return changed; } -static GdkFilterReturn -screen_on_event (GdkXEvent *xevent, - GdkEvent *event, - gpointer data) +static void +screen_on_monitors_changed (MetaDBusDisplayConfig *proxy, + gpointer data) { GnomeRRScreen *screen = data; - GnomeRRScreenPrivate *priv = screen->priv; - XEvent *e = xevent; - int event_num; - - if (!e) - return GDK_FILTER_CONTINUE; - - event_num = e->type - priv->randr_event_base; - - if (event_num == RRScreenChangeNotify) { - /* We don't reprobe the hardware; we just fetch the X server's latest - * state. The server already knows the new state of the outputs; that's - * why it sent us an event! - */ - screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */ -#if 0 - /* Enable this code to get a dialog showing the RANDR timestamps, for debugging purposes */ - { - GtkWidget *dialog; - XRRScreenChangeNotifyEvent *rr_event; - static int dialog_num; - - rr_event = (XRRScreenChangeNotifyEvent *) e; - - dialog = gtk_message_dialog_new (NULL, - 0, - GTK_MESSAGE_INFO, - GTK_BUTTONS_CLOSE, - "RRScreenChangeNotify timestamps (%d):\n" - "event change: %u\n" - "event config: %u\n" - "event serial: %lu\n" - "----------------------" - "screen change: %u\n" - "screen config: %u\n", - dialog_num++, - (guint32) rr_event->timestamp, - (guint32) rr_event->config_timestamp, - rr_event->serial, - (guint32) priv->info->resources->timestamp, - (guint32) priv->info->resources->configTimestamp); - g_signal_connect (dialog, "response", - G_CALLBACK (gtk_widget_destroy), NULL); - gtk_widget_show (dialog); - } -#endif - } -#if 0 - /* WHY THIS CODE IS DISABLED: - * - * Note that in gnome_rr_screen_new(), we only select for - * RRScreenChangeNotifyMask. We used to select for other values in - * RR*NotifyMask, but we weren't really doing anything useful with those - * events. We only care about "the screens changed in some way or another" - * for now. - * - * If we ever run into a situation that could benefit from processing more - * detailed events, we can enable this code again. - * - * Note that the X server sends RRScreenChangeNotify in conjunction with the - * more detailed events from RANDR 1.2 - see xserver/randr/randr.c:TellChanged(). - */ - else if (event_num == RRNotify) - { - /* Other RandR events */ - XRRNotifyEvent *event = (XRRNotifyEvent *)e; + screen_update (screen, REFRESH_FORCE_CALLBACK, NULL); +} - /* Here we can distinguish between RRNotify events supported - * since RandR 1.2 such as RRNotify_OutputProperty. For now, we - * don't have anything special to do for particular subevent types, so - * we leave this as an empty switch(). - */ - switch (event->subtype) - { - default: - break; - } +static void +name_owner_changed (GObject *object, + GParamSpec *pspec, + GnomeRRScreen *self) +{ + GError *error; + char *new_name_owner; - /* No need to reprobe hardware here */ - screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */ - } -#endif + new_name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object)); + if (new_name_owner == NULL) + return; + + error = NULL; + if (!screen_update (self, REFRESH_IGNORE_SERIAL | REFRESH_FORCE_CALLBACK, &error)) + g_warning ("Failed to refresh screen configuration after mutter was restarted: %s", + error->message); + + g_clear_error (&error); + g_free (new_name_owner); +} - /* Pass the event on to GTK+ */ - return GDK_FILTER_CONTINUE; +static void +power_save_mode_changed (GObject *object, + GParamSpec *pspec, + GnomeRRScreen *self) +{ + g_object_notify (G_OBJECT (self), "dpms-mode"); } static gboolean @@ -743,67 +694,133 @@ gnome_rr_screen_initable_init (GInitable *initable, GCancellable *canc, GError * { GnomeRRScreen *self = GNOME_RR_SCREEN (initable); GnomeRRScreenPrivate *priv = self->priv; - Display *dpy = GDK_SCREEN_XDISPLAY (self->priv->gdk_screen); - int event_base; - int ignore; + MetaDBusDisplayConfig *proxy; + + proxy = meta_dbus_display_config_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + "org.cinnamon.Muffin.DisplayConfig", + "/org/cinnamon/Muffin/DisplayConfig", + NULL, error); + if (!proxy) + return FALSE; + + priv->proxy = META_DBUS_DISPLAY_CONFIG (proxy); - priv->interface_settings = g_settings_new ("org.cinnamon.desktop.interface"); + priv->info = screen_info_new (self, error); + if (!priv->info) + return FALSE; - priv->connector_type_atom = XInternAtom (dpy, "ConnectorType", FALSE); + g_signal_connect_object (priv->proxy, "notify::g-name-owner", + G_CALLBACK (name_owner_changed), self, 0); + g_signal_connect_object (priv->proxy, "monitors-changed", + G_CALLBACK (screen_on_monitors_changed), self, 0); + g_signal_connect_object (priv->proxy, "notify::power-save-mode", + G_CALLBACK (power_save_mode_changed), self, 0); + return TRUE; +} - if (XRRQueryExtension (dpy, &event_base, &ignore)) - { - priv->randr_event_base = event_base; +static void +on_proxy_acquired (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = user_data; + GnomeRRScreen *self = g_task_get_source_object (task); + GnomeRRScreenPrivate *priv = self->priv; + MetaDBusDisplayConfig *proxy; + GError *error; - XRRQueryVersion (dpy, &priv->rr_major_version, &priv->rr_minor_version); - if (priv->rr_major_version < 1 || (priv->rr_major_version == 1 && priv->rr_minor_version < 2)) { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_RANDR_EXTENSION, - "RANDR extension is too old (must be at least 1.2)"); - return FALSE; - } + error = NULL; + proxy = meta_dbus_display_config_proxy_new_for_bus_finish (result, &error); + if (!proxy) + return g_task_return_error (task, error); - priv->info = screen_info_new (self, TRUE, error); + priv->proxy = META_DBUS_DISPLAY_CONFIG (proxy); - if (!priv->info) { - return FALSE; - } + priv->info = screen_info_new (self, &error); + if (!priv->info) + return g_task_return_error (task, error); - XRRSelectInput (priv->xdisplay, - priv->xroot, - RRScreenChangeNotifyMask); - gdk_x11_register_standard_event_type (gdk_screen_get_display (priv->gdk_screen), - event_base, - RRNotify + 1); - gdk_window_add_filter (priv->gdk_root, screen_on_event, self); + g_signal_connect_object (priv->proxy, "notify::g-name-owner", + G_CALLBACK (name_owner_changed), self, 0); + g_signal_connect_object (priv->proxy, "monitors-changed", + G_CALLBACK (screen_on_monitors_changed), self, 0); + g_signal_connect_object (priv->proxy, "notify::power-save-mode", + G_CALLBACK (power_save_mode_changed), self, 0); + g_task_return_boolean (task, TRUE); +} - return TRUE; - } - else - { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_RANDR_EXTENSION, - _("RANDR extension is not present")); +static void +on_name_appeared (GDBusConnection *connection, + const char *name, + const char *name_owner, + gpointer user_data) +{ + GTask *task = user_data; + GnomeRRScreen *self = g_task_get_source_object (task); + GnomeRRScreenPrivate *priv = self->priv; + + meta_dbus_display_config_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + "org.cinnamon.Muffin.DisplayConfig", + "/org/cinnamon/Muffin/DisplayConfig", + g_task_get_cancellable (task), + on_proxy_acquired, g_object_ref (task)); - return FALSE; - } + g_bus_unwatch_name (priv->init_name_watch_id); } -void +static void +gnome_rr_screen_async_initable_init_async (GAsyncInitable *initable, + int io_priority, + GCancellable *canc, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GnomeRRScreen *self = GNOME_RR_SCREEN (initable); + GnomeRRScreenPrivate *priv = self->priv; + GTask *task; + + task = g_task_new (self, canc, callback, user_data); + + priv->init_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.cinnamon.Muffin.DisplayConfig", + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_name_appeared, + NULL, + task, g_object_unref); +} + +static gboolean +gnome_rr_screen_async_initable_init_finish (GAsyncInitable *initable, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void gnome_rr_screen_initable_iface_init (GInitableIface *iface) { iface->init = gnome_rr_screen_initable_init; } +static void +gnome_rr_screen_async_initable_init (GAsyncInitableIface *iface) +{ + iface->init_async = gnome_rr_screen_async_initable_init_async; + iface->init_finish = gnome_rr_screen_async_initable_init_finish; +} + void gnome_rr_screen_finalize (GObject *gobject) { GnomeRRScreen *screen = GNOME_RR_SCREEN (gobject); - gdk_window_remove_filter (screen->priv->gdk_root, screen_on_event, screen); - if (screen->priv->info) screen_info_free (screen->priv->info); - g_clear_object (&screen->priv->interface_settings); + g_clear_object (&screen->priv->proxy); G_OBJECT_CLASS (gnome_rr_screen_parent_class)->finalize (gobject); } @@ -818,10 +835,9 @@ gnome_rr_screen_set_property (GObject *gobject, guint property_id, const GValue { case SCREEN_PROP_GDK_SCREEN: priv->gdk_screen = g_value_get_object (value); - priv->gdk_root = gdk_screen_get_root_window (priv->gdk_screen); - priv->xroot = gdk_x11_window_get_xid (priv->gdk_root); - priv->xdisplay = GDK_SCREEN_XDISPLAY (priv->gdk_screen); - priv->xscreen = gdk_x11_screen_get_xscreen (priv->gdk_screen); + return; + case SCREEN_PROP_DPMS_MODE: + gnome_rr_screen_set_dpms_mode (self, g_value_get_enum (value), NULL); return; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property); @@ -840,6 +856,14 @@ gnome_rr_screen_get_property (GObject *gobject, guint property_id, GValue *value case SCREEN_PROP_GDK_SCREEN: g_value_set_object (value, priv->gdk_screen); return; + case SCREEN_PROP_DPMS_MODE: { + GnomeRRDpmsMode mode; + if (gnome_rr_screen_get_dpms_mode (self, &mode, NULL)) + g_value_set_enum (value, mode); + else + g_value_set_enum (value, GNOME_RR_DPMS_UNKNOWN); + } + return; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property); return; @@ -851,6 +875,9 @@ gnome_rr_screen_class_init (GnomeRRScreenClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + gobject_class->set_property = gnome_rr_screen_set_property; gobject_class->get_property = gnome_rr_screen_get_property; gobject_class->finalize = gnome_rr_screen_finalize; @@ -868,6 +895,19 @@ gnome_rr_screen_class_init (GnomeRRScreenClass *klass) G_PARAM_STATIC_STRINGS) ); + g_object_class_install_property( + gobject_class, + SCREEN_PROP_DPMS_MODE, + g_param_spec_enum ( + "dpms-mode", + "DPMS Mode", + "The DPMS mode for this GnomeRRScreen", + GNOME_TYPE_RR_DPMS_MODE, + GNOME_RR_DPMS_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS) + ); + screen_signals[SCREEN_CHANGED] = g_signal_new("changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, @@ -939,17 +979,7 @@ gnome_rr_screen_class_init (GnomeRRScreenClass *klass) void gnome_rr_screen_init (GnomeRRScreen *self) { - GnomeRRScreenPrivate *priv = gnome_rr_screen_get_instance_private (self); - self->priv = priv; - - priv->gdk_screen = NULL; - priv->gdk_root = NULL; - priv->xdisplay = NULL; - priv->xroot = None; - priv->xscreen = NULL; - priv->info = NULL; - priv->rr_major_version = 0; - priv->rr_minor_version = 0; + self->priv = gnome_rr_screen_get_instance_private (self); } /* Weak reference callback set in gnome_rr_screen_new(); we remove the GObject data from the GdkScreen. */ @@ -985,8 +1015,6 @@ gnome_rr_screen_new (GdkScreen *screen, if (rr_screen) g_object_ref (rr_screen); else { - _gnome_desktop_init_i18n (); - rr_screen = g_initable_new (GNOME_TYPE_RR_SCREEN, NULL, error, "gdk-screen", screen, NULL); if (rr_screen) { g_object_set_data (G_OBJECT (screen), "GnomeRRScreen", rr_screen); @@ -998,20 +1026,29 @@ gnome_rr_screen_new (GdkScreen *screen, } void -gnome_rr_screen_set_size (GnomeRRScreen *screen, - int width, - int height, - int mm_width, - int mm_height) +gnome_rr_screen_new_async (GdkScreen *screen, + GAsyncReadyCallback callback, + gpointer user_data) { - g_return_if_fail (GNOME_IS_RR_SCREEN (screen)); + g_return_if_fail (GDK_IS_SCREEN (screen)); + + g_async_initable_new_async (GNOME_TYPE_RR_SCREEN, G_PRIORITY_DEFAULT, + NULL, callback, user_data, + "gdk-screen", screen, NULL); +} - g_debug ("Setting screen size: %d x %d, %dmm x %dmm", width, height, mm_width, mm_height); +GnomeRRScreen * +gnome_rr_screen_new_finish (GAsyncResult *result, + GError **error) +{ + GObject *source_object; + GnomeRRScreen *screen; - gdk_error_trap_push (); - XRRSetScreenSize (screen->priv->xdisplay, screen->priv->xroot, - width, height, mm_width, mm_height); - gdk_error_trap_pop_ignored (); + source_object = g_async_result_get_source_object (result); + screen = GNOME_RR_SCREEN (g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error)); + + g_object_unref (source_object); + return screen; } /** @@ -1050,83 +1087,6 @@ gnome_rr_screen_get_ranges (GnomeRRScreen *screen, *max_height = priv->info->max_height; } -/** - * gnome_rr_screen_get_timestamps: - * @screen: a #GnomeRRScreen - * @change_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last changed - * @config_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last obtained - * - * Queries the two timestamps that the X RANDR extension maintains. The X - * server will prevent change requests for stale configurations, those whose - * timestamp is not equal to that of the latest request for configuration. The - * X server will also prevent change requests that have an older timestamp to - * the latest change request. - */ -void -gnome_rr_screen_get_timestamps (GnomeRRScreen *screen, - guint32 *change_timestamp_ret, - guint32 *config_timestamp_ret) -{ - GnomeRRScreenPrivate *priv; - - g_return_if_fail (GNOME_IS_RR_SCREEN (screen)); - - priv = screen->priv; - - if (change_timestamp_ret) - *change_timestamp_ret = priv->info->resources->timestamp; - - if (config_timestamp_ret) - *config_timestamp_ret = priv->info->resources->configTimestamp; -} - -static gboolean -force_timestamp_update (GnomeRRScreen *screen) -{ - GnomeRRScreenPrivate *priv = screen->priv; - GnomeRRCrtc *crtc; - XRRCrtcInfo *current_info; - Status status; - gboolean timestamp_updated; - - timestamp_updated = FALSE; - - crtc = priv->info->crtcs[0]; - - if (crtc == NULL) - goto out; - - current_info = XRRGetCrtcInfo (priv->xdisplay, - priv->info->resources, - crtc->id); - - if (current_info == NULL) - goto out; - - gdk_error_trap_push (); - status = XRRSetCrtcConfig (priv->xdisplay, - priv->info->resources, - crtc->id, - current_info->timestamp, - current_info->x, - current_info->y, - current_info->mode, - current_info->rotation, - current_info->outputs, - current_info->noutput); - - XRRFreeCrtcInfo (current_info); - - gdk_flush (); - if (gdk_error_trap_pop ()) - goto out; - - if (status == RRSetConfigSuccess) - timestamp_updated = TRUE; -out: - return timestamp_updated; -} - /** * gnome_rr_screen_refresh: * @screen: a #GnomeRRScreen @@ -1144,99 +1104,50 @@ gboolean gnome_rr_screen_refresh (GnomeRRScreen *screen, GError **error) { - gboolean refreshed; - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - gdk_x11_display_grab (gdk_screen_get_display (screen->priv->gdk_screen)); - - refreshed = screen_update (screen, FALSE, TRUE, error); - force_timestamp_update (screen); /* this is to keep other clients from thinking that the X server re-detected things by itself - bgo#621046 */ - - gdk_x11_display_ungrab (gdk_screen_get_display (screen->priv->gdk_screen)); - - return refreshed; + return screen_update (screen, REFRESH_NONE, error); } /** * gnome_rr_screen_get_dpms_mode: + * @mode: (out): The current #GnomeRRDpmsMode of this screen **/ gboolean -gnome_rr_screen_get_dpms_mode (GnomeRRScreen *screen, - GnomeRRDpmsMode *mode, - GError **error) +gnome_rr_screen_get_dpms_mode (GnomeRRScreen *screen, + GnomeRRDpmsMode *mode, + GError **error) { - BOOL enabled = FALSE; - CARD16 state; - gboolean ret = FALSE; + MetaPowerSave power_save; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_return_val_if_fail (mode != NULL, FALSE); - if (!screen->priv->dpms_capable) { + power_save = meta_dbus_display_config_get_power_save_mode (screen->priv->proxy); + switch (power_save) { + case META_POWER_SAVE_UNKNOWN: g_set_error_literal (error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_DPMS_EXTENSION, "Display is not DPMS capable"); - goto out; - } - - if (!DPMSInfo (screen->priv->xdisplay, - &state, - &enabled)) { - g_set_error_literal (error, - GNOME_RR_ERROR, - GNOME_RR_ERROR_UNKNOWN, - "Unable to get DPMS state"); - goto out; - } - - /* DPMS not enabled is a valid mode */ - if (!enabled) { - *mode = GNOME_RR_DPMS_DISABLED; - ret = TRUE; - goto out; - } - - switch (state) { - case DPMSModeOn: + return FALSE; + case META_POWER_SAVE_ON: *mode = GNOME_RR_DPMS_ON; break; - case DPMSModeStandby: + case META_POWER_SAVE_STANDBY: *mode = GNOME_RR_DPMS_STANDBY; break; - case DPMSModeSuspend: + case META_POWER_SAVE_SUSPEND: *mode = GNOME_RR_DPMS_SUSPEND; break; - case DPMSModeOff: + case META_POWER_SAVE_OFF: *mode = GNOME_RR_DPMS_OFF; break; default: g_assert_not_reached (); break; } - ret = TRUE; -out: - return ret; -} -/** - * gnome_rr_screen_clear_dpms_timeouts: - **/ -static gboolean -gnome_rr_screen_clear_dpms_timeouts (GnomeRRScreen *screen, - GError **error) -{ - gdk_error_trap_push (); - /* DPMSSetTimeouts() return value is often a lie, so ignore it */ - DPMSSetTimeouts (screen->priv->xdisplay, 0, 0, 0); - if (gdk_error_trap_pop ()) { - g_set_error_literal (error, - GNOME_RR_ERROR, - GNOME_RR_ERROR_UNKNOWN, - "Could not set DPMS timeouts"); - return FALSE; - } return TRUE; } @@ -1246,60 +1157,38 @@ gnome_rr_screen_clear_dpms_timeouts (GnomeRRScreen *screen, * This method also disables the DPMS timeouts. **/ gboolean -gnome_rr_screen_set_dpms_mode (GnomeRRScreen *screen, - GnomeRRDpmsMode mode, - GError **error) +gnome_rr_screen_set_dpms_mode (GnomeRRScreen *screen, + GnomeRRDpmsMode mode, + GError **error) { - CARD16 state = 0; - gboolean ret; - GnomeRRDpmsMode current_mode; + MetaPowerSave power_save; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - /* set, if the new mode is different */ - ret = gnome_rr_screen_get_dpms_mode (screen, ¤t_mode, error); - if (!ret) - goto out; - if (current_mode == mode) { - ret = gnome_rr_screen_clear_dpms_timeouts (screen, error); - goto out; - } - switch (mode) { + case GNOME_RR_DPMS_UNKNOWN: + power_save = META_POWER_SAVE_UNKNOWN; + break; case GNOME_RR_DPMS_ON: - state = DPMSModeOn; + power_save = META_POWER_SAVE_ON; break; case GNOME_RR_DPMS_STANDBY: - state = DPMSModeStandby; + power_save = META_POWER_SAVE_STANDBY; break; case GNOME_RR_DPMS_SUSPEND: - state = DPMSModeSuspend; + power_save = META_POWER_SAVE_SUSPEND; break; case GNOME_RR_DPMS_OFF: - state = DPMSModeOff; + power_save = META_POWER_SAVE_OFF; break; default: g_assert_not_reached (); break; } - gdk_error_trap_push (); - /* DPMSForceLevel() return value is often a lie, so ignore it */ - DPMSForceLevel (screen->priv->xdisplay, state); - if (gdk_error_trap_pop ()) { - ret = FALSE; - g_set_error_literal (error, - GNOME_RR_ERROR, - GNOME_RR_ERROR_UNKNOWN, - "Could not change DPMS mode"); - goto out; - } + meta_dbus_display_config_set_power_save_mode (screen->priv->proxy, power_save); - ret = gnome_rr_screen_clear_dpms_timeouts (screen, error); - if (!ret) - goto out; -out: - return ret; + return TRUE; } /** @@ -1420,7 +1309,7 @@ gnome_rr_screen_get_output_by_id (GnomeRRScreen *screen, /* GnomeRROutput */ static GnomeRROutput * -output_new (ScreenInfo *info, RROutput id) +output_new (ScreenInfo *info, guint id) { GnomeRROutput *output = g_slice_new0 (GnomeRROutput); @@ -1430,230 +1319,121 @@ output_new (ScreenInfo *info, RROutput id) return output; } -static guint8 * -get_property (Display *dpy, - RROutput output, - Atom atom, - gsize *len) +static void +append_output_array (GnomeRROutput ***array, GnomeRROutput *output) { - unsigned char *prop; - int actual_format; - unsigned long nitems, bytes_after; - Atom actual_type; - guint8 *result; - - XRRGetOutputProperty (dpy, output, atom, - 0, 100, False, False, - AnyPropertyType, - &actual_type, &actual_format, - &nitems, &bytes_after, &prop); - - if (actual_type == XA_INTEGER && actual_format == 8) - { - result = g_memdup (prop, nitems); - if (len) - *len = nitems; - } - else - { - result = NULL; - } - - XFree (prop); - - return result; -} + unsigned i; -static guint8 * -read_edid_data (GnomeRROutput *output, gsize *len) -{ - Atom edid_atom; - guint8 *result; + for (i = 0; (*array)[i]; i++); - edid_atom = XInternAtom (DISPLAY (output), "EDID", FALSE); - result = get_property (DISPLAY (output), - output->id, edid_atom, len); + *array = g_renew (GnomeRROutput *, *array, i + 2); - if (!result) - { - edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE); - result = get_property (DISPLAY (output), - output->id, edid_atom, len); - } - - if (!result) - { - edid_atom = XInternAtom (DISPLAY (output), "XFree86_DDC_EDID1_RAWDATA", FALSE); - result = get_property (DISPLAY (output), - output->id, edid_atom, len); - } - - if (result) - { - if (*len % 128 == 0) - return result; - else - g_free (result); - } - - return NULL; -} - -static char * -get_connector_type_string (GnomeRROutput *output) -{ - char *result; - unsigned char *prop; - int actual_format; - unsigned long nitems, bytes_after; - Atom actual_type; - Atom connector_type; - char *connector_type_str; - - result = NULL; - - if (XRRGetOutputProperty (DISPLAY (output), output->id, output->info->screen->priv->connector_type_atom, - 0, 100, False, False, - AnyPropertyType, - &actual_type, &actual_format, - &nitems, &bytes_after, &prop) != Success) - return NULL; - - if (!(actual_type == XA_ATOM && actual_format == 32 && nitems == 1)) - goto out; - - connector_type = *((Atom *) prop); - - connector_type_str = XGetAtomName (DISPLAY (output), connector_type); - if (connector_type_str) { - result = g_strdup (connector_type_str); /* so the caller can g_free() it */ - XFree (connector_type_str); - } - -out: - - XFree (prop); - - return result; + (*array)[i] = output; + (*array)[i + 1] = NULL; } static void -update_brightness_limits (GnomeRROutput *output) -{ - gint rc; - Atom atom; - XRRPropertyInfo *info; - - gdk_error_trap_push (); - atom = XInternAtom (DISPLAY (output), "BACKLIGHT", FALSE); - info = XRRQueryOutputProperty (DISPLAY (output), output->id, atom); - rc = gdk_error_trap_pop (); - if (rc != Success) - { - if (rc != BadName) - g_warning ("could not get output property for %s, rc: %i", - output->name, rc); - goto out; - } - if (info == NULL) - { - g_warning ("could not get output property for %s", - output->name); - goto out; - } - if (!info->range || info->num_values != 2) - { - g_debug ("backlight %s was not range", output->name); - goto out; - } - output->backlight_min = info->values[0]; - output->backlight_max = info->values[1]; -out: - if (info != NULL) - { - XFree (info); - } -} - -static gboolean -output_initialize (GnomeRROutput *output, XRRScreenResources *res, GError **error) +output_initialize (GnomeRROutput *output, GVariant *info) { - XRROutputInfo *info = XRRGetOutputInfo (DISPLAY (output), - res, - output->id); GPtrArray *a; - int i; - -#if 0 - g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp); -#endif - - if (!info || !output->info) - { - /* FIXME: see the comment in crtc_initialize() */ - /* Translators: here, an "output" is a video output */ - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR, - _("could not get information about output %d"), - (int) output->id); - return FALSE; - } + GVariantIter *crtcs, *clones, *modes; + GVariant *properties, *edid, *tile; + gint32 current_crtc_id; + guint32 id; - output->name = g_strdup (info->name); /* FIXME: what is nameLen used for? */ - output->current_crtc = crtc_by_id (output->info, info->crtc); - output->width_mm = info->mm_width; - output->height_mm = info->mm_height; - output->connected = (info->connection == RR_Connected); - output->connector_type = get_connector_type_string (output); + g_variant_get (info, META_OUTPUT_STRUCT, + &output->id, &output->winsys_id, + ¤t_crtc_id, &crtcs, + &output->name, + &modes, &clones, &properties); /* Possible crtcs */ a = g_ptr_array_new (); - - for (i = 0; i < info->ncrtc; ++i) + while (g_variant_iter_loop (crtcs, "u", &id)) { - GnomeRRCrtc *crtc = crtc_by_id (output->info, info->crtcs[i]); + GnomeRRCrtc *crtc = crtc_by_id (output->info, id); + + if (!crtc) + continue; + + g_ptr_array_add (a, crtc); - if (crtc) - g_ptr_array_add (a, crtc); + if (current_crtc_id != -1 && crtc->id == (guint32) current_crtc_id) + { + output->current_crtc = crtc; + append_output_array (&crtc->current_outputs, output); + } + + append_output_array (&crtc->possible_outputs, output); } g_ptr_array_add (a, NULL); output->possible_crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE); + g_variant_iter_free (crtcs); /* Clones */ a = g_ptr_array_new (); - for (i = 0; i < info->nclone; ++i) + while (g_variant_iter_loop (clones, "u", &id)) { - GnomeRROutput *gnome_rr_output = gnome_rr_output_by_id (output->info, info->clones[i]); - - if (gnome_rr_output) - g_ptr_array_add (a, gnome_rr_output); + GnomeRROutput *gnome_rr_output = gnome_rr_output_by_id (output->info, id); + + if (gnome_rr_output) + g_ptr_array_add (a, gnome_rr_output); } g_ptr_array_add (a, NULL); output->clones = (GnomeRROutput **)g_ptr_array_free (a, FALSE); - + g_variant_iter_free (clones); + /* Modes */ a = g_ptr_array_new (); - for (i = 0; i < info->nmode; ++i) + while (g_variant_iter_loop (modes, "u", &id)) { - GnomeRRMode *mode = mode_by_id (output->info, info->modes[i]); - - if (mode) - g_ptr_array_add (a, mode); + GnomeRRMode *mode = mode_by_id (output->info, id); + + if (mode) + g_ptr_array_add (a, mode); } g_ptr_array_add (a, NULL); output->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE); + g_variant_iter_free (modes); + + g_variant_lookup (properties, "vendor", "s", &output->vendor); + g_variant_lookup (properties, "product", "s", &output->product); + g_variant_lookup (properties, "serial", "s", &output->serial); + g_variant_lookup (properties, "width-mm", "i", &output->width_mm); + g_variant_lookup (properties, "height-mm", "i", &output->height_mm); + g_variant_lookup (properties, "display-name", "s", &output->display_name); + g_variant_lookup (properties, "connector-type", "s", &output->connector_type); + g_variant_lookup (properties, "backlight", "i", &output->backlight); + g_variant_lookup (properties, "min-backlight-step", "i", &output->min_backlight_step); + g_variant_lookup (properties, "primary", "b", &output->is_primary); + g_variant_lookup (properties, "presentation", "b", &output->is_presentation); + g_variant_lookup (properties, "underscanning", "b", &output->is_underscanning); + g_variant_lookup (properties, "supports-underscanning", "b", &output->supports_underscanning); + + if ((edid = g_variant_lookup_value (properties, "edid", G_VARIANT_TYPE ("ay")))) + { + output->edid = g_variant_get_data_as_bytes (edid); + g_variant_unref (edid); + } + else + g_variant_lookup (properties, "edid-file", "s", &output->edid_file); + + if ((tile = g_variant_lookup_value (properties, "tile", G_VARIANT_TYPE ("(uuuuuuuu)")))) + { + g_variant_get (tile, "(uuuuuuuu)", + &output->tile_info.group_id, &output->tile_info.flags, + &output->tile_info.max_horiz_tiles, &output->tile_info.max_vert_tiles, + &output->tile_info.loc_horiz, &output->tile_info.loc_vert, + &output->tile_info.width, &output->tile_info.height); + g_variant_unref (tile); + } + else + memset(&output->tile_info, 0, sizeof(output->tile_info)); - output->n_preferred = info->npreferred; - - /* Edid data */ - output->edid_data = read_edid_data (output, &output->edid_size); - - /* brightness data */ - if (output->connected) - update_brightness_limits (output); - - XRRFreeOutputInfo (info); + if (output->is_primary) + output->info->primary = output; - return TRUE; + g_variant_unref (properties); } static GnomeRROutput* @@ -1668,14 +1448,19 @@ output_copy (const GnomeRROutput *from) output->id = from->id; output->info = from->info; output->name = g_strdup (from->name); - output->current_crtc = from->current_crtc; - output->width_mm = from->width_mm; - output->height_mm = from->height_mm; - output->connected = from->connected; - output->n_preferred = from->n_preferred; + output->display_name = g_strdup (from->display_name); output->connector_type = g_strdup (from->connector_type); - output->backlight_min = -1; - output->backlight_max = -1; + output->vendor = g_strdup (from->vendor); + output->product = g_strdup (from->product); + output->serial = g_strdup (from->serial); + output->current_crtc = from->current_crtc; + output->backlight = from->backlight; + if (from->edid) + output->edid = g_bytes_ref (from->edid); + output->edid_file = g_strdup (from->edid_file); + + output->is_primary = from->is_primary; + output->is_presentation = from->is_presentation; array = g_ptr_array_new (); for (p_crtc = from->possible_crtcs; *p_crtc != NULL; p_crtc++) @@ -1698,9 +1483,6 @@ output_copy (const GnomeRROutput *from) } output->modes = (GnomeRRMode**) g_ptr_array_free (array, FALSE); - output->edid_size = from->edid_size; - output->edid_data = g_memdup (from->edid_data, from->edid_size); - return output; } @@ -1710,9 +1492,15 @@ output_free (GnomeRROutput *output) g_free (output->clones); g_free (output->modes); g_free (output->possible_crtcs); - g_free (output->edid_data); g_free (output->name); + g_free (output->vendor); + g_free (output->product); + g_free (output->serial); + g_free (output->display_name); g_free (output->connector_type); + g_free (output->edid_file); + if (output->edid) + g_bytes_unref (output->edid); g_slice_free (GnomeRROutput, output); } @@ -1725,64 +1513,76 @@ gnome_rr_output_get_id (GnomeRROutput *output) } const guint8 * -gnome_rr_output_get_edid_data (GnomeRROutput *output, gsize *size) +gnome_rr_output_get_edid_data (GnomeRROutput *output, + gsize *size) { - g_return_val_if_fail (output != NULL, NULL); - if (size) - *size = output->edid_size; - return output->edid_data; -} + if (output->edid) + return g_bytes_get_data (output->edid, size); -gboolean -gnome_rr_output_get_ids_from_edid (GnomeRROutput *output, - char **vendor, - int *product, - int *serial) -{ - MonitorInfo *info; + if (output->edid_file) + { + GMappedFile *mmap; - g_return_val_if_fail (output != NULL, FALSE); + mmap = g_mapped_file_new (output->edid_file, FALSE, NULL); - if (!output->edid_data) - return FALSE; - info = decode_edid (output->edid_data); - if (!info) - return FALSE; - if (vendor) - *vendor = g_memdup (info->manufacturer_code, 4); - if (product) - *product = info->product_code; - if (serial) - *serial = info->serial_number; + if (mmap) + { + output->edid = g_mapped_file_get_bytes (mmap); - g_free (info); + g_mapped_file_unref (mmap); - return TRUE; + return g_bytes_get_data (output->edid, size); + } + } + return NULL; } /** - * gnome_rr_output_get_backlight_min: - * - * Returns: The minimum backlight value, or -1 if not supported + * gnome_rr_output_get_ids_from_edid: + * @output: a #GnomeRROutput + * @vendor: (out) (allow-none): + * @product: (out) (allow-none): + * @serial: (out) (allow-none): */ -gint -gnome_rr_output_get_backlight_min (GnomeRROutput *output) +void +gnome_rr_output_get_ids_from_edid (GnomeRROutput *output, + char **vendor, + char **product, + char **serial) { - g_return_val_if_fail (output != NULL, -1); - return output->backlight_min; + g_return_if_fail (output != NULL); + + *vendor = g_strdup (output->vendor); + *product = g_strdup (output->product); + *serial = g_strdup (output->serial); } /** - * gnome_rr_output_get_backlight_max: - * - * Returns: The maximum backlight value, or -1 if not supported + * gnome_rr_output_get_physical_size: + * @output: a #GnomeRROutput + * @width_mm: (out) (allow-none): + * @height_mm: (out) (allow-none): */ -gint -gnome_rr_output_get_backlight_max (GnomeRROutput *output) +void +gnome_rr_output_get_physical_size (GnomeRROutput *output, + int *width_mm, + int *height_mm) { - g_return_val_if_fail (output != NULL, -1); - return output->backlight_max; + g_return_if_fail (output != NULL); + + if (width_mm) + *width_mm = output->width_mm; + if (height_mm) + *height_mm = output->height_mm; +} + +const char * +gnome_rr_output_get_display_name (GnomeRROutput *output) +{ + g_return_val_if_fail (output != NULL, NULL); + + return output->display_name; } /** @@ -1790,108 +1590,43 @@ gnome_rr_output_get_backlight_max (GnomeRROutput *output) * * Returns: The currently set backlight brightness */ -gint -gnome_rr_output_get_backlight (GnomeRROutput *output, GError **error) -{ - guint now = -1; - unsigned long nitems; - unsigned long bytes_after; - guint *prop; - Atom atom; - Atom actual_type; - int actual_format; - gint retval; - +int +gnome_rr_output_get_backlight (GnomeRROutput *output) +{ g_return_val_if_fail (output != NULL, -1); - gdk_error_trap_push (); - atom = XInternAtom (DISPLAY (output), "BACKLIGHT", FALSE); - retval = XRRGetOutputProperty (DISPLAY (output), output->id, atom, - 0, 4, False, False, None, - &actual_type, &actual_format, - &nitems, &bytes_after, ((unsigned char **)&prop)); - gdk_flush (); - if (gdk_error_trap_pop ()) - { - g_set_error_literal (error, - GNOME_RR_ERROR, - GNOME_RR_ERROR_UNKNOWN, - "unhandled X error while getting the range of backlight values"); - goto out; - } + return output->backlight; +} - if (retval != Success) { - g_set_error_literal (error, - GNOME_RR_ERROR, - GNOME_RR_ERROR_RANDR_ERROR, - "could not get the range of backlight values"); - goto out; - } - if (actual_type == XA_INTEGER && - nitems == 1 && - actual_format == 32) - { - memcpy (&now, prop, sizeof (guint)); - } - else - { - g_set_error (error, - GNOME_RR_ERROR, - GNOME_RR_ERROR_RANDR_ERROR, - "failed to get correct property type, got %lu,%i", - nitems, actual_format); - } -out: - XFree (prop); - return now; +/** + * gnome_rr_output_get_min_backlight_step: + * + * Returns: The minimum backlight step available in percent + */ +int +gnome_rr_output_get_min_backlight_step (GnomeRROutput *output) +{ + g_return_val_if_fail (output != NULL, -1); + + return output->min_backlight_step; } /** * gnome_rr_output_set_backlight: - * @value: the absolute value which is min >= this <= max + * @value: the absolute value which is 0 >= this <= 100 * * Returns: %TRUE for success */ gboolean gnome_rr_output_set_backlight (GnomeRROutput *output, gint value, GError **error) { - gboolean ret = FALSE; - Atom atom; - g_return_val_if_fail (output != NULL, FALSE); - /* check this is sane */ - if (value < output->backlight_min || - value > output->backlight_max) - { - g_set_error (error, - GNOME_RR_ERROR, - GNOME_RR_ERROR_BOUNDS_ERROR, - "out of brightness range: %i, has to be %i -> %i", - value, - output->backlight_max, output->backlight_min); - goto out; - } - - /* don't abort on error */ - gdk_error_trap_push (); - atom = XInternAtom (DISPLAY (output), "BACKLIGHT", FALSE); - XRRChangeOutputProperty (DISPLAY (output), output->id, atom, - XA_INTEGER, 32, PropModeReplace, - (unsigned char *) &value, 1); - if (gdk_error_trap_pop ()) - { - g_set_error_literal (error, - GNOME_RR_ERROR, - GNOME_RR_ERROR_UNKNOWN, - "unhandled X error while setting the backlight values"); - goto out; - } - - /* we assume this succeeded as there's no return value */ - ret = TRUE; -out: - return ret; + return meta_dbus_display_config_call_change_backlight_sync (output->info->screen->priv->proxy, + output->info->serial, + output->id, value, + &output->backlight, + NULL, error); } /** @@ -1919,6 +1654,11 @@ gnome_rr_screen_get_output_by_name (GnomeRRScreen *screen, return NULL; } +/** + * gnome_rr_output_get_crtc: + * @output: a #GnomeRROutput + * Returns: (transfer none): + */ GnomeRRCrtc * gnome_rr_output_get_crtc (GnomeRROutput *output) { @@ -1927,66 +1667,88 @@ gnome_rr_output_get_crtc (GnomeRROutput *output) return output->current_crtc; } -/* Returns NULL if the ConnectorType property is not available */ -const char * -gnome_rr_output_get_connector_type (GnomeRROutput *output) +/** + * gnome_rr_output_get_possible_crtcs: + * @output: a #GnomeRROutput + * Returns: (array zero-terminated=1) (transfer none): + */ +GnomeRRCrtc ** +gnome_rr_output_get_possible_crtcs (GnomeRROutput *output) { g_return_val_if_fail (output != NULL, NULL); - return output->connector_type; + return output->possible_crtcs; } gboolean -_gnome_rr_output_name_is_laptop (const char *name) +_gnome_rr_output_connector_type_is_builtin_display (const char *connector_type) { - if (!name) + if (!connector_type) return FALSE; - if (strstr (name, "lvds") || /* Most drivers use an "LVDS" prefix... */ - strstr (name, "LVDS") || - strstr (name, "Lvds") || - strstr (name, "LCD") || /* ... but fglrx uses "LCD" in some versions. Shoot me now, kthxbye. */ - strstr (name, "eDP") || /* eDP is for internal laptop panel connections */ - strstr (name, "default")) /* Finally, NVidia and all others that don't bother to do RANDR properly */ + if (strcmp (connector_type, "LVDS") == 0 || + strcmp (connector_type, "eDP") == 0 || + strcmp (connector_type, "DSI") == 0) return TRUE; return FALSE; } gboolean -gnome_rr_output_is_laptop (GnomeRROutput *output) +gnome_rr_output_is_builtin_display (GnomeRROutput *output) { g_return_val_if_fail (output != NULL, FALSE); - if (!output->connected) - return FALSE; - - /* The ConnectorType property is present in RANDR 1.3 and greater */ - if (g_strcmp0 (output->connector_type, GNOME_RR_CONNECTOR_TYPE_PANEL) == 0) - return TRUE; - - /* Older versions of RANDR - this is a best guess, as @#$% RANDR doesn't have standard output names, - * so drivers can use whatever they like. - */ - if (_gnome_rr_output_name_is_laptop (output->name)) - return TRUE; - - return FALSE; + return _gnome_rr_output_connector_type_is_builtin_display (output->connector_type); } +/** + * gnome_rr_output_get_current_mode: + * @output: a #GnomeRROutput + * Returns: (transfer none): the current mode of this output + */ GnomeRRMode * gnome_rr_output_get_current_mode (GnomeRROutput *output) { GnomeRRCrtc *crtc; - + GnomeRRMode *mode; g_return_val_if_fail (output != NULL, NULL); if ((crtc = gnome_rr_output_get_crtc (output))) + { + int total_w, total_h, tile_w, tile_h; + mode = gnome_rr_crtc_get_current_mode (crtc); + + if (_gnome_rr_output_get_tiled_display_size (output, &tile_w, &tile_h, &total_w, &total_h)) + { + if (mode->width == tile_w && + mode->height == tile_h) { + if (output->modes[0]->tiled) + return output->modes[0]; + } + } return gnome_rr_crtc_get_current_mode (crtc); - + } return NULL; } +/** + * gnome_rr_output_is_connected: + * @output: a #GnomeRROutput + * Returns: If the output is connected + */ +gboolean +gnome_rr_output_is_connected (GnomeRROutput *output) +{ + return gnome_rr_output_get_current_mode (output) != NULL; +} + +/** + * gnome_rr_output_get_position: + * @output: a #GnomeRROutput + * @x: (out) (allow-none): + * @y: (out) (allow-none): + */ void gnome_rr_output_get_position (GnomeRROutput *output, int *x, @@ -2007,30 +1769,23 @@ gnome_rr_output_get_name (GnomeRROutput *output) return output->name; } -int -gnome_rr_output_get_width_mm (GnomeRROutput *output) -{ - g_assert (output != NULL); - return output->width_mm; -} - -int -gnome_rr_output_get_height_mm (GnomeRROutput *output) -{ - g_assert (output != NULL); - return output->height_mm; -} - +/** + * gnome_rr_output_get_preferred_mode: + * @output: a #GnomeRROutput + * Returns: (transfer none): + */ GnomeRRMode * gnome_rr_output_get_preferred_mode (GnomeRROutput *output) { g_return_val_if_fail (output != NULL, NULL); - if (output->n_preferred) - return output->modes[0]; - - return NULL; + return output->modes[0]; } +/** + * gnome_rr_output_list_modes: + * @output: a #GnomeRROutput + * Returns: (array zero-terminated=1) (transfer none): + */ GnomeRRMode ** gnome_rr_output_list_modes (GnomeRROutput *output) { @@ -2038,13 +1793,6 @@ gnome_rr_output_list_modes (GnomeRROutput *output) return output->modes; } -gboolean -gnome_rr_output_is_connected (GnomeRROutput *output) -{ - g_return_val_if_fail (output != NULL, FALSE); - return output->connected; -} - gboolean gnome_rr_output_supports_mode (GnomeRROutput *output, GnomeRRMode *mode) @@ -2084,210 +1832,33 @@ gnome_rr_output_can_clone (GnomeRROutput *output, gboolean gnome_rr_output_get_is_primary (GnomeRROutput *output) { - return output->info->primary == output->id; -} - -void -gnome_rr_screen_set_primary_output (GnomeRRScreen *screen, - GnomeRROutput *output) -{ - GnomeRRScreenPrivate *priv; - RROutput id; - - g_return_if_fail (GNOME_IS_RR_SCREEN (screen)); - - priv = screen->priv; - - if (output) - id = output->id; - else - id = None; - - if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv)) - XRRSetOutputPrimary (priv->xdisplay, priv->xroot, id); + return output->is_primary; } /* GnomeRRCrtc */ -typedef struct -{ - Rotation xrot; - GnomeRRRotation rot; -} RotationMap; - -static const RotationMap rotation_map[] = -{ - { RR_Rotate_0, GNOME_RR_ROTATION_0 }, - { RR_Rotate_90, GNOME_RR_ROTATION_90 }, - { RR_Rotate_180, GNOME_RR_ROTATION_180 }, - { RR_Rotate_270, GNOME_RR_ROTATION_270 }, - { RR_Reflect_X, GNOME_RR_REFLECT_X }, - { RR_Reflect_Y, GNOME_RR_REFLECT_Y }, +static const GnomeRRRotation rotation_map[] = +{ + GNOME_RR_ROTATION_0, + GNOME_RR_ROTATION_90, + GNOME_RR_ROTATION_180, + GNOME_RR_ROTATION_270, + GNOME_RR_REFLECT_X | GNOME_RR_ROTATION_0, + GNOME_RR_REFLECT_X | GNOME_RR_ROTATION_90, + GNOME_RR_REFLECT_X | GNOME_RR_ROTATION_180, + GNOME_RR_REFLECT_X | GNOME_RR_ROTATION_270, }; static GnomeRRRotation -gnome_rr_rotation_from_xrotation (Rotation r) -{ - int i; - GnomeRRRotation result = 0; - - for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i) - { - if (r & rotation_map[i].xrot) - result |= rotation_map[i].rot; - } - - return result; -} - -static Rotation -xrotation_from_rotation (GnomeRRRotation r) -{ - int i; - Rotation result = 0; - - for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i) - { - if (r & rotation_map[i].rot) - result |= rotation_map[i].xrot; - } - - return result; -} - -static void -set_crtc_scale (GnomeRRCrtc *crtc, - GnomeRRMode *mode, - float scale, - guint global_scale) -{ - gchar *filter; - float real_scale; - int looks_like_w, looks_like_h; - - real_scale = global_scale / scale; - - looks_like_w = 0; - looks_like_h = 0; - - if (mode != NULL) - { - looks_like_w = gnome_rr_mode_get_width (mode) * real_scale / global_scale; - looks_like_h = gnome_rr_mode_get_height (mode) * real_scale / global_scale; - } - - // Fractional scales use bilinear, doubling/halving can use nearest for better quality. - if (real_scale == 0.5 || real_scale == 1.0) - { - filter = g_strdup ("nearest"); - } - else - { - filter = g_strdup ("bilinear"); - } - - g_debug ("\n\n(xid: %lu) Transforming based on:\n" - "global ui scale: %d\n" - "requested logical scale: %.2f\n" - "requested logical size: %dx%d\n" - "xrandr transform value: %.2f (%d)\n" - "scaling method: %s", - crtc->id, - global_scale, scale, - looks_like_w, looks_like_h, - real_scale, XDoubleToFixed (real_scale), - filter); - - XTransform transform = {{ - { XDoubleToFixed (real_scale), 0 , 0 }, - { 0 , XDoubleToFixed (real_scale), 0 }, - { 0 , 0 , XDoubleToFixed (1.0) }, - }}; - - XRRSetCrtcTransform (DISPLAY (crtc), crtc->id, - &transform, - filter, - NULL, 0); -} - -gboolean -gnome_rr_crtc_set_config_with_time (GnomeRRCrtc *crtc, - guint32 timestamp, - int x, - int y, - GnomeRRMode *mode, - GnomeRRRotation rotation, - GnomeRROutput **outputs, - int n_outputs, - float scale, - guint global_scale, - GError **error) +gnome_rr_rotation_from_transform (enum wl_output_transform transform) { - ScreenInfo *info; - GArray *output_ids; - Status status; - gboolean result; - int i; - - g_return_val_if_fail (crtc != NULL, FALSE); - g_return_val_if_fail (mode != NULL || outputs == NULL || n_outputs == 0, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - info = crtc->info; - - if (mode) - { - if (x + mode->width > info->max_width || y + mode->height > info->max_height) - { - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_BOUNDS_ERROR, - /* Translators: the "position", "size", and "maximum" - * words here are not keywords; please translate them - * as usual. A CRTC is a CRT Controller (this is X terminology) */ - _("requested position/size for CRTC %d is outside the allowed limit: " - "position=(%d, %d), size=(%d, %d), maximum=(%d, %d)"), - (int) crtc->id, - x, y, - mode->width, mode->height, - info->max_width, info->max_height); - return FALSE; - } - } - - output_ids = g_array_new (FALSE, FALSE, sizeof (RROutput)); - - if (outputs) - { - for (i = 0; i < n_outputs; ++i) - g_array_append_val (output_ids, outputs[i]->id); - } - - gdk_error_trap_push (); - - set_crtc_scale (crtc, mode, scale, global_scale); - status = XRRSetCrtcConfig (DISPLAY (crtc), info->resources, crtc->id, - timestamp, - x, y, - mode ? mode->id : None, - xrotation_from_rotation (rotation), - (RROutput *)output_ids->data, - output_ids->len); - - g_array_free (output_ids, TRUE); - - if (gdk_error_trap_pop () || status != RRSetConfigSuccess) { - /* Translators: CRTC is a CRT Controller (this is X terminology). - * It is *very* unlikely that you'll ever get this error, so it is - * only listed for completeness. */ - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR, - _("could not set the configuration for CRTC %d"), - (int) crtc->id); - return FALSE; - } else { - result = TRUE; - } - - return result; + return rotation_map[transform]; } +/** + * gnome_rr_crtc_get_current_mode: + * @crtc: a #GnomeRRCrtc + * Returns: (transfer none): the current mode of this crtc + */ GnomeRRMode * gnome_rr_crtc_get_current_mode (GnomeRRCrtc *crtc) { @@ -2322,7 +1893,12 @@ gnome_rr_crtc_can_drive_output (GnomeRRCrtc *crtc, return FALSE; } -/* FIXME: merge with get_mode()? */ +/** + * gnome_rr_crtc_get_position: + * @crtc: a #GnomeRRCrtc + * @x: (out) (allow-none): + * @y: (out) (allow-none): + */ void gnome_rr_crtc_get_position (GnomeRRCrtc *crtc, int *x, @@ -2337,27 +1913,32 @@ gnome_rr_crtc_get_position (GnomeRRCrtc *crtc, *y = crtc->y; } -float -gnome_rr_crtc_get_scale (GnomeRRCrtc *crtc) -{ - g_return_val_if_fail (crtc != NULL, MINIMUM_LOGICAL_SCALE_FACTOR); - - return crtc->scale; -} - -/* FIXME: merge with get_mode()? */ GnomeRRRotation gnome_rr_crtc_get_current_rotation (GnomeRRCrtc *crtc) { g_assert(crtc != NULL); - return crtc->current_rotation; + return gnome_rr_rotation_from_transform (crtc->transform); +} + +static GnomeRRRotation +gnome_rr_rotation_from_all_transforms (int all_transforms) +{ + GnomeRRRotation ret = all_transforms & 0xF; + + if (all_transforms & (1 << WL_OUTPUT_TRANSFORM_FLIPPED)) + ret |= GNOME_RR_REFLECT_X; + + if (all_transforms & (1 << WL_OUTPUT_TRANSFORM_FLIPPED_180)) + ret |= GNOME_RR_REFLECT_Y; + + return ret; } GnomeRRRotation gnome_rr_crtc_get_rotations (GnomeRRCrtc *crtc) { g_assert(crtc != NULL); - return crtc->rotations; + return gnome_rr_rotation_from_all_transforms (crtc->all_transforms); } gboolean @@ -2365,16 +1946,18 @@ gnome_rr_crtc_supports_rotation (GnomeRRCrtc * crtc, GnomeRRRotation rotation) { g_return_val_if_fail (crtc != NULL, FALSE); - return (crtc->rotations & rotation); + return (gnome_rr_rotation_from_all_transforms (crtc->all_transforms) & rotation); } static GnomeRRCrtc * -crtc_new (ScreenInfo *info, RROutput id) +crtc_new (ScreenInfo *info, guint id) { GnomeRRCrtc *crtc = g_slice_new0 (GnomeRRCrtc); crtc->id = id; crtc->info = info; + crtc->current_outputs = g_new0 (GnomeRROutput *, 1); + crtc->possible_outputs = g_new0 (GnomeRROutput *, 1); return crtc; } @@ -2391,8 +1974,8 @@ crtc_copy (const GnomeRRCrtc *from) to->current_mode = from->current_mode; to->x = from->x; to->y = from->y; - to->current_rotation = from->current_rotation; - to->rotations = from->rotations; + to->transform = from->transform; + to->all_transforms = from->all_transforms; to->gamma_size = from->gamma_size; array = g_ptr_array_new (); @@ -2412,161 +1995,27 @@ crtc_copy (const GnomeRRCrtc *from) return to; } -static float -scale_from_transformation (XRRCrtcTransformAttributes *transformation) -{ - XTransform *xt; - float scale; - - if (!transformation) - return 1.0f; - - xt = &transformation->currentTransform; - - if (xt->matrix[0][0] == xt->matrix[1][1]) - scale = XFixedToDouble (xt->matrix[0][0]); - else - scale = XFixedToDouble (xt->matrix[0][0] + xt->matrix[1][1]) / 2.0; - - return 1.0f / scale; -} - -static float -get_transform_scale (GnomeRRCrtc *crtc) -{ - XRRCrtcTransformAttributes *transform_attributes; - float ret; - - if (!XRRGetCrtcTransform (DISPLAY (crtc), crtc->id, &transform_attributes)) - { - transform_attributes = NULL; - } - - ret = scale_from_transformation (transform_attributes); - - XFree (transform_attributes); - - return ret; -} - -guint -gnome_rr_screen_get_global_scale (GnomeRRScreen *screen) -{ - GdkScreen *gdkscreen; - GValue value = G_VALUE_INIT; - gint window_scale = 1; - - gdkscreen = gdk_screen_get_default (); - - g_value_init (&value, G_TYPE_INT); - - if (gdk_screen_get_setting (gdkscreen, "gdk-window-scaling-factor", &value)) - { - window_scale = g_value_get_int (&value); - } - - return (guint) CLAMP (window_scale, MINIMUM_GLOBAL_SCALE_FACTOR, MAXIMUM_GLOBAL_SCALE_FACTOR); -} - -guint -gnome_rr_screen_get_global_scale_setting (GnomeRRScreen *screen) -{ - guint scale_factor; - - scale_factor = g_settings_get_uint (screen->priv->interface_settings, - GLOBAL_SCALE_FACTOR_KEY); - - return scale_factor; -} - -void -gnome_rr_screen_set_global_scale_setting (GnomeRRScreen *screen, - guint scale_factor) -{ - g_settings_set_uint (screen->priv->interface_settings, - GLOBAL_SCALE_FACTOR_KEY, - scale_factor); -} - -static float -get_crtc_scale (GnomeRRCrtc *crtc) -{ - return gnome_rr_screen_get_global_scale (NULL) * get_transform_scale (crtc); -} - -static gboolean -crtc_initialize (GnomeRRCrtc *crtc, - XRRScreenResources *res, - GError **error) +static void +crtc_initialize (GnomeRRCrtc *crtc, GVariant *info) { - XRRCrtcInfo *info = XRRGetCrtcInfo (DISPLAY (crtc), res, crtc->id); - GPtrArray *a; - int i; - -#if 0 - g_print ("CRTC %lx Timestamp: %u\n", crtc->id, (guint32)info->timestamp); -#endif - - if (!info) - { - /* FIXME: We need to reacquire the screen resources */ - /* FIXME: can we actually catch BadRRCrtc, and does it make sense to emit that? */ - - /* Translators: CRTC is a CRT Controller (this is X terminology). - * It is *very* unlikely that you'll ever get this error, so it is - * only listed for completeness. */ - g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR, - _("could not get information about CRTC %d"), - (int) crtc->id); - return FALSE; - } - - /* GnomeRRMode */ - crtc->current_mode = mode_by_id (crtc->info, info->mode); - - crtc->x = info->x; - crtc->y = info->y; - - /* Current outputs */ - a = g_ptr_array_new (); - for (i = 0; i < info->noutput; ++i) - { - GnomeRROutput *output = gnome_rr_output_by_id (crtc->info, info->outputs[i]); + GVariantIter *all_transforms; + int current_mode_id; + guint transform; - if (output) - g_ptr_array_add (a, output); - } - - g_ptr_array_add (a, NULL); - crtc->current_outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE); - - /* Possible outputs */ - a = g_ptr_array_new (); - for (i = 0; i < info->npossible; ++i) - { - GnomeRROutput *output = gnome_rr_output_by_id (crtc->info, info->possible[i]); - - if (output) - g_ptr_array_add (a, output); - } + g_variant_get (info, META_CRTC_STRUCT, + &crtc->id, &crtc->winsys_id, + &crtc->x, &crtc->y, + NULL, NULL, + ¤t_mode_id, + &crtc->transform, &all_transforms, + NULL); - g_ptr_array_add (a, NULL); - crtc->possible_outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE); + if (current_mode_id >= 0) + crtc->current_mode = mode_by_id (crtc->info, current_mode_id); - /* Rotations */ - crtc->current_rotation = gnome_rr_rotation_from_xrotation (info->rotation); - crtc->rotations = gnome_rr_rotation_from_xrotation (info->rotations); - crtc->scale = get_crtc_scale (crtc); - - XRRFreeCrtcInfo (info); - - /* get an store gamma size */ - crtc->gamma_size = XRRGetCrtcGammaSize (DISPLAY (crtc), crtc->id); - - g_debug ("Initialized GnomeCrtc: %d, at %d, %d, with a scale factor of %.2f (%u global scale)", - (int) crtc->id, crtc->x, crtc->y, crtc->scale, gnome_rr_screen_get_global_scale (NULL)); - - return TRUE; + while (g_variant_iter_loop (all_transforms, "u", &transform)) + crtc->all_transforms |= 1 << transform; + g_variant_iter_free (all_transforms); } static void @@ -2579,7 +2028,7 @@ crtc_free (GnomeRRCrtc *crtc) /* GnomeRRMode */ static GnomeRRMode * -mode_new (ScreenInfo *info, RRMode id) +mode_new (ScreenInfo *info, guint id) { GnomeRRMode *mode = g_slice_new0 (GnomeRRMode); @@ -2603,13 +2052,6 @@ gnome_rr_mode_get_width (GnomeRRMode *mode) return mode->width; } -guint -gnome_rr_mode_get_height (GnomeRRMode *mode) -{ - g_return_val_if_fail (mode != NULL, 0); - return mode->height; -} - int gnome_rr_mode_get_freq (GnomeRRMode *mode) { @@ -2624,35 +2066,45 @@ gnome_rr_mode_get_freq_f (GnomeRRMode *mode) return (mode->freq) / 1000.0; } -void -gnome_rr_mode_get_flags (GnomeRRMode *mode, - gboolean *doublescan, - gboolean *interlaced, - gboolean *vsync) +guint +gnome_rr_mode_get_height (GnomeRRMode *mode) +{ + g_return_val_if_fail (mode != NULL, 0); + return mode->height; +} + +/** + * gnome_rr_mode_get_is_tiled: + * @mode: a #GnomeRRMode + * + * Returns TRUE if this mode is a tiled + * mode created for span a tiled monitor. + */ +gboolean +gnome_rr_mode_get_is_tiled (GnomeRRMode *mode) { - g_return_if_fail (mode != NULL); + g_return_val_if_fail (mode != NULL, FALSE); + return mode->tiled; +} - if (doublescan) - *doublescan = mode->doublescan; - if (interlaced) - *interlaced = mode->interlaced; - if (vsync) - *vsync = mode->vsync; +gboolean +gnome_rr_mode_get_is_interlaced (GnomeRRMode *mode) +{ + g_return_val_if_fail (mode != NULL, 0); + return (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0; } static void -mode_initialize (GnomeRRMode *mode, XRRModeInfo *info) +mode_initialize (GnomeRRMode *mode, GVariant *info) { - g_assert (mode != NULL); - g_assert (info != NULL); + gdouble frequency; + + g_variant_get (info, META_MONITOR_MODE_STRUCT, + &mode->id, &mode->winsys_id, + &mode->width, &mode->height, + &frequency, &mode->flags); - mode->name = g_strdup (info->name); - mode->width = info->width; - mode->height = info->height; - mode->freq = info->dotClock / ((float)info->hTotal * info->vTotal) * 1000; - mode->doublescan = (info->modeFlags & RR_DoubleScan); - mode->interlaced = (info->modeFlags & RR_Interlace); - mode->vsync = (info->modeFlags & RR_VSyncPositive); + mode->freq = frequency * 1000; } static GnomeRRMode * @@ -2662,7 +2114,6 @@ mode_copy (const GnomeRRMode *from) to->id = from->id; to->info = from->info; - to->name = g_strdup (from->name); to->width = from->width; to->height = from->height; to->freq = from->freq; @@ -2673,350 +2124,173 @@ mode_copy (const GnomeRRMode *from) static void mode_free (GnomeRRMode *mode) { - g_free (mode->name); g_slice_free (GnomeRRMode, mode); } -void -gnome_rr_crtc_set_gamma (GnomeRRCrtc *crtc, int size, +gboolean +_gnome_rr_screen_apply_configuration (GnomeRRScreen *screen, + gboolean persistent, + GVariant *crtcs, + GVariant *outputs, + GError **error) +{ + return meta_dbus_display_config_call_apply_configuration_sync (screen->priv->proxy, + screen->priv->info->serial, + persistent, + crtcs, outputs, + NULL, error); +} + +gboolean +gnome_rr_crtc_set_gamma (GnomeRRCrtc *crtc, + int size, unsigned short *red, unsigned short *green, unsigned short *blue) { - int copy_size; - XRRCrtcGamma *gamma; + GBytes *red_bytes, *green_bytes, *blue_bytes; + GVariant *red_v, *green_v, *blue_v; + gboolean ok; - g_return_if_fail (crtc != NULL); - g_return_if_fail (red != NULL); - g_return_if_fail (green != NULL); - g_return_if_fail (blue != NULL); + red_bytes = g_bytes_new (red, size * sizeof (unsigned short)); + green_bytes = g_bytes_new (green, size * sizeof (unsigned short)); + blue_bytes = g_bytes_new (blue, size * sizeof (unsigned short)); - if (size != crtc->gamma_size) - return; + red_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), + red_bytes, TRUE); + green_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), + green_bytes, TRUE); + blue_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), + blue_bytes, TRUE); - gamma = XRRAllocGamma (crtc->gamma_size); + ok = meta_dbus_display_config_call_set_crtc_gamma_sync (crtc->info->screen->priv->proxy, + crtc->info->serial, + crtc->id, + red_v, + green_v, + blue_v, + NULL, NULL); - copy_size = crtc->gamma_size * sizeof (unsigned short); - memcpy (gamma->red, red, copy_size); - memcpy (gamma->green, green, copy_size); - memcpy (gamma->blue, blue, copy_size); + g_bytes_unref (red_bytes); + g_bytes_unref (green_bytes); + g_bytes_unref (blue_bytes); + /* The variant above are floating, no need to free them */ - XRRSetCrtcGamma (DISPLAY (crtc), crtc->id, gamma); - XRRFreeGamma (gamma); + return ok; } +/** + * gnome_rr_crtc_get_gamma: + * @crtc: a #GnomeRRCrtc + * @size: + * @red: (out): the minimum width + * @green: (out): the maximum width + * @blue: (out): the minimum height + * + * Returns: %TRUE for success + */ gboolean -gnome_rr_crtc_get_gamma (GnomeRRCrtc *crtc, int *size, - unsigned short **red, unsigned short **green, +gnome_rr_crtc_get_gamma (GnomeRRCrtc *crtc, + int *size, + unsigned short **red, + unsigned short **green, unsigned short **blue) { - int copy_size; - unsigned short *r, *g, *b; - XRRCrtcGamma *gamma; - - g_return_val_if_fail (crtc != NULL, FALSE); - - gamma = XRRGetCrtcGamma (DISPLAY (crtc), crtc->id); - if (!gamma) - return FALSE; - - copy_size = crtc->gamma_size * sizeof (unsigned short); - - if (red) { - r = g_new0 (unsigned short, crtc->gamma_size); - memcpy (r, gamma->red, copy_size); - *red = r; - } - - if (green) { - g = g_new0 (unsigned short, crtc->gamma_size); - memcpy (g, gamma->green, copy_size); - *green = g; - } - - if (blue) { - b = g_new0 (unsigned short, crtc->gamma_size); - memcpy (b, gamma->blue, copy_size); - *blue = b; - } - - XRRFreeGamma (gamma); - - if (size) - *size = crtc->gamma_size; - - return TRUE; -} + GBytes *red_bytes, *green_bytes, *blue_bytes; + GVariant *red_v, *green_v, *blue_v; + gboolean ok; + gsize dummy; + + ok = meta_dbus_display_config_call_get_crtc_gamma_sync (crtc->info->screen->priv->proxy, + crtc->info->serial, + crtc->id, + &red_v, + &green_v, + &blue_v, + NULL, NULL); + if (!ok) + return FALSE; -#define SCALE_FACTORS_PER_INTEGER 4 -#define SCALE_FACTORS_STEPS (1.0 / (float) SCALE_FACTORS_PER_INTEGER) + red_bytes = g_variant_get_data_as_bytes (red_v); + green_bytes = g_variant_get_data_as_bytes (green_v); + blue_bytes = g_variant_get_data_as_bytes (blue_v); -/* The minimum resolution at which we turn on a window-scale of 2 */ -#define HIDPI_LIMIT 192 + /* Unref the variant early so that the bytes hold the only reference to + the data and we don't need to copy + */ + g_variant_unref (red_v); + g_variant_unref (green_v); + g_variant_unref (blue_v); -/* The minimum screen height at which we automatically turn on a window - *-scale of 2; below this there just isn't enough vertical real estate for GNOME - * apps to work, and it's better to just be tiny */ -#define MINIMUM_REAL_VERTICAL_PIXELS 1400 + if (size) + *size = g_bytes_get_size (red_bytes) / sizeof (unsigned short); -/* The minimum logical height we'll allow - any smaller than this is - * not very usable and can make Cinnamon behave unpredictably. */ -#define MINIMUM_LOGICAL_VERTICAL_PIXELS 700 + if (red) + *red = g_bytes_unref_to_data (red_bytes, &dummy); + else + g_bytes_unref (red_bytes); + if (green) + *green = g_bytes_unref_to_data (green_bytes, &dummy); + else + g_bytes_unref (green_bytes); + if (blue) + *blue = g_bytes_unref_to_data (blue_bytes, &dummy); + else + g_bytes_unref (blue_bytes); -static gboolean -is_logical_size_large_enough (int width, - int height) -{ - return height >= MINIMUM_LOGICAL_VERTICAL_PIXELS; + return TRUE; } -static gboolean -is_scale_valid_for_size (float width, - float height, - float scale) +gboolean +gnome_rr_output_get_is_underscanning (GnomeRROutput *output) { - return scale >= MINIMUM_LOGICAL_SCALE_FACTOR && - scale <= MAXIMUM_LOGICAL_SCALE_FACTOR && - is_logical_size_large_enough (floorf (width/scale), floorf (height/scale)); + g_assert(output != NULL); + return output->is_underscanning; } -static float -get_closest_scale_factor_for_resolution (float width, - float height, - float scale) +gboolean +gnome_rr_output_supports_underscanning (GnomeRROutput *output) { - unsigned int i, j; - float scaled_h; - float scaled_w; - float best_scale; - int base_scaled_w; - gboolean found_one; - - best_scale = 0; - - if (!is_scale_valid_for_size (width, height, scale)) - goto out; - - if (fmodf (width, scale) == 0.0 && fmodf (height, scale) == 0.0) - return scale; - - i = 0; - found_one = FALSE; - base_scaled_w = floorf (width / scale); - do - { - for (j = 0; j < 2; j++) - { - float current_scale; - int offset = i * (j ? 1 : -1); - - scaled_w = base_scaled_w + offset; - current_scale = width / scaled_w; - scaled_h = height / current_scale; - - if (current_scale >= scale + SCALE_FACTORS_STEPS || - current_scale <= scale - SCALE_FACTORS_STEPS || - current_scale < MINIMUM_LOGICAL_SCALE_FACTOR || - current_scale > MAXIMUM_LOGICAL_SCALE_FACTOR) - { - goto out; - } - - if (floorf (scaled_h) == scaled_h) - { - found_one = TRUE; - - if (fabsf (current_scale - scale) < fabsf (best_scale - scale)) - best_scale = current_scale; - } - } - - i++; - } - while (!found_one); - -out: - return best_scale; + g_assert (output != NULL); + return output->supports_underscanning; } -float * -gnome_rr_screen_calculate_supported_scales (GnomeRRScreen *screen, - int width, - int height, - int *n_supported_scales) +const char * +_gnome_rr_output_get_connector_type (GnomeRROutput *output) { - unsigned int i, j; - GArray *supported_scales; - - supported_scales = g_array_new (FALSE, FALSE, sizeof (float)); - - for (i = floorf (MINIMUM_LOGICAL_SCALE_FACTOR); - i <= ceilf (MAXIMUM_LOGICAL_SCALE_FACTOR); - i++) - { - for (j = 0; j < SCALE_FACTORS_PER_INTEGER; j++) - { - float scale; - float scale_value = i + j * SCALE_FACTORS_STEPS; - - scale = get_closest_scale_factor_for_resolution (width, - height, - scale_value); - if (scale > 0.0f) - g_array_append_val (supported_scales, scale); - } - } - - if (supported_scales->len == 0) - { - float fallback_scale; - - fallback_scale = MINIMUM_LOGICAL_SCALE_FACTOR; - g_array_append_val (supported_scales, fallback_scale); - } + g_return_val_if_fail (output != NULL, NULL); - *n_supported_scales = supported_scales->len; - return (float *) g_array_free (supported_scales, FALSE); + return output->connector_type; } -static void -get_real_monitor_size (GnomeRRScreen *screen, - GdkMonitor *monitor, - gint monitor_index, - gint *width, - gint *height) +gboolean +_gnome_rr_output_get_tile_info (GnomeRROutput *output, + GnomeRRTile *tile) { - GnomeRROutput **outputs; - XID output_id; - gint i; - - *width = 0; - *height = 0; - - output_id = gdk_x11_screen_get_monitor_output (gdk_screen_get_default (), monitor_index); - outputs = gnome_rr_screen_list_outputs (screen); - - for (i = 0; outputs[i] != NULL; i++) - { - GnomeRROutput *output = outputs[i]; - - if (gnome_rr_output_get_id (output) == output_id) - { - GnomeRRMode *mode = gnome_rr_output_get_current_mode (output); - - if (mode == NULL) - { - mode = gnome_rr_output_get_preferred_mode (output); - } - - if (mode != NULL) - { - *width = gnome_rr_mode_get_width (mode); - *height = gnome_rr_mode_get_height (mode); - } - - break; - } - } + if (output->tile_info.group_id == UNDEFINED_GROUP_ID) + return FALSE; - if (*width == 0 || *height == 0) - { - GdkRectangle rect; - gdk_monitor_get_geometry (monitor, &rect); + if (!tile) + return FALSE; - *width = rect.width; - *height = rect.height; - } + *tile = output->tile_info; + return TRUE; } -guint -gnome_rr_screen_calculate_best_global_scale (GnomeRRScreen *screen, - gint index) -{ - guint window_scale; - GdkDisplay *display; - GdkMonitor *monitor; - int width_mm, height_mm; - int monitor_scale; - double dpi_x, dpi_y; - int real_width, real_height; - - display = gdk_display_get_default (); - - if (index == -1) - { - monitor = gdk_display_get_primary_monitor (display); - - index = 0; - } - else - { - if ((index >= 0) && (index < gdk_display_get_n_monitors (display))) - { - monitor = gdk_display_get_monitor (display, index); - } - else - { - g_warning ("Invalid monitor index provided (%d)", index); - return 1; - } - } - - /* GdkMonitor geometry is affected by any active current transformation/scaling!!!! - * So if I have 2x global scale, and a transform on a 1080 monitor down to 540, that's - * what gdk_monitor_get_geometry() returns. We actually have to get the dimensions from - * the current RRMode for the given monitor. - */ - get_real_monitor_size (screen, monitor, index, &real_width, &real_height); - - - width_mm = gdk_monitor_get_width_mm (monitor); - height_mm = gdk_monitor_get_height_mm (monitor); - monitor_scale = gdk_monitor_get_scale_factor (monitor); - - window_scale = 1; - - g_debug ("Calculating best global scale for monitor %d. Physical size: %dmm x %dmm," - " REAL pixel size: %d x %d. Current global scale: %d, reported monitor scale: %d", - index, width_mm, height_mm, real_width, real_height, gnome_rr_screen_get_global_scale (NULL), monitor_scale); - - if (real_height < MINIMUM_REAL_VERTICAL_PIXELS) - { - g_debug ("REAL height of %d for monitor %d is less than %d, so the recommended scale will be 1", real_height, index, MINIMUM_REAL_VERTICAL_PIXELS); - goto out; - } - - /* Some monitors/TV encode the aspect ratio (16/9 or 16/10) instead of the physical size */ - if ((width_mm == 160 && height_mm == 90) || - (width_mm == 160 && height_mm == 100) || - (width_mm == 16 && height_mm == 9) || - (width_mm == 16 && height_mm == 10) || - (width_mm == 0 || height_mm == 0)) - { - g_debug ("Aspect ratio instead of physical dimensions were encoded as the physical size, or the physical" - " size was not set. Unable to reliably calculate the recommended scale, returning 1"); - goto out; - } - - if (width_mm > 0 && height_mm > 0) { - dpi_x = (double) real_width / (width_mm / 25.4); - dpi_y = (double) real_height / (height_mm / 25.4); - /* We don't completely trust these values so both must be high, and never pick higher ratio than - 2 automatically */ - if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT) - { - g_debug ("The REAL monitor DPI of %.1f x %.1f exceeds the cutoff of %d x %d, recommended scale will be 2", - dpi_x, dpi_y, HIDPI_LIMIT, HIDPI_LIMIT); - - window_scale = 2; - } - else - { - g_debug ("The REAL monitor DPI of %.1f x %.1f does not meet the cutoff of %d x %d, recommended scale will be 1", - dpi_x, dpi_y, HIDPI_LIMIT, HIDPI_LIMIT); - } - } - -out: - return window_scale; +GType +gnome_rr_dpms_mode_get_type (void) +{ + static GType etype = 0; + if (etype == 0) { + static const GEnumValue values[] = { + { GNOME_RR_DPMS_ON, "GNOME_RR_DPMS_ON", "on" }, + { GNOME_RR_DPMS_STANDBY, "GNOME_RR_DPMS_STANDBY", "standby" }, + { GNOME_RR_DPMS_SUSPEND, "GNOME_RR_DPMS_SUSPEND", "suspend" }, + { GNOME_RR_DPMS_OFF, "GNOME_RR_DPMS_OFF", "off" }, + { GNOME_RR_DPMS_UNKNOWN, "GNOME_RR_DPMS_UNKNOWN", "unknown" }, + { 0, NULL, NULL } + }; + etype = g_enum_register_static ("GnomeRRDpmsModeType", values); + } + return etype; } diff --git a/libcinnamon-desktop/gnome-rr.h b/libcinnamon-desktop/gnome-rr.h index 8bfeb21..197783b 100644 --- a/libcinnamon-desktop/gnome-rr.h +++ b/libcinnamon-desktop/gnome-rr.h @@ -45,9 +45,9 @@ typedef struct { typedef struct { GObjectClass parent_class; - void (* changed) (void); - void (* output_connected) (GnomeRROutput *output); - void (* output_disconnected) (GnomeRROutput *output); + void (*changed) (GnomeRRScreen *screen); + void (*output_connected) (GnomeRRScreen *screen, GnomeRROutput *output); + void (*output_disconnected) (GnomeRRScreen *screen, GnomeRROutput *output); } GnomeRRScreenClass; typedef enum @@ -66,7 +66,6 @@ typedef enum { GNOME_RR_DPMS_STANDBY, GNOME_RR_DPMS_SUSPEND, GNOME_RR_DPMS_OFF, - GNOME_RR_DPMS_DISABLED, GNOME_RR_DPMS_UNKNOWN } GnomeRRDpmsMode; @@ -83,10 +82,10 @@ typedef enum { GNOME_RR_ERROR_BOUNDS_ERROR, /* requested bounds of a CRTC are outside the maximum size */ GNOME_RR_ERROR_CRTC_ASSIGNMENT, /* could not assign CRTCs to outputs */ GNOME_RR_ERROR_NO_MATCHING_CONFIG, /* none of the saved configurations matched the current configuration */ - GNOME_RR_ERROR_NO_DPMS_EXTENSION /* DPMS extension is not present */ + GNOME_RR_ERROR_NO_DPMS_EXTENSION, /* DPMS extension is not present */ } GnomeRRError; -#define GNOME_RR_CONNECTOR_TYPE_PANEL "Panel" /* This is a laptop's built-in LCD */ +#define GNOME_RR_CONNECTOR_TYPE_PANEL "Panel" /* This is a built-in LCD */ #define GNOME_TYPE_RR_SCREEN (gnome_rr_screen_get_type()) #define GNOME_RR_SCREEN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_RR_SCREEN, GnomeRRScreen)) @@ -98,24 +97,28 @@ typedef enum { #define GNOME_TYPE_RR_OUTPUT (gnome_rr_output_get_type()) #define GNOME_TYPE_RR_CRTC (gnome_rr_crtc_get_type()) #define GNOME_TYPE_RR_MODE (gnome_rr_mode_get_type()) +#define GNOME_TYPE_RR_DPMS_MODE (gnome_rr_dpms_mode_get_type()) + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GnomeRRScreen, g_object_unref) GType gnome_rr_screen_get_type (void); GType gnome_rr_output_get_type (void); GType gnome_rr_crtc_get_type (void); GType gnome_rr_mode_get_type (void); +GType gnome_rr_dpms_mode_get_type (void); /* GnomeRRScreen */ GnomeRRScreen * gnome_rr_screen_new (GdkScreen *screen, GError **error); +void gnome_rr_screen_new_async (GdkScreen *screen, + GAsyncReadyCallback callback, + gpointer user_data); +GnomeRRScreen * gnome_rr_screen_new_finish (GAsyncResult *result, + GError **error); GnomeRROutput **gnome_rr_screen_list_outputs (GnomeRRScreen *screen); GnomeRRCrtc ** gnome_rr_screen_list_crtcs (GnomeRRScreen *screen); GnomeRRMode ** gnome_rr_screen_list_modes (GnomeRRScreen *screen); GnomeRRMode ** gnome_rr_screen_list_clone_modes (GnomeRRScreen *screen); -void gnome_rr_screen_set_size (GnomeRRScreen *screen, - int width, - int height, - int mm_width, - int mm_height); GnomeRRCrtc * gnome_rr_screen_get_crtc_by_id (GnomeRRScreen *screen, guint32 id); gboolean gnome_rr_screen_refresh (GnomeRRScreen *screen, @@ -129,14 +132,6 @@ void gnome_rr_screen_get_ranges (GnomeRRScreen *scree int *max_width, int *min_height, int *max_height); -void gnome_rr_screen_get_timestamps (GnomeRRScreen *screen, - guint32 *change_timestamp_ret, - guint32 *config_timestamp_ret); - -void gnome_rr_screen_set_primary_output (GnomeRRScreen *screen, - GnomeRROutput *output); - -GnomeRRMode **gnome_rr_screen_create_clone_modes (GnomeRRScreen *screen); gboolean gnome_rr_screen_get_dpms_mode (GnomeRRScreen *screen, GnomeRRDpmsMode *mode, @@ -144,45 +139,32 @@ gboolean gnome_rr_screen_get_dpms_mode (GnomeRRScreen *screen gboolean gnome_rr_screen_set_dpms_mode (GnomeRRScreen *screen, GnomeRRDpmsMode mode, GError **error); -guint gnome_rr_screen_get_global_scale (GnomeRRScreen *screen); -guint gnome_rr_screen_get_global_scale_setting (GnomeRRScreen *screen); -void gnome_rr_screen_set_global_scale_setting (GnomeRRScreen *screen, - guint scale_factor); -gboolean gnome_rr_screen_get_use_upscaling (GnomeRRScreen *screen); -float * gnome_rr_screen_calculate_supported_scales (GnomeRRScreen *screen, - int width, - int height, - int *n_supported_scales); -/* screen class method, used in csd-xsettings and here */ -guint gnome_rr_screen_calculate_best_global_scale (GnomeRRScreen *screen, gint index); /* GnomeRROutput */ guint32 gnome_rr_output_get_id (GnomeRROutput *output); const char * gnome_rr_output_get_name (GnomeRROutput *output); -gboolean gnome_rr_output_is_connected (GnomeRROutput *output); -int gnome_rr_output_get_size_inches (GnomeRROutput *output); -int gnome_rr_output_get_width_mm (GnomeRROutput *output); -int gnome_rr_output_get_height_mm (GnomeRROutput *output); +const char * gnome_rr_output_get_display_name (GnomeRROutput *output); const guint8 * gnome_rr_output_get_edid_data (GnomeRROutput *output, - gsize *size); -gboolean gnome_rr_output_get_ids_from_edid (GnomeRROutput *output, + gsize *size); +void gnome_rr_output_get_ids_from_edid (GnomeRROutput *output, char **vendor, - int *product, - int *serial); - -gint gnome_rr_output_get_backlight_min (GnomeRROutput *output); -gint gnome_rr_output_get_backlight_max (GnomeRROutput *output); -gint gnome_rr_output_get_backlight (GnomeRROutput *output, - GError **error); + char **product, + char **serial); +void gnome_rr_output_get_physical_size (GnomeRROutput *output, + int *width_mm, + int *height_mm); + +gint gnome_rr_output_get_backlight (GnomeRROutput *output); +gint gnome_rr_output_get_min_backlight_step(GnomeRROutput *output); gboolean gnome_rr_output_set_backlight (GnomeRROutput *output, gint value, GError **error); GnomeRRCrtc ** gnome_rr_output_get_possible_crtcs (GnomeRROutput *output); GnomeRRMode * gnome_rr_output_get_current_mode (GnomeRROutput *output); +gboolean gnome_rr_output_is_connected (GnomeRROutput *output); GnomeRRCrtc * gnome_rr_output_get_crtc (GnomeRROutput *output); -const char * gnome_rr_output_get_connector_type (GnomeRROutput *output); -gboolean gnome_rr_output_is_laptop (GnomeRROutput *output); +gboolean gnome_rr_output_is_builtin_display (GnomeRROutput *output); void gnome_rr_output_get_position (GnomeRROutput *output, int *x, int *y); @@ -193,6 +175,8 @@ GnomeRRMode * gnome_rr_output_get_preferred_mode (GnomeRROutput *outpu gboolean gnome_rr_output_supports_mode (GnomeRROutput *output, GnomeRRMode *mode); gboolean gnome_rr_output_get_is_primary (GnomeRROutput *output); +gboolean gnome_rr_output_get_is_underscanning (GnomeRROutput *output); +gboolean gnome_rr_output_supports_underscanning (GnomeRROutput *output); /* GnomeRRMode */ guint32 gnome_rr_mode_get_id (GnomeRRMode *mode); @@ -200,31 +184,18 @@ guint gnome_rr_mode_get_width (GnomeRRMode *mode) guint gnome_rr_mode_get_height (GnomeRRMode *mode); int gnome_rr_mode_get_freq (GnomeRRMode *mode); double gnome_rr_mode_get_freq_f (GnomeRRMode *mode); -void gnome_rr_mode_get_flags (GnomeRRMode *mode, - gboolean *doublescan, - gboolean *interlaced, - gboolean *vsync); +gboolean gnome_rr_mode_get_is_tiled (GnomeRRMode *mode); +gboolean gnome_rr_mode_get_is_interlaced (GnomeRRMode *mode); + /* GnomeRRCrtc */ guint32 gnome_rr_crtc_get_id (GnomeRRCrtc *crtc); -gboolean gnome_rr_crtc_set_config_with_time (GnomeRRCrtc *crtc, - guint32 timestamp, - int x, - int y, - GnomeRRMode *mode, - GnomeRRRotation rotation, - GnomeRROutput **outputs, - int n_outputs, - float scale, - guint global_scale, - GError **error); gboolean gnome_rr_crtc_can_drive_output (GnomeRRCrtc *crtc, GnomeRROutput *output); GnomeRRMode * gnome_rr_crtc_get_current_mode (GnomeRRCrtc *crtc); void gnome_rr_crtc_get_position (GnomeRRCrtc *crtc, int *x, int *y); -float gnome_rr_crtc_get_scale (GnomeRRCrtc *crtc); GnomeRRRotation gnome_rr_crtc_get_current_rotation (GnomeRRCrtc *crtc); GnomeRRRotation gnome_rr_crtc_get_rotations (GnomeRRCrtc *crtc); gboolean gnome_rr_crtc_supports_rotation (GnomeRRCrtc *crtc, @@ -235,9 +206,10 @@ gboolean gnome_rr_crtc_get_gamma (GnomeRRCrtc *crtc, unsigned short **red, unsigned short **green, unsigned short **blue); -void gnome_rr_crtc_set_gamma (GnomeRRCrtc *crtc, +gboolean gnome_rr_crtc_set_gamma (GnomeRRCrtc *crtc, int size, unsigned short *red, unsigned short *green, unsigned short *blue); + #endif /* GNOME_RR_H */ diff --git a/libcinnamon-desktop/gnome-xkb-info.c b/libcinnamon-desktop/gnome-xkb-info.c index 09de962..f2f51ab 100644 --- a/libcinnamon-desktop/gnome-xkb-info.c +++ b/libcinnamon-desktop/gnome-xkb-info.c @@ -25,26 +25,18 @@ #include #include -#include -#include - -#include +#include #include #define XKEYBOARD_CONFIG_(String) ((char *) g_dgettext ("xkeyboard-config", String)) #define GNOME_DESKTOP_USE_UNSTABLE_API +#include "gnome-languages.h" #include "gnome-xkb-info.h" #ifndef XKB_RULES_FILE #define XKB_RULES_FILE "evdev" #endif -#ifndef XKB_LAYOUT -#define XKB_LAYOUT "us" -#endif -#ifndef XKB_MODEL -#define XKB_MODEL "pc105+inet" -#endif typedef struct _Layout Layout; struct _Layout @@ -55,6 +47,8 @@ struct _Layout gchar *description; gboolean is_variant; const Layout *main_layout; + GSList *iso639Ids; + GSList *iso3166Ids; }; typedef struct _XkbOption XkbOption; @@ -76,8 +70,8 @@ struct _XkbOptionGroup struct _GnomeXkbInfoPrivate { GHashTable *option_groups_table; - GHashTable *layouts_by_short_desc; - GHashTable *layouts_by_iso639; + GHashTable *layouts_by_country; + GHashTable *layouts_by_language; GHashTable *layouts_table; /* Only used while parsing */ @@ -86,10 +80,12 @@ struct _GnomeXkbInfoPrivate Layout *current_parser_layout; Layout *current_parser_variant; gchar *current_parser_iso639Id; + gchar *current_parser_iso3166Id; gchar **current_parser_text; }; -G_DEFINE_TYPE_WITH_PRIVATE (GnomeXkbInfo, gnome_xkb_info, G_TYPE_OBJECT); +G_DEFINE_TYPE_WITH_CODE (GnomeXkbInfo, gnome_xkb_info, G_TYPE_OBJECT, + G_ADD_PRIVATE (GnomeXkbInfo)); static void free_layout (gpointer data) @@ -102,6 +98,8 @@ free_layout (gpointer data) g_free (layout->xkb_name); g_free (layout->short_desc); g_free (layout->description); + g_slist_free_full (layout->iso639Ids, g_free); + g_slist_free_full (layout->iso3166Ids, g_free); g_slice_free (Layout, layout); } @@ -130,88 +128,18 @@ free_option_group (gpointer data) g_slice_free (XkbOptionGroup, group); } -/** - * gnome_xkb_info_get_var_defs: (skip) - * @rules: (out) (transfer full): location to store the rules file - * path. Use g_free() when it's no longer needed - * @var_defs: (out) (transfer full): location to store a - * #XkbRF_VarDefsRec pointer. Use gnome_xkb_info_free_var_defs() to - * free it - * - * Gets both the XKB rules file path and the current XKB parameters in - * use by the X server. - * - * Since: 3.6 - */ -void -gnome_xkb_info_get_var_defs (gchar **rules, - XkbRF_VarDefsRec **var_defs) -{ - Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - char *tmp; - - g_return_if_fail (rules != NULL); - g_return_if_fail (var_defs != NULL); - - *rules = NULL; - *var_defs = g_new0 (XkbRF_VarDefsRec, 1); - - gdk_error_trap_push (); - - /* Get it from the X property or fallback on defaults */ - if (!XkbRF_GetNamesProp (display, rules, *var_defs) || !*rules) - { - *rules = strdup (XKB_RULES_FILE); - (*var_defs)->model = strdup (XKB_MODEL); - (*var_defs)->layout = strdup (XKB_LAYOUT); - (*var_defs)->variant = NULL; - (*var_defs)->options = NULL; - } - - gdk_error_trap_pop_ignored (); - - tmp = *rules; - - if (*rules[0] == '/') - *rules = g_strdup (*rules); - else - *rules = g_build_filename (XKB_BASE, "rules", *rules, NULL); - - free (tmp); -} - -/** - * gnome_xkb_info_free_var_defs: (skip) - * @var_defs: #XkbRF_VarDefsRec instance to free - * - * Frees an #XkbRF_VarDefsRec instance allocated by - * gnome_xkb_info_get_var_defs(). - * - * Since: 3.6 - */ -void -gnome_xkb_info_free_var_defs (XkbRF_VarDefsRec *var_defs) -{ - g_return_if_fail (var_defs != NULL); - - free (var_defs->model); - free (var_defs->layout); - free (var_defs->variant); - free (var_defs->options); - - g_free (var_defs); -} - static gchar * get_xml_rules_file_path (const gchar *suffix) { - XkbRF_VarDefsRec *xkb_var_defs; + const gchar *base_path; gchar *rules_file; gchar *xml_rules_file; - gnome_xkb_info_get_var_defs (&rules_file, &xkb_var_defs); - gnome_xkb_info_free_var_defs (xkb_var_defs); + base_path = g_getenv ("XKB_CONFIG_ROOT"); + if (!base_path) + base_path = XKB_BASE; + rules_file = g_build_filename (base_path, "rules", XKB_RULES_FILE, NULL); xml_rules_file = g_strdup_printf ("%s%s", rules_file, suffix); g_free (rules_file); @@ -268,6 +196,10 @@ parse_start_element (GMarkupParseContext *context, { priv->current_parser_text = &priv->current_parser_iso639Id; } + else if (strcmp (element_name, "iso3166Id") == 0) + { + priv->current_parser_text = &priv->current_parser_iso3166Id; + } else if (strcmp (element_name, "layout") == 0) { if (priv->current_parser_layout) @@ -354,25 +286,82 @@ parse_start_element (GMarkupParseContext *context, } } -static gboolean -maybe_replace (GHashTable *table, - gchar *key, - Layout *new_layout) +static void +add_layout_to_table (GHashTable *table, + const gchar *key, + Layout *layout) { - /* There might be multiple layouts for the same language. In that - * case considering the "canonical" layout to be the one with the - * shorter description seems to be good enough. */ - Layout *layout; - gboolean exists; - gboolean replace = TRUE; + GHashTable *set; + + if (!layout->id) + return; + + set = g_hash_table_lookup (table, key); + if (!set) + { + set = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_replace (table, g_strdup (key), set); + } + else + { + if (g_hash_table_contains (set, layout->id)) + return; + } + g_hash_table_replace (set, layout->id, layout); +} + +static void +add_layout_to_locale_tables (Layout *layout, + GHashTable *layouts_by_language, + GHashTable *layouts_by_country) +{ + GSList *l, *lang_codes, *country_codes; + gchar *language, *country; + + lang_codes = layout->iso639Ids; + country_codes = layout->iso3166Ids; + + if (layout->is_variant) + { + if (!lang_codes) + lang_codes = layout->main_layout->iso639Ids; + if (!country_codes) + country_codes = layout->main_layout->iso3166Ids; + } + + for (l = lang_codes; l; l = l->next) + { + language = gnome_get_language_from_code ((gchar *) l->data, NULL); + if (language) + { + add_layout_to_table (layouts_by_language, language, layout); + g_free (language); + } + } - exists = g_hash_table_lookup_extended (table, key, NULL, (gpointer *)&layout); - if (exists) - replace = strlen (new_layout->description) < strlen (layout->description); - if (replace) - g_hash_table_replace (table, key, new_layout); + for (l = country_codes; l; l = l->next) + { + country = gnome_get_country_from_code ((gchar *) l->data, NULL); + if (country) + { + add_layout_to_table (layouts_by_country, country, layout); + g_free (country); + } + } +} - return replace; +static void +add_iso639 (Layout *layout, + gchar *id) +{ + layout->iso639Ids = g_slist_prepend (layout->iso639Ids, id); +} + +static void +add_iso3166 (Layout *layout, + gchar *id) +{ + layout->iso3166Ids = g_slist_prepend (layout->iso3166Ids, id); } static void @@ -396,20 +385,16 @@ parse_end_element (GMarkupParseContext *context, if (g_hash_table_contains (priv->layouts_table, priv->current_parser_layout->id)) { - if (priv->current_parser_layout != NULL) { - free_layout (priv->current_parser_layout); - priv->current_parser_layout = NULL; - } + g_clear_pointer (&priv->current_parser_layout, free_layout); return; } - if (priv->current_parser_layout->short_desc) - maybe_replace (priv->layouts_by_short_desc, - priv->current_parser_layout->short_desc, priv->current_parser_layout); - g_hash_table_replace (priv->layouts_table, priv->current_parser_layout->id, priv->current_parser_layout); + add_layout_to_locale_tables (priv->current_parser_layout, + priv->layouts_by_language, + priv->layouts_by_country); priv->current_parser_layout = NULL; } else if (strcmp (element_name, "variant") == 0) @@ -426,19 +411,22 @@ parse_end_element (GMarkupParseContext *context, priv->current_parser_variant->xkb_name, NULL); - if (priv->current_parser_variant->short_desc) - maybe_replace (priv->layouts_by_short_desc, - priv->current_parser_variant->short_desc, priv->current_parser_variant); + if (g_hash_table_contains (priv->layouts_table, priv->current_parser_variant->id)) + { + g_clear_pointer (&priv->current_parser_variant, free_layout); + return; + } g_hash_table_replace (priv->layouts_table, priv->current_parser_variant->id, priv->current_parser_variant); + add_layout_to_locale_tables (priv->current_parser_variant, + priv->layouts_by_language, + priv->layouts_by_country); priv->current_parser_variant = NULL; } else if (strcmp (element_name, "iso639Id") == 0) { - gboolean replaced = FALSE; - if (!priv->current_parser_iso639Id) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, @@ -446,18 +434,29 @@ parse_end_element (GMarkupParseContext *context, return; } - if (priv->current_parser_layout) - replaced = maybe_replace (priv->layouts_by_iso639, - priv->current_parser_iso639Id, priv->current_parser_layout); - else if (priv->current_parser_variant) - replaced = maybe_replace (priv->layouts_by_iso639, - priv->current_parser_iso639Id, priv->current_parser_variant); - - if (!replaced) - g_free (priv->current_parser_iso639Id); + if (priv->current_parser_variant) + add_iso639 (priv->current_parser_variant, priv->current_parser_iso639Id); + else if (priv->current_parser_layout) + add_iso639 (priv->current_parser_layout, priv->current_parser_iso639Id); priv->current_parser_iso639Id = NULL; } + else if (strcmp (element_name, "iso3166Id") == 0) + { + if (!priv->current_parser_iso3166Id) + { + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "'iso3166Id' elements must enclose text"); + return; + } + + if (priv->current_parser_variant) + add_iso3166 (priv->current_parser_variant, priv->current_parser_iso3166Id); + else if (priv->current_parser_layout) + add_iso3166 (priv->current_parser_layout, priv->current_parser_iso3166Id); + + priv->current_parser_iso3166Id = NULL; + } else if (strcmp (element_name, "group") == 0) { if (!priv->current_parser_group->description || !priv->current_parser_group->id) @@ -516,6 +515,7 @@ parse_error (GMarkupParseContext *context, free_layout (priv->current_parser_layout); free_layout (priv->current_parser_variant); g_free (priv->current_parser_iso639Id); + g_free (priv->current_parser_iso3166Id); } static const GMarkupParser markup_parser = { @@ -560,12 +560,24 @@ parse_rules (GnomeXkbInfo *self) gchar *file_path; GError *error = NULL; + /* Make sure the translated strings we get from XKEYBOARD_CONFIG() are + * in UTF-8 and not in the current locale */ + bind_textdomain_codeset ("xkeyboard-config", "UTF-8"); + /* Maps option group ids to XkbOptionGroup structs. Owns the XkbOptionGroup structs. */ priv->option_groups_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_option_group); - priv->layouts_by_short_desc = g_hash_table_new (g_str_hash, g_str_equal); - priv->layouts_by_iso639 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + /* Maps country strings to a GHashTable which is a set of Layout + struct pointers into the Layout structs stored in + layouts_table. */ + priv->layouts_by_country = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_hash_table_destroy); + /* Maps language strings to a GHashTable which is a set of Layout + struct pointers into the Layout structs stored in + layouts_table. */ + priv->layouts_by_language = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_hash_table_destroy); /* Maps layout ids to Layout structs. Owns the Layout structs. */ priv->layouts_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_layout); @@ -591,36 +603,13 @@ parse_rules (GnomeXkbInfo *self) return; cleanup: - if (error != NULL) { - g_warning ("Failed to load XKB rules file %s: %s", file_path, error->message); - g_error_free (error); - error = NULL; - } - - if (file_path != NULL) { - g_free (file_path); - file_path = NULL; - } - - if (priv->option_groups_table != NULL) { - g_hash_table_destroy (priv->option_groups_table); - priv->option_groups_table = NULL; - } - - if (priv->layouts_by_short_desc != NULL) { - g_hash_table_destroy (priv->layouts_by_short_desc); - priv->layouts_by_short_desc = NULL; - } - - if (priv->layouts_by_iso639 != NULL) { - g_hash_table_destroy (priv->layouts_by_iso639); - priv->layouts_by_iso639 = NULL; - } - - if (priv->layouts_table != NULL) { - g_hash_table_destroy (priv->layouts_table); - priv->layouts_table = NULL; - } + g_warning ("Failed to load XKB rules file %s: %s", file_path, error->message); + g_clear_pointer (&error, g_error_free); + g_clear_pointer (&file_path, g_free); + g_clear_pointer (&priv->option_groups_table, g_hash_table_destroy); + g_clear_pointer (&priv->layouts_by_country, g_hash_table_destroy); + g_clear_pointer (&priv->layouts_by_language, g_hash_table_destroy); + g_clear_pointer (&priv->layouts_table, g_hash_table_destroy); } static gboolean @@ -647,10 +636,10 @@ gnome_xkb_info_finalize (GObject *self) if (priv->option_groups_table) g_hash_table_destroy (priv->option_groups_table); - if (priv->layouts_by_short_desc) - g_hash_table_destroy (priv->layouts_by_short_desc); - if (priv->layouts_by_iso639) - g_hash_table_destroy (priv->layouts_by_iso639); + if (priv->layouts_by_country) + g_hash_table_destroy (priv->layouts_by_country); + if (priv->layouts_by_language) + g_hash_table_destroy (priv->layouts_by_language); if (priv->layouts_table) g_hash_table_destroy (priv->layouts_table); @@ -732,6 +721,36 @@ gnome_xkb_info_get_all_option_groups (GnomeXkbInfo *self) return g_hash_table_get_keys (priv->option_groups_table); } +/** + * gnome_xkb_info_description_for_group: + * @self: a #GnomeXkbInfo + * @group_id: identifier for group + * + * Return value: the translated description for the group @group_id. + * + * Since: 3.8 + */ +const gchar * +gnome_xkb_info_description_for_group (GnomeXkbInfo *self, + const gchar *group_id) +{ + GnomeXkbInfoPrivate *priv; + const XkbOptionGroup *group; + + g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL); + + priv = self->priv; + + if (!ensure_rules_are_parsed (self)) + return NULL; + + group = g_hash_table_lookup (priv->option_groups_table, group_id); + if (!group) + return NULL; + + return XKEYBOARD_CONFIG_(group->description); +} + /** * gnome_xkb_info_get_options_for_group: * @self: a #GnomeXkbInfo @@ -889,92 +908,158 @@ gnome_xkb_info_get_layout_info (GnomeXkbInfo *self, return TRUE; } +static void +collect_layout_ids (gpointer key, + gpointer value, + gpointer data) +{ + Layout *layout = value; + GList **list = data; + + *list = g_list_prepend (*list, layout->id); +} + /** - * gnome_xkb_info_get_layout_info_for_language: + * gnome_xkb_info_get_layouts_for_language: * @self: a #GnomeXkbInfo - * @language: an ISO 639 code - * @id: (out) (allow-none) (transfer none): location to store the - * layout's identifier, or %NULL - * @display_name: (out) (allow-none) (transfer none): location to store - * the layout's display name, or %NULL - * @short_name: (out) (allow-none) (transfer none): location to store - * the layout's short name, or %NULL - * @xkb_layout: (out) (allow-none) (transfer none): location to store - * the layout's XKB name, or %NULL - * @xkb_variant: (out) (allow-none) (transfer none): location to store - * the layout's XKB variant, or %NULL + * @language_code: an ISO 639 code string * - * Retrieves the layout that better fits @language. It also fetches - * information about that layout like gnome_xkb_info_get_layout_info(). + * Returns a list of all layout identifiers we know about for + * @language_code. + * + * Return value: (transfer container) (element-type utf8): the list + * of layout ids. The caller takes ownership of the #GList but not of + * the strings themselves, those are internally allocated and must not + * be modified. + * + * Since: 3.8 + */ +GList * +gnome_xkb_info_get_layouts_for_language (GnomeXkbInfo *self, + const gchar *language_code) +{ + GnomeXkbInfoPrivate *priv; + GHashTable *layouts_for_language; + gchar *language; + GList *list; + + g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL); + + priv = self->priv; + + if (!ensure_rules_are_parsed (self)) + return NULL; + + language = gnome_get_language_from_code (language_code, NULL); + if (!language) + return NULL; + + layouts_for_language = g_hash_table_lookup (priv->layouts_by_language, language); + g_free (language); + + if (!layouts_for_language) + return NULL; + + list = NULL; + g_hash_table_foreach (layouts_for_language, collect_layout_ids, &list); + + return list; +} + +/** + * gnome_xkb_info_get_layouts_for_country: + * @self: a #GnomeXkbInfo + * @country_code: an ISO 3166 code string * - * If a layout can't be found the return value is %FALSE and all the - * (out) parameters are set to %NULL. + * Returns a list of all layout identifiers we know about for + * @country_code. * - * Return value: %TRUE if a layout exists or %FALSE otherwise. + * Return value: (transfer container) (element-type utf8): the list + * of layout ids. The caller takes ownership of the #GList but not of + * the strings themselves, those are internally allocated and must not + * be modified. * - * Since: 3.6 + * Since: 3.8 */ -gboolean -gnome_xkb_info_get_layout_info_for_language (GnomeXkbInfo *self, - const gchar *language, - const gchar **id, - const gchar **display_name, - const gchar **short_name, - const gchar **xkb_layout, - const gchar **xkb_variant) +GList * +gnome_xkb_info_get_layouts_for_country (GnomeXkbInfo *self, + const gchar *country_code) { GnomeXkbInfoPrivate *priv; - const Layout *layout; + GHashTable *layouts_for_country; + gchar *country; + GList *list; - if (id) - *id = NULL; - if (display_name) - *display_name = NULL; - if (short_name) - *short_name = NULL; - if (xkb_layout) - *xkb_layout = NULL; - if (xkb_variant) - *xkb_variant = NULL; + g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL); - g_return_val_if_fail (GNOME_IS_XKB_INFO (self), FALSE); + priv = self->priv; + + if (!ensure_rules_are_parsed (self)) + return NULL; + + country = gnome_get_country_from_code (country_code, NULL); + if (!country) + return NULL; + + layouts_for_country = g_hash_table_lookup (priv->layouts_by_country, country); + g_free (country); + + if (!layouts_for_country) + return NULL; + + list = NULL; + g_hash_table_foreach (layouts_for_country, collect_layout_ids, &list); + + return list; +} + +static void +collect_languages (gpointer value, + gpointer data) +{ + gchar *language = value; + GList **list = data; + + *list = g_list_append (*list, language); +} + +/** + * gnome_xkb_info_get_languages_for_layout: + * @self: a #GnomeXkbInfo + * @layout_id: a layout identifier + * + * Returns a list of all languages supported by a layout, given by + * @layout_id. + * + * Return value: (transfer container) (element-type utf8): the list of + * ISO 639 code strings. The caller takes ownership of the #GList but + * not of the strings themselves, those are internally allocated and + * must not be modified. + * + * Since: 3.18 + */ +GList * +gnome_xkb_info_get_languages_for_layout (GnomeXkbInfo *self, + const gchar *layout_id) +{ + GnomeXkbInfoPrivate *priv; + Layout *layout; + GList *list; + + g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL); priv = self->priv; if (!ensure_rules_are_parsed (self)) - return FALSE; + return NULL; - /* First look in the proper language codes index, if we can't find - * it there try again on the (untranslated) short descriptions since - * sometimes those will give us a good match. */ - if (!g_hash_table_lookup_extended (priv->layouts_by_iso639, language, NULL, (gpointer *)&layout)) - if (!g_hash_table_lookup_extended (priv->layouts_by_short_desc, language, NULL, (gpointer *)&layout)) - return FALSE; + layout = g_hash_table_lookup (priv->layouts_table, layout_id); - if (id) - *id = layout->id; - if (display_name) - *display_name = XKEYBOARD_CONFIG_(layout->description); + if (!layout) + return NULL; - if (!layout->is_variant) - { - if (short_name) - *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc : ""); - if (xkb_layout) - *xkb_layout = layout->xkb_name; - if (xkb_variant) - *xkb_variant = ""; - } - else - { - if (short_name) - *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc : - layout->main_layout->short_desc ? layout->main_layout->short_desc : ""); - if (xkb_layout) - *xkb_layout = layout->main_layout->xkb_name; - if (xkb_variant) - *xkb_variant = layout->xkb_name; - } + list = NULL; + g_slist_foreach (layout->iso639Ids, collect_languages, &list); - return TRUE; + return list; } diff --git a/libcinnamon-desktop/gnome-xkb-info.h b/libcinnamon-desktop/gnome-xkb-info.h index d667a65..938bc31 100644 --- a/libcinnamon-desktop/gnome-xkb-info.h +++ b/libcinnamon-desktop/gnome-xkb-info.h @@ -30,9 +30,6 @@ #include -#include -#include - G_BEGIN_DECLS #define GNOME_TYPE_XKB_INFO (gnome_xkb_info_get_type ()) @@ -58,6 +55,8 @@ struct _GnomeXkbInfoClass GObjectClass parent_class; }; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GnomeXkbInfo, g_object_unref) + GType gnome_xkb_info_get_type (void); GnomeXkbInfo *gnome_xkb_info_new (void); GList *gnome_xkb_info_get_all_layouts (GnomeXkbInfo *self); @@ -67,23 +66,20 @@ gboolean gnome_xkb_info_get_layout_info (GnomeXkbInfo *s const gchar **short_name, const gchar **xkb_layout, const gchar **xkb_variant); -gboolean gnome_xkb_info_get_layout_info_for_language (GnomeXkbInfo *self, - const gchar *language, - const gchar **id, - const gchar **display_name, - const gchar **short_name, - const gchar **xkb_layout, - const gchar **xkb_variant); GList *gnome_xkb_info_get_all_option_groups (GnomeXkbInfo *self); +const gchar *gnome_xkb_info_description_for_group (GnomeXkbInfo *self, + const gchar *group_id); GList *gnome_xkb_info_get_options_for_group (GnomeXkbInfo *self, const gchar *group_id); const gchar *gnome_xkb_info_description_for_option (GnomeXkbInfo *self, const gchar *group_id, const gchar *id); - -void gnome_xkb_info_get_var_defs (gchar **rules, - XkbRF_VarDefsRec **var_defs); -void gnome_xkb_info_free_var_defs (XkbRF_VarDefsRec *var_defs); +GList *gnome_xkb_info_get_layouts_for_language (GnomeXkbInfo *self, + const gchar *language_code); +GList *gnome_xkb_info_get_layouts_for_country (GnomeXkbInfo *self, + const gchar *country_code); +GList *gnome_xkb_info_get_languages_for_layout (GnomeXkbInfo *self, + const gchar *layout_id); G_END_DECLS diff --git a/libcinnamon-desktop/meson.build b/libcinnamon-desktop/meson.build index 61976c9..dc72a27 100644 --- a/libcinnamon-desktop/meson.build +++ b/libcinnamon-desktop/meson.build @@ -1,3 +1,9 @@ +dbus_xrandr_built_sources = gnome.gdbus_codegen('meta-dbus-xrandr', + 'xrandr.xml', + namespace: 'MetaDBus', + interface_prefix: 'org.cinnamon.Muffin' +) + dbus_idle_built_sources = gnome.gdbus_codegen('meta-dbus-idle-monitor', 'idle-monitor.xml', namespace: 'MetaDBus', @@ -12,8 +18,10 @@ libcinnamon_desktop_gir_sources = [ 'gnome-bg.c', 'gnome-desktop-thumbnail.c', 'gnome-desktop-utils.c', + 'gnome-gettext-portable.c', 'gnome-idle-monitor.c', 'gnome-installer.c', + 'gnome-languages.c', 'gnome-pnp-ids.c', 'gnome-rr-config.c', 'gnome-rr-labeler.c', @@ -58,7 +66,11 @@ libcinnamon_desktop_headers = [ ] libcinnamon_desktop = shared_library('cinnamon-desktop', - [libcinnamon_desktop_sources, dbus_idle_built_sources], + [ + libcinnamon_desktop_sources, + dbus_idle_built_sources, + dbus_xrandr_built_sources + ], include_directories: [ rootInclude ], c_args: [ '-DG_LOG_DOMAIN="CinnamonDesktop"', diff --git a/libcinnamon-desktop/meta-xrandr-shared.h b/libcinnamon-desktop/meta-xrandr-shared.h new file mode 100644 index 0000000..84ebd17 --- /dev/null +++ b/libcinnamon-desktop/meta-xrandr-shared.h @@ -0,0 +1,41 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2013 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* This file is shared between mutter (src/core/meta-xrandr-shared.h) + and gnome-desktop (libcinnamon-desktop/meta-xrandr-shared.h). + + The canonical place for all changes is mutter. + + There should be no includes in this file. +*/ + +#ifndef META_XRANDR_SHARED_H +#define META_XRANDR_SHARED_H + +typedef enum { + META_POWER_SAVE_UNKNOWN = -1, + META_POWER_SAVE_ON = 0, + META_POWER_SAVE_STANDBY, + META_POWER_SAVE_SUSPEND, + META_POWER_SAVE_OFF, +} MetaPowerSave; + +#define META_OUTPUT_STRUCT "(uxiausauau@a{sv})" +#define META_CRTC_STRUCT "(uxiiiiiuau@a{sv})" +#define META_MONITOR_MODE_STRUCT "(uxuudu)" +#endif diff --git a/libcinnamon-desktop/test-pnp-ids.c b/libcinnamon-desktop/test-pnp-ids.c index af2c7e6..c70855a 100644 --- a/libcinnamon-desktop/test-pnp-ids.c +++ b/libcinnamon-desktop/test-pnp-ids.c @@ -37,7 +37,7 @@ int main (int argc, char *argv[]) { GnomePnpIds *ids; - guint i; + int i; ids = gnome_pnp_ids_new (); @@ -69,6 +69,7 @@ main (int argc, char *argv[]) if (argc < 2) { show_vendor (ids, "ELO"); show_vendor (ids, "IBM"); + show_vendor (ids, "DEL4085"); } g_object_unref (ids); diff --git a/libcinnamon-desktop/test-xkb-info.c b/libcinnamon-desktop/test-xkb-info.c index 46f828d..b05ccef 100644 --- a/libcinnamon-desktop/test-xkb-info.c +++ b/libcinnamon-desktop/test-xkb-info.c @@ -1,4 +1,3 @@ -#include #define GNOME_DESKTOP_USE_UNSTABLE_API #include int @@ -9,8 +8,6 @@ main (int argc, char **argv) GList *option_groups, *g; GList *options, *o; - gtk_init (&argc, &argv); - info = gnome_xkb_info_new (); layouts = gnome_xkb_info_get_all_layouts (info); diff --git a/libcinnamon-desktop/xrandr.xml b/libcinnamon-desktop/xrandr.xml new file mode 100644 index 0000000..6420660 --- /dev/null +++ b/libcinnamon-desktop/xrandr.xml @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/meson.build b/meson.build index 8fe2765..cc1efc8 100644 --- a/meson.build +++ b/meson.build @@ -40,6 +40,7 @@ xext = dependency('xext', version: '>=1.1') xkbconf = dependency('xkeyboard-config') xkbfile = dependency('xkbfile') xrandr = dependency('xrandr', version: '>=1.3') +iso_codes = dependency('iso-codes') cinnamon_deps = [ gdk_pixb, @@ -58,6 +59,7 @@ cinnamon_deps = [ use_alsa = get_option('alsa') xkb_base = xkbconf.get_variable(pkgconfig: 'xkb_base') +iso_codes_prefix = iso_codes.get_pkgconfig_variable('prefix') # Path to the pnp.ids file -- to know if we use one shipped with another # package, or an internal file @@ -76,18 +78,12 @@ endif ################################################################################ # Config -timerfd_check = cc.compiles(''' -#include -#include -int main () { - timerfd_create (CLOCK_MONOTONIC, TFD_CLOEXEC); - return 0; -} -''') gettext_package = meson.project_name() conf.set_quoted('GETTEXT_PACKAGE', gettext_package) conf.set_quoted('GNOMELOCALEDIR', join_paths(get_option('prefix'), get_option('localedir'))) +conf.set_quoted('LIBLOCALEDIR', join_paths(get_option('prefix'), 'lib', 'locale')) +conf.set_quoted('ISO_CODES_PREFIX', iso_codes_prefix) conf.set_quoted('PACKAGE_VERSION', meson.project_version()) conf.set('HAVE_BIND_TEXTDOMAIN_CODESET', cc.has_function('bind_textdomain_codeset')) @@ -97,8 +93,9 @@ conf.set('HAVE_ALSA', use_alsa) conf.set('HAVE_GETTEXT', true) conf.set('HAVE_INTROSPECTION', true) conf.set('HAVE_SYSTEMD', systemd.found()) -conf.set('HAVE_TIMERFD', timerfd_check) - +conf.set('HAVE_TIMERFD', cc.has_function('timerfd_create')) +conf.set('HAVE_XLOCALE', cc.has_header('xlocale.h')) +conf.set('HAVE_USELOCALE', cc.has_function('uselocale')) ################################################################################ rootInclude = include_directories('.')