Skip to content

Commit

Permalink
Support ~/.config/xkb ~/.xkb
Browse files Browse the repository at this point in the history
Poke into xkb_context include directory to get all the valid directories.
While we could also use xkbregistry, it polls in libxml then indirectly
polls in icu. I'd prefer to keep using expat as a light weight solution.
  • Loading branch information
wengxt committed Jan 9, 2023
1 parent c094844 commit df632d7
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/im/keyboard/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
add_library(keyboard STATIC keyboard.cpp isocodes.cpp xkbrules.cpp xmlparser.cpp longpress.cpp compose.cpp)
target_link_libraries(keyboard Fcitx5::Core Expat::Expat LibIntl::LibIntl Fcitx5::Module::Spell Fcitx5::Module::Notifications Fcitx5::Module::QuickPhrase PkgConfig::JsonC ${FMT_TARGET})
target_link_libraries(keyboard Fcitx5::Core Expat::Expat LibIntl::LibIntl Fcitx5::Module::Spell Fcitx5::Module::Notifications XKBCommon::XKBCommon Fcitx5::Module::QuickPhrase PkgConfig::JsonC ${FMT_TARGET})
if (ENABLE_X11)
target_link_libraries(keyboard Fcitx5::Module::XCB)
endif()
Expand Down
37 changes: 27 additions & 10 deletions src/im/keyboard/keyboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <cstring>
#include <fmt/format.h>
#include <libintl.h>
#include <xkbcommon/xkbcommon.h>
#include "fcitx-config/iniparser.h"
#include "fcitx-utils/charutils.h"
#include "fcitx-utils/cutf8.h"
Expand Down Expand Up @@ -166,25 +167,41 @@ KeyboardEngineState::KeyboardEngineState(KeyboardEngine *engine,
KeyboardEngine::KeyboardEngine(Instance *instance) : instance_(instance) {
setupDefaultLongPressConfig(longPressConfig_);
registerDomain("xkeyboard-config", XKEYBOARDCONFIG_DATADIR "/locale");
std::string rule;
std::string ruleName = DEFAULT_XKB_RULES;
std::string extraRuleFile;
#ifdef ENABLE_X11
auto *xcb = instance_->addonManager().addon("xcb");
if (xcb) {
auto rules = xcb->call<IXCBModule::xkbRulesNames>("");
if (!rules[0].empty()) {
rule = rules[0];
if (rule[0] != '/') {
rule = XKEYBOARDCONFIG_XKBBASE "/rules/" + rule;
if (rules[0][0] == '/') {
extraRuleFile = rules[0];
if (!stringutils::endsWith(extraRuleFile, ".xml")) {
extraRuleFile = extraRuleFile + ".xml";
}
} else {
ruleName = rules[0];
}
rule += ".xml";
ruleName_ = rule;
}
}
#endif
if (rule.empty() || !xkbRules_.read(rule)) {
rule = XKEYBOARDCONFIG_XKBBASE "/rules/" DEFAULT_XKB_RULES ".xml";
xkbRules_.read(rule);
ruleName_ = DEFAULT_XKB_RULES;

UniqueCPtr<xkb_context, xkb_context_unref> xkbContext(
xkb_context_new(XKB_CONTEXT_NO_FLAGS));
std::vector<std::string> directories;
if (xkbContext) {
for (unsigned int i = 0,
e = xkb_context_num_include_paths(xkbContext.get());
i < e; i++) {
directories.push_back(
xkb_context_include_path_get(xkbContext.get(), i));
}
}
if (directories.empty()) {
directories.push_back(XKEYBOARDCONFIG_XKBBASE);
}
if (!xkbRules_.read(directories, ruleName, extraRuleFile)) {
xkbRules_.read(directories, DEFAULT_XKB_RULES, "");
}

instance_->inputContextManager().registerProperty("keyboardState",
Expand Down
1 change: 0 additions & 1 deletion src/im/keyboard/keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ class KeyboardEngine final : public InputMethodEngineV3 {
LongPressConfig longPressConfig_;
std::unordered_map<std::string, std::vector<std::string>> longPressData_;
XkbRules xkbRules_;
std::string ruleName_;
KeyStates selectionModifier_;
KeyList selectionKeys_;
std::unique_ptr<EventSource> deferEvent_;
Expand Down
35 changes: 23 additions & 12 deletions src/im/keyboard/xkbrules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,29 +148,40 @@ struct XkbRulesParseState : public XMLParser {
}
};

bool XkbRules::read(const std::string &fileName) {
bool XkbRules::read(const std::vector<std::string> &directories,
const std::string &name, const std::string &extraFile) {
clear();

{
XkbRulesParseState state;
state.rules_ = this;
if (!state.parse(fileName)) {
return false;
for (const auto &directory : directories) {
std::string fileName = stringutils::joinPath(
directory, "rules", stringutils::concat(name, ".xml"));
{
XkbRulesParseState state;
state.rules_ = this;
if (!state.parse(fileName)) {
return false;
}
state.merge(this);
}
state.merge(this);
}

if (stringutils::endsWith(fileName, ".xml")) {
auto extraFile = fileName.substr(0, fileName.size() - 3);
extraFile += "extras.xml";
std::string extraFileName = stringutils::joinPath(
directory, "rules", stringutils::concat(name, ".extras.xml"));
{
XkbRulesParseState state;
state.rules_ = this;
if (state.parse(extraFile)) {
if (state.parse(extraFileName)) {
state.merge(this);
}
}
}

if (!extraFile.empty()) {
XkbRulesParseState state;
state.rules_ = this;
if (state.parse(extraFile)) {
state.merge(this);
}
}
return true;
}

Expand Down
3 changes: 2 additions & 1 deletion src/im/keyboard/xkbrules.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ struct XkbRulesParseState;
class XkbRules {
public:
friend struct XkbRulesParseState;
bool read(const std::string &fileName);
bool read(const std::vector<std::string> &directories,
const std::string &name, const std::string &extraFile);
#ifdef _TEST_XKBRULES
void dump();
#endif
Expand Down
2 changes: 1 addition & 1 deletion test/testxkbrules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

int main() {
fcitx::XkbRules xkbRules;
xkbRules.read(XKEYBOARDCONFIG_XKBBASE "/rules/" DEFAULT_XKB_RULES ".xml");
xkbRules.read({XKEYBOARDCONFIG_XKBBASE}, DEFAULT_XKB_RULES, {});
xkbRules.dump();
return 0;
}

0 comments on commit df632d7

Please sign in to comment.