Skip to content

Commit df632d7

Browse files
committed
Support ~/.config/xkb ~/.xkb
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.
1 parent c094844 commit df632d7

File tree

6 files changed

+54
-26
lines changed

6 files changed

+54
-26
lines changed

src/im/keyboard/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
add_library(keyboard STATIC keyboard.cpp isocodes.cpp xkbrules.cpp xmlparser.cpp longpress.cpp compose.cpp)
2-
target_link_libraries(keyboard Fcitx5::Core Expat::Expat LibIntl::LibIntl Fcitx5::Module::Spell Fcitx5::Module::Notifications Fcitx5::Module::QuickPhrase PkgConfig::JsonC ${FMT_TARGET})
2+
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})
33
if (ENABLE_X11)
44
target_link_libraries(keyboard Fcitx5::Module::XCB)
55
endif()

src/im/keyboard/keyboard.cpp

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <cstring>
1212
#include <fmt/format.h>
1313
#include <libintl.h>
14+
#include <xkbcommon/xkbcommon.h>
1415
#include "fcitx-config/iniparser.h"
1516
#include "fcitx-utils/charutils.h"
1617
#include "fcitx-utils/cutf8.h"
@@ -166,25 +167,41 @@ KeyboardEngineState::KeyboardEngineState(KeyboardEngine *engine,
166167
KeyboardEngine::KeyboardEngine(Instance *instance) : instance_(instance) {
167168
setupDefaultLongPressConfig(longPressConfig_);
168169
registerDomain("xkeyboard-config", XKEYBOARDCONFIG_DATADIR "/locale");
169-
std::string rule;
170+
std::string ruleName = DEFAULT_XKB_RULES;
171+
std::string extraRuleFile;
170172
#ifdef ENABLE_X11
171173
auto *xcb = instance_->addonManager().addon("xcb");
172174
if (xcb) {
173175
auto rules = xcb->call<IXCBModule::xkbRulesNames>("");
174176
if (!rules[0].empty()) {
175-
rule = rules[0];
176-
if (rule[0] != '/') {
177-
rule = XKEYBOARDCONFIG_XKBBASE "/rules/" + rule;
177+
if (rules[0][0] == '/') {
178+
extraRuleFile = rules[0];
179+
if (!stringutils::endsWith(extraRuleFile, ".xml")) {
180+
extraRuleFile = extraRuleFile + ".xml";
181+
}
182+
} else {
183+
ruleName = rules[0];
178184
}
179-
rule += ".xml";
180-
ruleName_ = rule;
181185
}
182186
}
183187
#endif
184-
if (rule.empty() || !xkbRules_.read(rule)) {
185-
rule = XKEYBOARDCONFIG_XKBBASE "/rules/" DEFAULT_XKB_RULES ".xml";
186-
xkbRules_.read(rule);
187-
ruleName_ = DEFAULT_XKB_RULES;
188+
189+
UniqueCPtr<xkb_context, xkb_context_unref> xkbContext(
190+
xkb_context_new(XKB_CONTEXT_NO_FLAGS));
191+
std::vector<std::string> directories;
192+
if (xkbContext) {
193+
for (unsigned int i = 0,
194+
e = xkb_context_num_include_paths(xkbContext.get());
195+
i < e; i++) {
196+
directories.push_back(
197+
xkb_context_include_path_get(xkbContext.get(), i));
198+
}
199+
}
200+
if (directories.empty()) {
201+
directories.push_back(XKEYBOARDCONFIG_XKBBASE);
202+
}
203+
if (!xkbRules_.read(directories, ruleName, extraRuleFile)) {
204+
xkbRules_.read(directories, DEFAULT_XKB_RULES, "");
188205
}
189206

190207
instance_->inputContextManager().registerProperty("keyboardState",

src/im/keyboard/keyboard.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ class KeyboardEngine final : public InputMethodEngineV3 {
206206
LongPressConfig longPressConfig_;
207207
std::unordered_map<std::string, std::vector<std::string>> longPressData_;
208208
XkbRules xkbRules_;
209-
std::string ruleName_;
210209
KeyStates selectionModifier_;
211210
KeyList selectionKeys_;
212211
std::unique_ptr<EventSource> deferEvent_;

src/im/keyboard/xkbrules.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,29 +148,40 @@ struct XkbRulesParseState : public XMLParser {
148148
}
149149
};
150150

151-
bool XkbRules::read(const std::string &fileName) {
151+
bool XkbRules::read(const std::vector<std::string> &directories,
152+
const std::string &name, const std::string &extraFile) {
152153
clear();
153154

154-
{
155-
XkbRulesParseState state;
156-
state.rules_ = this;
157-
if (!state.parse(fileName)) {
158-
return false;
155+
for (const auto &directory : directories) {
156+
std::string fileName = stringutils::joinPath(
157+
directory, "rules", stringutils::concat(name, ".xml"));
158+
{
159+
XkbRulesParseState state;
160+
state.rules_ = this;
161+
if (!state.parse(fileName)) {
162+
return false;
163+
}
164+
state.merge(this);
159165
}
160-
state.merge(this);
161-
}
162166

163-
if (stringutils::endsWith(fileName, ".xml")) {
164-
auto extraFile = fileName.substr(0, fileName.size() - 3);
165-
extraFile += "extras.xml";
167+
std::string extraFileName = stringutils::joinPath(
168+
directory, "rules", stringutils::concat(name, ".extras.xml"));
166169
{
167170
XkbRulesParseState state;
168171
state.rules_ = this;
169-
if (state.parse(extraFile)) {
172+
if (state.parse(extraFileName)) {
170173
state.merge(this);
171174
}
172175
}
173176
}
177+
178+
if (!extraFile.empty()) {
179+
XkbRulesParseState state;
180+
state.rules_ = this;
181+
if (state.parse(extraFile)) {
182+
state.merge(this);
183+
}
184+
}
174185
return true;
175186
}
176187

src/im/keyboard/xkbrules.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ struct XkbRulesParseState;
5151
class XkbRules {
5252
public:
5353
friend struct XkbRulesParseState;
54-
bool read(const std::string &fileName);
54+
bool read(const std::vector<std::string> &directories,
55+
const std::string &name, const std::string &extraFile);
5556
#ifdef _TEST_XKBRULES
5657
void dump();
5758
#endif

test/testxkbrules.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
int main() {
1212
fcitx::XkbRules xkbRules;
13-
xkbRules.read(XKEYBOARDCONFIG_XKBBASE "/rules/" DEFAULT_XKB_RULES ".xml");
13+
xkbRules.read({XKEYBOARDCONFIG_XKBBASE}, DEFAULT_XKB_RULES, {});
1414
xkbRules.dump();
1515
return 0;
1616
}

0 commit comments

Comments
 (0)