Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions cinnamon-session/csm-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,12 +835,76 @@ csm_util_stop_systemd_unit (const char *unit,
return TRUE;
}

static gboolean
csm_util_unset_user_environment (const char *variable,
GError **error)
{
GDBusConnection *connection;
gboolean environment_updated;
GVariantBuilder builder;
GVariant *reply;
GError *bus_error = NULL;

environment_updated = FALSE;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);

if (connection == NULL) {
return FALSE;
}

g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
g_variant_builder_add (&builder, "s", variable);

reply = g_dbus_connection_call_sync (connection,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"UnsetEnvironment",
g_variant_new ("(@as)",
g_variant_builder_end (&builder)),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, &bus_error);

if (bus_error != NULL) {
g_propagate_error (error, bus_error);
} else {
environment_updated = TRUE;
g_variant_unref (reply);
}

g_clear_object (&connection);

return environment_updated;
}

void
csm_util_setenv (const char *variable,
const char *value)
{
GError *error = NULL;

/* An empty value means "unset". Genuinely remove the variable from the
* process and systemd environments; the D-Bus activation environment has
* no unset operation (only set), so it is cleared to the empty string
* there, which is equivalent to unset for its consumers.
*/
if (value == NULL || *value == '\0') {
g_unsetenv (variable);

if (!csm_util_update_activation_environment (variable, "", &error)) {
g_warning ("Could not clear %s from the activation environment: %s", variable, error->message);
g_clear_error (&error);
}

if (!csm_util_unset_user_environment (variable, &error)) {
g_debug ("Could not make systemd unset the %s environment variable: %s", variable, error->message);
g_clear_error (&error);
}

return;
}

g_setenv (variable, value, TRUE);

/* If this fails it isn't fatal, it means some things like session
Expand Down
107 changes: 99 additions & 8 deletions cinnamon-session/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,46 @@ require_dbus_session (int argc,
return TRUE;
}

/* Whether ~/.xinputrc (written by im-config / mintlocale-im) selects fcitx5.
* On Wayland this is the source of truth, since we do not export
* GTK_IM_MODULE=fcitx there (apps use text-input-v3 via the compositor).
* Mirrors js/misc/imFramework.js.
*
* The "run_im fcitx5" prefix match (with '#'-comment skip) is duplicated in
* muffin src/core/meta-im-mode.c (we run before muffin exists, so we can't
* ask it) and cinnamon's cinnamon-settings bin/util.py. If im-config ever
* changes its line format, all three must change together. */
static gboolean
xinputrc_selects_fcitx (void)
{
gchar *path;
gchar *contents = NULL;
gboolean found = FALSE;

path = g_build_filename (g_get_home_dir (), ".xinputrc", NULL);
if (g_file_get_contents (path, &contents, NULL, NULL)) {
gchar **lines = g_strsplit (contents, "\n", -1);
int i;

for (i = 0; lines[i]; i++) {
const gchar *line = g_strchug (lines[i]);

if (line[0] == '#')
continue;
if (g_str_has_prefix (line, "run_im fcitx5")) {
found = TRUE;
break;
}
}
g_strfreev (lines);
}

g_free (contents);
g_free (path);

return found;
}

int
main (int argc, char **argv)
{
Expand Down Expand Up @@ -268,23 +308,74 @@ main (int argc, char **argv)
{
gchar *ibus_path;
gchar *fcitx_path;
gboolean is_wayland;
gboolean fcitx_selected;
gboolean fcitx_active;

ibus_path = g_find_program_in_path ("ibus-daemon");
fcitx_path = g_find_program_in_path ("fcitx5");

/* When fcitx is the active framework, it owns the Qt input env;
* don't override it with the ibus QT_IM_MODULES list (Qt6 prefers that
* list over QT_IM_MODULE and would try ibus first). On Wayland we always
* assume ibus (there is no fcitx path there currently). Mirrors
* js/misc/imFramework.js. */
{
gboolean is_wayland = csm_util_is_wayland_session ();
* list over QT_IM_MODULE and would try ibus first). Detection follows
* js/misc/imFramework.js: env on X11, ~/.xinputrc on Wayland (where we
* do not set GTK_IM_MODULE=fcitx). */
is_wayland = csm_util_is_wayland_session ();

if (is_wayland) {
fcitx_selected = xinputrc_selects_fcitx ();
} else {
const gchar *gtk_im = g_getenv ("GTK_IM_MODULE");
const gchar *xmod = g_getenv ("XMODIFIERS");
gboolean fcitx_env = (gtk_im && g_strrstr (gtk_im, "fcitx")) ||
(xmod && g_strrstr (xmod, "fcitx"));
fcitx_active = !is_wayland && fcitx_env && (fcitx_path != NULL);
fcitx_selected = (gtk_im && g_strrstr (gtk_im, "fcitx")) ||
(xmod && g_strrstr (xmod, "fcitx"));
}
fcitx_active = fcitx_selected && (fcitx_path != NULL);

/* On Wayland, apps receive input methods through the compositor
* (text-input-v3), not client-side IM modules. Clear any client-side
* IM env up front - including values leaked from an earlier same-boot
* X11 session via the persistent systemd/D-Bus activation environment,
* which would otherwise force apps off text-input-v3 (e.g. a stale
* GTK_IM_MODULE=ibus routes GTK to the client-side ibus module, whose
* candidate popup can't be positioned on Wayland). Passing "" unsets
* each var (csm_util_setenv treats an empty value as unset). The block
* below re-establishes just what this session needs. X11 is left
* untouched: there the client-side modules are the input path. */
if (is_wayland) {
const gchar *im_vars[] = {
"GTK_IM_MODULE", "CLUTTER_IM_MODULE", "QT_IM_MODULE",
"QT_IM_MODULES", "XMODIFIERS", "SDL_IM_MODULE",
};
guint i;

for (i = 0; i < G_N_ELEMENTS (im_vars); i++)
csm_util_setenv (im_vars[i], "");
}

/* SDL_IM_MODULE is the one client-side IM var fcitx sets that ibus
* never does (im-config's 21_ibus.rc deliberately skips it, Debian
* #1008481), so a fcitx value survives a fcitx->ibus switch via the
* activation environment. Clear it whenever fcitx is not active - on
* X11 too, since ibus has no SDL module and SDL defaults to ibus when
* the var is unset. (Wayland already cleared it above.) */
if (!is_wayland && !fcitx_active)
csm_util_setenv ("SDL_IM_MODULE", "");

/* XWayland clients cannot reach fcitx through the compositor
* (text-input-v3 stops at the Wayland boundary), so they need
* the classic client-side paths back: XMODIFIERS drives XIM for
* Xlib and GTK X11 apps, and Qt tries the QT_IM_MODULES list in
* order, keeping native Qt apps on the compositor's text-input
* while xcb apps fall through to the fcitx plugin (Qt5 has only
* the singular and loads the fcitx plugin everywhere - fcitx
* still draws, mirroring im-config's X11 setup). GTK Wayland
* apps keep using text-input-v3: GTK_IM_MODULE stays unset. */
if (is_wayland && fcitx_active) {
csm_util_setenv ("XMODIFIERS", "@im=fcitx");
csm_util_setenv ("QT_IM_MODULES", "wayland;fcitx");
csm_util_setenv ("QT_IM_MODULE", "fcitx");
csm_util_setenv ("SDL_IM_MODULE", "fcitx");
}

if (ibus_path && !fcitx_active) {
Expand Down
Loading