Skip to content

Commit

Permalink
Fix #795: Add candidate window config to 'ibus_config.textproto'
Browse files Browse the repository at this point in the history
This is a follow up to 4a77c6a,
which introduced the 'MOZC_IBUS_CANDIDATE_WINDOW' environment variable
per discussion at #637.

With this commit, 'ibus_config.textproto' can be used to customize
when to use 'mozc_renderer' instead of relying on the environment
variable.

To disable Mozc' candidate window:

  mozc_renderer {
    enabled : False
  }

To use Mozc' candidate window for X11 sessions and KDE Wayland
sessions:

  mozc_renderer {
    enabled : True
    compatible_wayland_desktop_names : ["KDE"]
  }

If there is no settings, it will be interpreted as if there was the
following settings.

  mozc_renderer {
    enabled : True
    compatible_wayland_desktop_names : ["GNOME"]
  }

While environment variable 'MOZC_IBUS_CANDIDATE_WINDOW' is still
supported, it is considered to be deprecated and may be removed in a
future version of Mozc.

PiperOrigin-RevId: 560186593
  • Loading branch information
yukawa authored and hiroyuki-komatsu committed Aug 25, 2023
1 parent 0e5d720 commit 65a5d1f
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 31 deletions.
62 changes: 49 additions & 13 deletions docs/configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ engines {
rank : 80
}
active_on_launch: False
mozc_renderer {
enabled : True
compatible_wayland_desktop_names : ["GNOME"]
}
```

The variables of `engines` are mapped to the same named variables of IBus.
Expand Down Expand Up @@ -175,32 +179,64 @@ engines {
}
```


### Candidate window

Linux input method frameworks (e.g. IBus and Fcitx) often provide their own
candidate window UI, while Mozc also provides candidate window UI via
`mozc_renderer` process that is used in macOS and Windows as well.

When `$XDG_SESSION_TYPE` is set to `wayland`, ibus-mozc uses IBus' default
candidate window by default due to the technical limitations in Wayland.
Otherwise, Mozc's candidate window will be used by default.

To override the above behavior, set an environment variable
`MOZC_IBUS_CANDIDATE_WINDOW` to `mozc` or `ibus`.

Here is the quick comparison of two options.

#### Mozc's candidate window
##### Mozc's candidate window

