diff --git b/gnome-settings-daemon/gnome-settings-bus.c a/gnome-settings-daemon/gnome-settings-bus.c index ed032127..0f3ed066 100644 --- b/gnome-settings-daemon/gnome-settings-bus.c +++ a/gnome-settings-daemon/gnome-settings-bus.c @@ -27,6 +27,10 @@ #include #include +#if HAVE_WAYLAND +#include +#endif + #include "gnome-settings-bus.h" #define GNOME_SESSION_DBUS_NAME "org.gnome.SessionManager" @@ -192,3 +196,29 @@ out: g_clear_pointer (&variant, g_variant_unref); return ret; } + +static gpointer +is_wayland_session (gpointer user_data) +{ +#if HAVE_WAYLAND + struct wl_display *display; + + display = wl_display_connect (NULL); + if (!display) + return GUINT_TO_POINTER(FALSE); + wl_display_disconnect (display); + return GUINT_TO_POINTER(TRUE); +#else + return GUINT_TO_POINTER(FALSE); +#endif +} + +gboolean +gnome_settings_is_wayland (void) +{ + static GOnce wayland_once = G_ONCE_INIT; + + g_once (&wayland_once, is_wayland_session, NULL); + + return GPOINTER_TO_UINT(wayland_once.retval); +} diff --git b/gnome-settings-daemon/gnome-settings-bus.h a/gnome-settings-daemon/gnome-settings-bus.h index 58dd2a1f..89808101 100644 --- b/gnome-settings-daemon/gnome-settings-bus.h +++ a/gnome-settings-daemon/gnome-settings-bus.h @@ -34,6 +34,7 @@ GsdSessionManager *gnome_settings_bus_get_session_proxy (void); GsdScreenSaver *gnome_settings_bus_get_screen_saver_proxy (void); GsdShell *gnome_settings_bus_get_shell_proxy (void); GsdDisplayConfig *gnome_settings_bus_get_display_config_proxy (void); +gboolean gnome_settings_is_wayland (void); char * gnome_settings_get_chassis_type (void); G_END_DECLS diff --git b/gnome-settings-daemon/meson.build a/gnome-settings-daemon/meson.build index 979ddf95..42af46cc 100644 --- b/gnome-settings-daemon/meson.build +++ a/gnome-settings-daemon/meson.build @@ -30,6 +30,10 @@ endforeach deps = [gio_unix_dep] +if enable_wayland + deps += wayland_client_dep +endif + libgsd = shared_library( 'gsd', sources: sources + dbus_headers, diff --git b/meson.build a/meson.build index 5d33a49c..dbd3abfb 100644 --- b/meson.build +++ a/meson.build @@ -101,9 +101,9 @@ if not geocode_glib_dep.found() endif gio_dep = dependency('gio-2.0', version: '>= 2.53.0') gio_unix_dep = dependency('gio-unix-2.0') -gnome_desktop_dep = dependency('gnome-desktop-4') +gnome_desktop_dep = dependency('gnome-desktop-3.0', version: '>= 3.37.1') gsettings_desktop_dep = dependency('gsettings-desktop-schemas', version: '>= 46.beta') -gtk_dep = dependency('gtk4') +gtk_dep = dependency('gtk+-3.0', version: '>= 3.15.3') gweather_dep = dependency('gweather4') libcanberra_dep = dependency('libcanberra') libgeoclue_dep = dependency('libgeoclue-2.0', version: '>= 2.3.1') @@ -159,6 +159,14 @@ endif has_timerfd_create = cc.has_function('timerfd_create') config_h.set10('HAVE_TIMERFD', has_timerfd_create) +# Check for wayland dependencies +enable_wayland = get_option('wayland') +if enable_wayland + assert(enable_gudev, 'GUDev support is required for wayland support.') + wayland_client_dep = dependency('wayland-client') +endif +config_h.set10('HAVE_WAYLAND', enable_wayland) + # smartcard section enable_smartcard = get_option('smartcard') if enable_smartcard @@ -272,6 +280,7 @@ output += ' NetworkManager support: ' + enable_network_manager.to_strin output += ' Smartcard support: ' + enable_smartcard.to_string() + '\n' output += ' USB Protection support: ' + enable_usb_protection.to_string() + '\n' output += ' Cups support: ' + enable_cups.to_string() + '\n' +output += ' Wayland support: ' + enable_wayland.to_string() + '\n' output += ' RFKill support: ' + enable_rfkill.to_string() + '\n' if enable_systemd output += ' Systemd user unit dir: ' + systemd_userunitdir + '\n' diff --git b/meson_options.txt a/meson_options.txt index 18af48d7..5e2cccab 100644 --- b/meson_options.txt +++ a/meson_options.txt @@ -8,6 +8,7 @@ option('network_manager', type: 'boolean', value: true, description: 'build with option('rfkill', type: 'boolean', value: true, description: 'build with rfkill support (not optional on Linux platforms)') option('smartcard', type: 'boolean', value: true, description: 'build with smartcard support') option('usb-protection', type: 'boolean', value: true, description: 'build with usb-protection support') +option('wayland', type: 'boolean', value: true, description: 'build with Wayland support') option('wwan', type: 'boolean', value: true, description: 'build with WWAN support') option('gcr3', type: 'boolean', value: false, description: 'build with gcr3, otherwise gcr4 is used') option('colord', type: 'boolean', value: true, description: 'build with colord support') diff --git b/plugins/color/main.c a/plugins/color/main.c index 1996a6d1..0b6c7e6e 100644 --- b/plugins/color/main.c +++ a/plugins/color/main.c @@ -1,13 +1,9 @@ -#include - #include "gsd-main-helper.h" #include "gsd-color-manager.h" int main (int argc, char **argv) { - gtk_init (); - return gsd_main_helper (GSD_TYPE_COLOR_MANAGER, argc, argv); } diff --git b/plugins/common/gsd-main-helper.c a/plugins/common/gsd-main-helper.c index 71f8c8df..61894b35 100644 --- b/plugins/common/gsd-main-helper.c +++ a/plugins/common/gsd-main-helper.c @@ -22,9 +22,16 @@ #include #include +#ifdef USE_GTK +#include +#endif #include "gnome-settings-bus.h" +#ifdef USE_GTK +#include "gsd-resources.h" +#endif + #ifndef PLUGIN_NAME #error Include PLUGIN_CFLAGS in the daemon s CFLAGS #endif /* !PLUGIN_NAME */ @@ -186,6 +193,32 @@ register_with_gnome_session (GApplication *manager) g_unsetenv ("DESKTOP_AUTOSTART_ID"); } +#ifdef USE_GTK +static void +set_empty_gtk_theme (gboolean set) +{ + static char *old_gtk_theme = NULL; + + if (set) { + /* Override GTK_THEME to reduce overhead of CSS engine. By using + * GTK_THEME environment variable, GtkSettings is not allowed to + * initially parse the Adwaita theme. + * + * https://bugzilla.gnome.org/show_bug.cgi?id=780555 */ + old_gtk_theme = g_strdup (g_getenv ("GTK_THEME")); + g_setenv ("GTK_THEME", "Disabled", TRUE); + } else { + /* GtkSettings has loaded, so we can drop GTK_THEME used to initialize + * our internal theme. Only the main thread accesses the GTK_THEME + * environment variable, so this is safe to release. */ + if (old_gtk_theme != NULL) + g_setenv ("GTK_THEME", old_gtk_theme, TRUE); + else + g_unsetenv ("GTK_THEME"); + } +} +#endif + static int start (GApplication *manager, int argc, @@ -218,12 +251,28 @@ gsd_main_helper (GType manager_type, textdomain (GETTEXT_PACKAGE); setlocale (LC_ALL, ""); +#ifdef USE_GTK + /* Ensure we don't lose resources during linkage */ + g_resources_register (gsd_get_resource ()); + + set_empty_gtk_theme (TRUE); + + if (! gtk_init_with_args (&argc, &argv, PLUGIN_NAME, entries, NULL, &error)) { + if (error != NULL) { + g_printerr ("%s\n", error->message); + } + exit (1); + } + + set_empty_gtk_theme (FALSE); +#else context = g_option_context_new (NULL); g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_printerr ("%s\n", error->message); exit (1); } +#endif if (verbose) { g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); diff --git b/plugins/common/gsd.gresources.xml a/plugins/common/gsd.gresources.xml new file mode 100644 index 00000000..e4ac1cd4 --- /dev/null +++ a/plugins/common/gsd.gresources.xml @@ -0,0 +1,6 @@ + + + + gtk.css + + diff --git b/plugins/common/gtk.css a/plugins/common/gtk.css new file mode 100644 index 00000000..e69de29b diff --git b/plugins/common/meson.build a/plugins/common/meson.build index b04be8c9..2b91c437 100644 --- b/plugins/common/meson.build +++ a/plugins/common/meson.build @@ -10,6 +10,19 @@ main_helper_sources = files( 'gsd-main-helper.c', ) +resource_data = files('gtk.css') + +sources += gnome.compile_resources( + 'gsd-resources', + 'gsd.gresources.xml', + c_name: 'gsd', + dependencies: resource_data +) + +deps = plugins_deps + [ + gnome_desktop_dep, +] + ldflags = [] if host_is_darwin ldflags += ['-Wl,-bundle_loader,@0@'.format(join_paths(), meson.project_build_root(), meson.project_name(), meson.project_name())] @@ -19,7 +32,7 @@ libcommon = static_library( plugin_name, sources: sources, include_directories: top_inc, - dependencies: [libgsd_dep, gio_dep], + dependencies: deps, c_args: cflags, link_args: ldflags ) diff --git b/plugins/media-keys/audio-selection-test.c a/plugins/media-keys/audio-selection-test.c new file mode 100644 index 00000000..d06759f7 --- /dev/null +++ a/plugins/media-keys/audio-selection-test.c @@ -0,0 +1,263 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2017 Bastien Nocera + * + * 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 . + * + */ + +#include "config.h" + +#include + +#define AUDIO_SELECTION_DBUS_NAME "org.gnome.Shell.AudioDeviceSelection" +#define AUDIO_SELECTION_DBUS_PATH "/org/gnome/Shell/AudioDeviceSelection" +#define AUDIO_SELECTION_DBUS_INTERFACE "org.gnome.Shell.AudioDeviceSelection" + +static guint audio_selection_watch_id; +static guint audio_selection_signal_id; +static GDBusConnection *audio_selection_conn; +static gboolean audio_selection_requested; +static GtkWidget *check_headphones, *check_headset, *check_micro; +static GtkWidget *button, *label; + +/* Copy-paste from gvc-mixer-control.h */ +typedef enum +{ + GVC_HEADSET_PORT_CHOICE_NONE = 0, + GVC_HEADSET_PORT_CHOICE_HEADPHONES = 1 << 0, + GVC_HEADSET_PORT_CHOICE_HEADSET = 1 << 1, + GVC_HEADSET_PORT_CHOICE_MIC = 1 << 2 +} GvcHeadsetPortChoice; + +typedef struct { + GvcHeadsetPortChoice choice; + gchar *name; +} AudioSelectionChoice; + +static AudioSelectionChoice audio_selection_choices[] = { + { GVC_HEADSET_PORT_CHOICE_HEADPHONES, "headphones" }, + { GVC_HEADSET_PORT_CHOICE_HEADSET, "headset" }, + { GVC_HEADSET_PORT_CHOICE_MIC, "microphone" }, +}; + +static void +audio_selection_done (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer data) +{ + const gchar *choice; + + if (!audio_selection_requested) + return; + + choice = NULL; + g_variant_get_child (parameters, 0, "&s", &choice); + if (!choice) + return; + + gtk_label_set_text (GTK_LABEL (label), choice); + + audio_selection_requested = FALSE; +} + +static void +audio_selection_needed (GvcHeadsetPortChoice choices) +{ + gchar *args[G_N_ELEMENTS (audio_selection_choices) + 1]; + guint i, n; + + if (!audio_selection_conn) + return; + + n = 0; + for (i = 0; i < G_N_ELEMENTS (audio_selection_choices); ++i) { + if (choices & audio_selection_choices[i].choice) + args[n++] = audio_selection_choices[i].name; + } + args[n] = NULL; + + audio_selection_requested = TRUE; + g_dbus_connection_call (audio_selection_conn, + AUDIO_SELECTION_DBUS_NAME, + AUDIO_SELECTION_DBUS_PATH, + AUDIO_SELECTION_DBUS_INTERFACE, + "Open", + g_variant_new ("(^as)", args), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); +} + +static void +update_ask_button (void) +{ + guint num_buttons = 0; + gboolean active = FALSE; + + /* Need gnome-shell running */ + if (audio_selection_conn == NULL) + goto end; + + /* Need at least 2 choices */ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_headphones))) + num_buttons++; + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_headset))) + num_buttons++; + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_micro))) + num_buttons++; + + if (num_buttons < 2) + goto end; + + /* And no questions in flight */ + if (audio_selection_requested) + goto end; + + active = TRUE; + +end: + gtk_widget_set_sensitive (GTK_WIDGET (button), active); +} + +static void +audio_selection_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer data) +{ + audio_selection_conn = connection; + audio_selection_signal_id = + g_dbus_connection_signal_subscribe (connection, + AUDIO_SELECTION_DBUS_NAME, + AUDIO_SELECTION_DBUS_INTERFACE, + "DeviceSelected", + AUDIO_SELECTION_DBUS_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + audio_selection_done, + NULL, + NULL); + update_ask_button (); +} + +static void +audio_selection_vanished (GDBusConnection *connection, + const gchar *name, + gpointer data) +{ + if (audio_selection_signal_id) + g_dbus_connection_signal_unsubscribe (audio_selection_conn, + audio_selection_signal_id); + audio_selection_signal_id = 0; + audio_selection_conn = NULL; + update_ask_button (); +} + +static void +watch_gnome_shell (void) +{ + audio_selection_watch_id = + g_bus_watch_name (G_BUS_TYPE_SESSION, + AUDIO_SELECTION_DBUS_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + audio_selection_appeared, + audio_selection_vanished, + NULL, + NULL); +} + +static void +check_buttons_changed (GtkToggleButton *button, + gpointer user_data) +{ + update_ask_button (); +} + +static void +button_clicked (GtkButton *button, + gpointer user_data) +{ + guint choices = 0; + + gtk_label_set_text (GTK_LABEL (label), ""); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_headphones))) + choices |= GVC_HEADSET_PORT_CHOICE_HEADPHONES; + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_headset))) + choices |= GVC_HEADSET_PORT_CHOICE_HEADSET; + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_micro))) + choices |= GVC_HEADSET_PORT_CHOICE_MIC; + + audio_selection_needed (choices); +} + +static void +setup_ui (void) +{ + GtkWidget *window; + GtkWidget *box; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (GTK_WINDOW (window), "delete-event", + G_CALLBACK (gtk_main_quit), NULL); + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8); + gtk_container_add (GTK_CONTAINER (window), box); + + check_headphones = gtk_check_button_new_with_label ("Headphones"); + g_signal_connect (check_headphones, "toggled", + G_CALLBACK (check_buttons_changed), NULL); + gtk_container_add (GTK_CONTAINER (box), check_headphones); + + check_headset = gtk_check_button_new_with_label ("Headset"); + g_signal_connect (check_headset, "toggled", + G_CALLBACK (check_buttons_changed), NULL); + gtk_container_add (GTK_CONTAINER (box), check_headset); + + check_micro = gtk_check_button_new_with_label ("Microphone"); + g_signal_connect (check_micro, "toggled", + G_CALLBACK (check_buttons_changed), NULL); + gtk_container_add (GTK_CONTAINER (box), check_micro); + + button = gtk_button_new_with_label ("Ask!"); + g_signal_connect (button, "clicked", + G_CALLBACK (button_clicked), NULL); + gtk_container_add (GTK_CONTAINER (box), button); + gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE); + + label = gtk_label_new (""); + gtk_container_add (GTK_CONTAINER (box), label); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_headphones), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_headset), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_micro), TRUE); + + gtk_widget_show_all (window); +} + +int main (int argc, char **argv) +{ + gtk_init (&argc, &argv); + + setup_ui (); + watch_gnome_shell (); + + gtk_main (); + + return 0; +} diff --git b/plugins/media-keys/main.c a/plugins/media-keys/main.c index 52ac0ea8..23a1738d 100644 --- b/plugins/media-keys/main.c +++ a/plugins/media-keys/main.c @@ -1,13 +1,9 @@ -#include - #include "gsd-main-helper.h" #include "gsd-media-keys-manager.h" int main (int argc, char **argv) { - gtk_init (); - return gsd_main_helper (GSD_TYPE_MEDIA_KEYS_MANAGER, argc, argv); } diff --git b/plugins/media-keys/meson.build a/plugins/media-keys/meson.build index 2d95686c..255fd2c9 100644 --- b/plugins/media-keys/meson.build +++ a/plugins/media-keys/meson.build @@ -50,3 +50,12 @@ executable( install_rpath: gsd_pkglibdir, install_dir: gsd_libexecdir ) + +program = 'audio-selection-test' + +executable( + program, + program + '.c', + include_directories: top_inc, + dependencies: deps +) diff --git b/plugins/print-notifications/main.c a/plugins/print-notifications/main.c index 5574bc56..6428efd7 100644 --- b/plugins/print-notifications/main.c +++ a/plugins/print-notifications/main.c @@ -1,12 +1,8 @@ -#include - #include "gsd-main-helper.h" #include "gsd-print-notifications-manager.h" int main (int argc, char **argv) { - gtk_init (); - return gsd_main_helper (GSD_TYPE_PRINT_NOTIFICATIONS_MANAGER, argc, argv); } diff --git b/plugins/xsettings/gsd-xsettings-manager.c a/plugins/xsettings/gsd-xsettings-manager.c index 2a90e09a..58cecfe9 100644 --- b/plugins/xsettings/gsd-xsettings-manager.c +++ a/plugins/xsettings/gsd-xsettings-manager.c @@ -1285,17 +1285,6 @@ update_gtk_im_module (GsdXSettingsManager *manager) g_free (setting); } -static gboolean -is_xwayland (GsdXSettingsManager *manager) -{ - int opcode_ignored, event_ignored, error_ignored; - - return XQueryExtension (manager->xdisplay, "XWAYLAND", - &opcode_ignored, - &event_ignored, - &error_ignored) == True; -} - static void gsd_xsettings_manager_startup (GApplication *app) { @@ -1438,7 +1427,7 @@ gsd_xsettings_manager_startup (GApplication *app) update_xft_settings (manager); /* Launch Xwayland services */ - if (is_xwayland (manager)) + if (gnome_settings_is_wayland ()) launch_xwayland_services (); start_fontconfig_monitor (manager); diff --git b/plugins/xsettings/meson.build a/plugins/xsettings/meson.build index 26d9bcc9..ac1ae836 100644 --- b/plugins/xsettings/meson.build +++ a/plugins/xsettings/meson.build @@ -15,7 +15,6 @@ sources += main_helper_sources deps = plugins_deps + [ gsd_enums_dep, - gio_dep, x11_dep, xfixes_dep, libcommon_dep,