* Provides the same UI and functionality as Mozc for macOS and Windows.
* Many technical and compatibility challenges to properly work under Wayland sessions
([#431](https://github.com/google/mozc/issues/431)).
* Always needs to use X11 protocol, even under Wayland sessions
([#794](https://github.com/google/mozc/issues/794)).
* Theme support (e.g. dark theme) is planned but not yet implemented.

#### IBus' candidate window
##### IBus' candidate window

* Provides a UI that is consistent with the desktop environment (e.g. theme
support including dark mode).
* Supports Wayland by using deep integration with the desktop shell.
* Supports GNOME Wayland by using deep integration with the desktop shell.
* Lacks several features that are available in Mozc for macOS and Windows.

To use IBus' default candidate window, set `enabled : False` as follows.

```
mozc_renderer {
# This means IBus' candidate window is always used.
enabled : False
}
```

Then restart IBus as follows for your change to take effect.
```
ibus restart
```

#### Wayland compatibility of Mozc's candidate window

While how to support IMEs' candidate window UI under Wayland sessions is still
[under discussion](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/40),
in some desktop environments such as GNOME, Mozc's candidate window may continue
to work with XWayland ([#794](https://github.com/google/mozc/issues/794)).

You can configure which Wayland desktop environment is compatible with Mozc's
candidate window will be used as follows.

```
mozc_renderer {
enabled : True
compatible_wayland_desktop_names : ["GNOME"]
}
```

When one of the desktop name specified in `compatible_wayland_desktop_names` is
found in `$XDG_CURRENT_DESKTOP` environment variable, Mozc's candidate window
will be used.

Restart IBus as follows for your change to take effect.
```
ibus restart
```
4 changes: 4 additions & 0 deletions src/unix/ibus/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ mozc_cc_library(
"//protocol:config_cc_proto",
"//renderer:renderer_client",
"//testing:gunit_prod",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
Expand Down Expand Up @@ -306,6 +307,7 @@ mozc_cc_test(
],
),
deps = [
":ibus_config",
":ibus_mozc_lib",
"//base:clock",
"//base:port",
Expand All @@ -316,7 +318,9 @@ mozc_cc_test(
"//protocol:commands_cc_proto",
"//protocol:config_cc_proto",
"//testing:gunit_main",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/random",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)
Expand Down
12 changes: 12 additions & 0 deletions src/unix/ibus/gen_mozc_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
CPP_FOOTER = """} // namespace
#endif // %s"""

XDG_CURRENT_DESKTOP_URL = 'https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys'


def GetXmlElement(key, value):
if key == 'composition_mode':
Expand Down Expand Up @@ -113,6 +115,16 @@ def GetIbusConfigTextProto(engines):
output.append(GetTextProtoElement(key, value))
output.append('}')
output.append('active_on_launch: False')
output.append('mozc_renderer {')
output.append(" # Set 'False' to use IBus' candidate window.")
output.append(' enabled : True')
output.append(" # For Wayland sessions, 'mozc_renderer' will be used if and "
"only if any value")
output.append(' # set in this field (e.g. "GNOME", "KDE") is found in '
'$XDG_CURRENT_DESKTOP.')
output.append(f' # {XDG_CURRENT_DESKTOP_URL}')
output.append(' compatible_wayland_desktop_names : ["GNOME"]')
output.append('}')
return '\n'.join(output)


Expand Down
20 changes: 20 additions & 0 deletions src/unix/ibus/ibus_config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@

#include <string>
#include <utility>
#include <vector>

#include "base/file_util.h"
#include "base/logging.h"
#include "base/protobuf/text_format.h"
#include "base/system_util.h"
#include "unix/ibus/ibus_config.pb.h"
#include "unix/ibus/main.h"
#include "absl/status/status.h"
#include "absl/strings/ascii.h"
Expand Down Expand Up @@ -184,4 +186,22 @@ bool IbusConfig::IsActiveOnLaunch() const {
// https://github.com/google/mozc/issues/201
return false;
}

bool IbusConfig::IsMozcRendererEnabled() const {
if (!config_.has_mozc_renderer()) {
return true;
}
return config_.mozc_renderer().enabled();
}

std::vector<std::string>
IbusConfig::GetMozcRendererCompatibleWaylandDesktopNames() const {
if (!config_.has_mozc_renderer()) {
return {};
}
const auto &desktop_names =
config_.mozc_renderer().compatible_wayland_desktop_names();
return {desktop_names.begin(), desktop_names.end()};
}

} // namespace mozc
4 changes: 3 additions & 1 deletion src/unix/ibus/ibus_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
#ifndef MOZC_UNIX_IBUS_IBUS_CONFIG_H_
#define MOZC_UNIX_IBUS_IBUS_CONFIG_H_

#include <map>
#include <string>
#include <vector>

#include "unix/ibus/ibus_config.pb.h"
#include "absl/strings/string_view.h"
Expand All @@ -57,6 +57,8 @@ class IbusConfig {
const std::string &GetLayout(absl::string_view name) const;
const ibus::Config &GetConfig() const;
bool IsActiveOnLaunch() const;
bool IsMozcRendererEnabled() const;
std::vector<std::string> GetMozcRendererCompatibleWaylandDesktopNames() const;

ibus::Engine::CompositionMode GetCompositionMode(
absl::string_view engine_name) const;
Expand Down
11 changes: 11 additions & 0 deletions src/unix/ibus/ibus_config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ message Config {
repeated Engine engines = 1;
// Whether IME is on (i.e. Hiragana mode) in the initial state.
optional bool active_on_launch = 2;
// Configurations for 'mozc_renderer'.
optional MozcRendererConfig mozc_renderer = 3;
}

message Engine {
Expand All @@ -59,3 +61,12 @@ message Engine {
// The value NONE does not change the composition mode.
optional CompositionMode composition_mode = 8 [default = NONE];
}

message MozcRendererConfig {
// Set 'False' to use IBus' candidate window.
optional bool enabled = 1 [default = true];
// For Wayland sessions, 'mozc_renderer' will be used if and only if any value
// set in this field (e.g. "GNOME", "KDE") is found in $XDG_CURRENT_DESKTOP.
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys
repeated string compatible_wayland_desktop_names = 2;
}
102 changes: 85 additions & 17 deletions src/unix/ibus/mozc_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include "unix/ibus/preedit_handler.h"
#include "unix/ibus/property_handler.h"
#include "unix/ibus/surrounding_text_util.h"
#include "absl/strings/str_split.h"

ABSL_FLAG(bool, use_mozc_renderer, true,
"The engine tries to use mozc_renderer if available.");
Expand Down Expand Up @@ -167,50 +168,84 @@ std::unique_ptr<client::ClientInterface> CreateAndConfigureClient() {
return client;
}

bool UseMozcCandidateWindow() {
if (!absl::GetFlag(FLAGS_use_mozc_renderer)) {
return false;
std::optional<absl::string_view> GetMapValue(
const absl::flat_hash_map<std::string, std::string> &map,
absl::string_view key) {
absl::flat_hash_map<std::string, std::string>::const_iterator it =
map.find(key);
if (it == map.end()) {
return std::nullopt;
}
return absl::string_view(it->second);
}

// Follows the user's preference set in the environment variable.
const std::string candidate_window = GetEnv("MOZC_IBUS_CANDIDATE_WINDOW");
if (candidate_window == "ibus") {
return false;
}
if (candidate_window == "mozc") {
bool IsWaylandSession(
const absl::flat_hash_map<std::string, std::string> &env) {
const std::optional<absl::string_view> env_xdg_session_type =
GetMapValue(env, "XDG_SESSION_TYPE");
if (env_xdg_session_type.has_value() &&
env_xdg_session_type.value() == "wayland") {
return true;
}
return false;
}

if (GetEnv("XDG_SESSION_TYPE") == "wayland") {
// mozc_renderer is not supported on wayland session.
std::vector<std::string> GetCurrentDesktops(
const absl::flat_hash_map<std::string, std::string> &env) {
const std::optional<absl::string_view> env_xdg_current_desktop =
GetMapValue(env, "XDG_CURRENT_DESKTOP");
if (!env_xdg_current_desktop.has_value()) {
return {};
}
// $XDG_CURRENT_DESKTOP is a colon-separated list.
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys
return absl::StrSplit(env_xdg_current_desktop.value(), ':');
}

void UpdateEnvironMap(absl::flat_hash_map<std::string, std::string> &env,
const char *envname) {
const char *result = ::getenv(envname);
if (result == nullptr) {
return;
}
env.insert_or_assign(envname, result);
}

bool UseMozcCandidateWindow(const IbusConfig &ibus_config) {
if (!absl::GetFlag(FLAGS_use_mozc_renderer)) {
return false;
}

// TODO(nona): integrate with renderer/renderer_client.cc
// TODO(nona): Check executable bit for renderer.
const std::string renderer_path =
FileUtil::JoinPath(SystemUtil::GetServerDirectory(), "mozc_renderer");
if (absl::Status s = FileUtil::FileExists(renderer_path); !s.ok()) {
LOG(ERROR) << s;
return false;
}
return true;

absl::flat_hash_map<std::string, std::string> env;
UpdateEnvironMap(env, "MOZC_IBUS_CANDIDATE_WINDOW");
UpdateEnvironMap(env, "XDG_CURRENT_DESKTOP");
UpdateEnvironMap(env, "XDG_SESSION_TYPE");

return CanUseMozcCandidateWindow(ibus_config, env);
}

} // namespace

MozcEngine::MozcEngine()
: last_sync_time_(Clock::GetAbslTime()),
key_event_handler_(new KeyEventHandler),
client_(CreateAndConfigureClient()),
preedit_handler_(new PreeditHandler()),
use_mozc_candidate_window_(UseMozcCandidateWindow()),
use_mozc_candidate_window_(false),
mozc_candidate_window_handler_(new renderer::RendererClient()),
preedit_method_(config::Config::ROMAN) {
ibus_config_.Initialize();
use_mozc_candidate_window_ = UseMozcCandidateWindow(ibus_config_);
if (use_mozc_candidate_window_) {
mozc_candidate_window_handler_.RegisterGSettingsObserver();
}

ibus_config_.Initialize();
property_handler_ = std::make_unique<PropertyHandler>(
std::make_unique<LocaleBasedMessageTranslator>(GetMessageLocale()),
ibus_config_.IsActiveOnLaunch(), client_.get());
Expand Down Expand Up @@ -616,5 +651,38 @@ CandidateWindowHandlerInterface *MozcEngine::GetCandidateWindowHandler(
return &ibus_candidate_window_handler_;
}

bool CanUseMozcCandidateWindow(
const IbusConfig &ibus_config,
const absl::flat_hash_map<std::string, std::string> &env) {
if (!ibus_config.IsMozcRendererEnabled()) {
return false;
}

const std::optional<absl::string_view> env_candidate_window =
GetMapValue(env, "MOZC_IBUS_CANDIDATE_WINDOW");
if (env_candidate_window.has_value()
&& env_candidate_window.value() == "ibus") {
return false;
}

if (!IsWaylandSession(env)) {
return true;
}

const std::vector<std::string> current_desktops = GetCurrentDesktops(env);
if (current_desktops.empty()) {
return false;
}
for (const std::string &compatible_desktop :
ibus_config.GetMozcRendererCompatibleWaylandDesktopNames()) {
if (std::find(current_desktops.begin(),
current_desktops.end(),
compatible_desktop) != current_desktops.end()) {
return true;
}
}
return false;
}

} // namespace ibus
} // namespace mozc
5 changes: 5 additions & 0 deletions src/unix/ibus/mozc_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "unix/ibus/ibus_wrapper.h"
#include "unix/ibus/preedit_handler.h"
#include "unix/ibus/property_handler.h"
#include "absl/container/flat_hash_map.h"

namespace mozc {

Expand Down Expand Up @@ -155,6 +156,10 @@ class MozcEngine : public EngineInterface {
FRIEND_TEST(LaunchToolTest, LaunchToolTest);
};

bool CanUseMozcCandidateWindow(
const IbusConfig &ibus_config,
const absl::flat_hash_map<std::string, std::string> &env);

} // namespace ibus
} // namespace mozc

Expand Down
Loading

0 comments on commit 65a5d1f

Please sign in to comment.