diff --git a/CMakeLists.txt b/CMakeLists.txt
index f94259222..37d5a3a64 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,6 +36,9 @@ find_package(IsoCodes)
find_package(LibXml2)
find_package(XKeyboardConfig)
find_package(Cairo COMPONENTS Cairo XCB EGL)
+find_package(Wayland COMPONENTS Client)
+find_package(WaylandScanner)
+find_package(WaylandProtocols)
set(DEFAULT_XKB_RULES_FILES "${XKEYBOARDCONFIG_XKBBASE}/rules/${DEFAULT_XKB_RULES}.xml")
if (NOT EXISTS "${DEFAULT_XKB_RULES_FILES}")
diff --git a/cmake/FindWaylandProtocols.cmake b/cmake/FindWaylandProtocols.cmake
new file mode 100644
index 000000000..7859af740
--- /dev/null
+++ b/cmake/FindWaylandProtocols.cmake
@@ -0,0 +1,24 @@
+find_package(PkgConfig)
+
+pkg_check_modules(WaylandProtocols QUIET "wayland-protocols>=${WaylandProtocols_FIND_VERSION}")
+
+pkg_get_variable(WaylandProtocols_PKGDATADIR wayland-protocols pkgdatadir)
+
+mark_as_advanced(WaylandProtocols_PKGDATADIR)
+
+string(REGEX REPLACE "[\r\n]" "" WaylandProtocols_PKGDATADIR "${WaylandProtocols_PKGDATADIR}")
+
+find_package_handle_standard_args(WaylandProtocols
+ FOUND_VAR
+ WaylandProtocols_FOUND
+ REQUIRED_VARS
+ WaylandProtocols_PKGDATADIR
+ VERSION_VAR
+ WaylandProtocols_VERSION
+ HANDLE_COMPONENTS
+)
+
+set(WAYLAND_PROTOCOLS_FOUND ${WaylandProtocols_FOUND})
+set(WAYLAND_PROTOCOLS_PKGDATADIR ${WaylandProtocols_PKGDATADIR})
+set(WAYLAND_PROTOCOLS_VERSION ${WaylandProtocols_VERSION})
+
diff --git a/src/frontend/CMakeLists.txt b/src/frontend/CMakeLists.txt
index 25c29f0d3..2ce01caf1 100644
--- a/src/frontend/CMakeLists.txt
+++ b/src/frontend/CMakeLists.txt
@@ -1,2 +1,3 @@
add_subdirectory(xim)
add_subdirectory(dbusfrontend)
+add_subdirectory(waylandim)
diff --git a/src/frontend/waylandim/CMakeLists.txt b/src/frontend/waylandim/CMakeLists.txt
new file mode 100644
index 000000000..57c7a91de
--- /dev/null
+++ b/src/frontend/waylandim/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(WAYLAND_IM_PROTOCOL_SRCS)
+
+ecm_add_wayland_client_protocol(WAYLAND_IM_PROTOCOL_SRCS
+ PROTOCOL ${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/input-method/input-method-unstable-v1.xml
+ BASENAME input-method-unstable-v1)
+
+ecm_add_wayland_client_protocol(WAYLAND_IM_PROTOCOL_SRCS
+ PROTOCOL ${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/text-input/text-input-unstable-v1.xml
+ BASENAME text-input-unstable-v1)
+
+add_library(waylandim MODULE waylandim.cpp ${WAYLAND_IM_PROTOCOL_SRCS})
+target_include_directories(waylandim PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+target_link_libraries(waylandim Fcitx5::Core Wayland::Client XKBCommon::XKBCommon)
+set_target_properties(waylandim PROPERTIES PREFIX "")
+install(TARGETS waylandim DESTINATION "${FCITX_INSTALL_ADDONDIR}")
+install(FILES waylandim.conf DESTINATION "${FCITX_INSTALL_PKGDATADIR}/addon")
+
diff --git a/src/frontend/waylandim/waylandim.conf b/src/frontend/waylandim/waylandim.conf
new file mode 100644
index 000000000..0873a74e4
--- /dev/null
+++ b/src/frontend/waylandim/waylandim.conf
@@ -0,0 +1,8 @@
+[Addon]
+Name=waylandim
+Type=SharedLibrary
+Library=waylandim
+Category=Frontend
+Dependencies/0=wayland
+Dependencies/Length=1
+
diff --git a/src/frontend/waylandim/waylandim.cpp b/src/frontend/waylandim/waylandim.cpp
new file mode 100644
index 000000000..cfd174f08
--- /dev/null
+++ b/src/frontend/waylandim/waylandim.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2016~2016 by CSSlayer
+ * wengxt@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING. If not,
+ * see .
+ */
+#include "waylandim.h"
+#include "fcitx-utils/utf8.h"
+#include "fcitx/inputcontext.h"
+#include "wayland-text-input-unstable-v1-client-protocol.h"
+#include
+#include
+#include
+#include
+
+namespace fcitx {
+
+class WaylandIMServer {
+ friend class WaylandIMInputContextV1;
+public:
+ WaylandIMServer(wl_display *display, FocusGroup *group, const std::string &name, WaylandIMModule *waylandim);
+
+ InputContextManager &inputContextManager() { return parent_->instance()->inputContextManager(); }
+
+ void registryHandlerGlobal(struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version);
+ void registryHandlerGlobalRemove(struct wl_registry *registry, uint32_t name);
+
+ void activate(struct zwp_input_method_v1 *inputMethod, struct zwp_input_method_context_v1 *id);
+
+ void deactivate(struct zwp_input_method_v1 *inputMethod, struct zwp_input_method_context_v1 *id);
+
+ ~WaylandIMServer() {}
+
+private:
+ static const struct wl_registry_listener registryListener;
+ static const struct zwp_input_method_v1_listener inputMethodListener;
+ FocusGroup *group_;
+ std::string name_;
+ WaylandIMModule *parent_;
+ zwp_input_method_v1 *inputMethodV1_;
+
+ std::unique_ptr context_;
+ std::unique_ptr keymap_;
+ std::unique_ptr state_;
+
+ struct StateMask {
+ uint32_t shift_mask;
+ uint32_t lock_mask;
+ uint32_t control_mask;
+ uint32_t mod1_mask;
+ uint32_t mod2_mask;
+ uint32_t mod3_mask;
+ uint32_t mod4_mask;
+ uint32_t mod5_mask;
+ uint32_t super_mask;
+ uint32_t hyper_mask;
+ uint32_t meta_mask;
+ } stateMask_;
+
+ KeyStates modifiers_;
+};
+
+const struct wl_registry_listener WaylandIMServer::registryListener = {
+ [](void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {
+ static_cast(data)->registryHandlerGlobal(registry, name, interface, version);
+ },
+ [](void *data, struct wl_registry *registry, uint32_t name) {
+ static_cast(data)->registryHandlerGlobalRemove(registry, name);
+ }};
+const struct zwp_input_method_v1_listener WaylandIMServer::inputMethodListener = {
+ [](void *data, struct zwp_input_method_v1 *inputMethod, struct zwp_input_method_context_v1 *id) {
+ static_cast(data)->activate(inputMethod, id);
+ },
+ [](void *data, struct zwp_input_method_v1 *inputMethod, struct zwp_input_method_context_v1 *id) {
+ static_cast(data)->deactivate(inputMethod, id);
+ }};
+
+class WaylandIMInputContextV1 : public InputContext {
+public:
+ WaylandIMInputContextV1(InputContextManager &inputContextManager, WaylandIMServer *server,
+ zwp_input_method_context_v1 *ic)
+ : InputContext(inputContextManager), server_(server), ic_(ic) {
+ zwp_input_method_context_v1_set_user_data(ic_, this);
+ zwp_input_method_context_v1_add_listener(ic_, &inputMethodContextListener, this);
+
+ auto keyboard = zwp_input_method_context_v1_grab_keyboard(ic_);
+ wl_keyboard_add_listener(keyboard, &keyboardListener, ic);
+ }
+ ~WaylandIMInputContextV1() { zwp_input_method_context_v1_set_user_data(ic_, nullptr); }
+
+protected:
+ virtual void commitStringImpl(const std::string &text) override {
+ zwp_input_method_context_v1_commit_string(ic_, serial_, text.c_str());
+ }
+ virtual void deleteSurroundingTextImpl(int offset, unsigned int size) override {
+ zwp_input_method_context_v1_delete_surrounding_text(ic_, offset, size);
+ }
+ virtual void forwardKeyImpl(const ForwardKeyEvent &key) override {
+ zwp_input_method_context_v1_keysym(
+ ic_, serial_, 0, key.rawKey().sym(),
+ key.isRelease() ? WL_KEYBOARD_KEY_STATE_RELEASED : WL_KEYBOARD_KEY_STATE_PRESSED, key.rawKey().states());
+ }
+
+ static inline unsigned int waylandFormat(TextFormatFlags flags) {
+ unsigned int result = 0;
+ if (flags & TextFormatFlag::UnderLine) {
+ result |= ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE;
+ }
+ if (flags & TextFormatFlag::HighLight) {
+ result |= ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_SELECTION;
+ }
+ if (flags & TextFormatFlag::Bold) {
+ result |= ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_ACTIVE;
+ }
+ if (flags & TextFormatFlag::Strike) {
+ result |= ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INCORRECT;
+ }
+ return result;
+ }
+
+ virtual void updatePreeditImpl() override {
+ auto &preedit = this->preedit();
+
+ for (int i = 0, e = preedit.size(); i < e; i++) {
+ if (!utf8::validate(preedit.stringAt(i))) {
+ return;
+ }
+ }
+
+ zwp_input_method_context_v1_preedit_cursor(ic_, preedit.cursor());
+ // FIXME second string should be for commit
+ zwp_input_method_context_v1_preedit_string(ic_, serial_, preedit.toString().c_str(),
+ preedit.toString().c_str());
+ unsigned int index;
+ for (int i = 0, e = preedit.size(); i < e; i++) {
+ zwp_input_method_context_v1_preedit_styling(ic_, index, preedit.stringAt(i).size(),
+ waylandFormat(preedit.formatAt(i)));
+ index += preedit.stringAt(i).size();
+ }
+ }
+
+private:
+ void surroundingTextCallback(struct zwp_input_method_context_v1 *inputContext, const char *text, uint32_t cursor,
+ uint32_t anchor);
+ void resetCallback(struct zwp_input_method_context_v1 *inputContext);
+ void contentTypeCallback(struct zwp_input_method_context_v1 *inputContext, uint32_t hint, uint32_t purpose);
+ void invokeActionCallback(struct zwp_input_method_context_v1 *inputContext, uint32_t button, uint32_t index);
+ void commitStateCallback(struct zwp_input_method_context_v1 *inputContext, uint32_t serial);
+ void preferredLanguageCallback(struct zwp_input_method_context_v1 *inputContext, const char *language);
+
+ void keymapCallback(struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size);
+ void keyCallback(struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state);
+ void modifiersCallback(struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked, uint32_t group);
+
+ static const struct zwp_input_method_context_v1_listener inputMethodContextListener;
+ static const struct wl_keyboard_listener keyboardListener;
+ WaylandIMServer *server_;
+ zwp_input_method_context_v1 *ic_;
+ uint32_t serial_ = 0;
+};
+
+const struct zwp_input_method_context_v1_listener WaylandIMInputContextV1::inputMethodContextListener = {
+ [](void *data, struct zwp_input_method_context_v1 *inputContext, const char *text, uint32_t cursor,
+ uint32_t anchor) {
+ static_cast(data)->surroundingTextCallback(inputContext, text, cursor, anchor);
+ },
+ [](void *data, struct zwp_input_method_context_v1 *inputContext) {
+ static_cast(data)->resetCallback(inputContext);
+ },
+ [](void *data, struct zwp_input_method_context_v1 *inputContext, uint32_t hint, uint32_t purpose) {
+ static_cast(data)->contentTypeCallback(inputContext, hint, purpose);
+ },
+ [](void *data, struct zwp_input_method_context_v1 *inputContext, uint32_t button, uint32_t index) {
+ static_cast(data)->invokeActionCallback(inputContext, button, index);
+ },
+ [](void *data, struct zwp_input_method_context_v1 *inputContext, uint32_t serial) {
+ static_cast(data)->commitStateCallback(inputContext, serial);
+ },
+ [](void *data, struct zwp_input_method_context_v1 *inputContext, const char *language) {
+ static_cast(data)->preferredLanguageCallback(inputContext, language);
+ }};
+
+const struct wl_keyboard_listener WaylandIMInputContextV1::keyboardListener = {
+
+ [](void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) {
+ static_cast(data)->keymapCallback(keyboard, format, fd, size);
+ },
+ nullptr, nullptr,
+ [](void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
+ static_cast(data)->keyCallback(keyboard, serial, time, key, state);
+ },
+ [](void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group) {
+ static_cast(data)->modifiersCallback(keyboard, serial, mods_depressed, mods_latched,
+ mods_locked, group);
+ },
+ nullptr,
+};
+
+WaylandIMServer::WaylandIMServer(wl_display *display, FocusGroup *group, const std::string &name,
+ WaylandIMModule *waylandim)
+ : group_(group), name_(name), parent_(waylandim), context_(nullptr, &xkb_context_unref), keymap_(nullptr, xkb_keymap_unref), state_(nullptr, xkb_state_unref) {
+ struct wl_registry *registry = wl_display_get_registry(display);
+ wl_registry_add_listener(registry, &WaylandIMServer::registryListener, waylandim);
+}
+
+void WaylandIMServer::registryHandlerGlobal(struct wl_registry *registry, uint32_t name, const char *interface,
+ uint32_t version) {
+ FCITX_UNUSED(version);
+ if (0 == strcmp(interface, "zwp_input_method_v1")) {
+ inputMethodV1_ =
+ static_cast(wl_registry_bind(registry, name, &zwp_input_method_v1_interface, 1));
+ zwp_input_method_v1_add_listener(inputMethodV1_, &WaylandIMServer::inputMethodListener, this);
+ }
+}
+
+void WaylandIMServer::registryHandlerGlobalRemove(struct wl_registry *registry, uint32_t name) {
+ // FIXME do we need anything here?
+ FCITX_UNUSED(registry);
+ FCITX_UNUSED(name);
+}
+
+void WaylandIMServer::activate(struct zwp_input_method_v1 *inputMethod, struct zwp_input_method_context_v1 *id) {
+ assert(inputMethod == inputMethodV1_);
+ auto ic = new WaylandIMInputContextV1(parent_->instance()->inputContextManager(), this, id);
+ ic->setDisplayServer("wayland:" + name_);
+ ic->setFocusGroup(group_);
+}
+
+void WaylandIMServer::deactivate(struct zwp_input_method_v1 *inputMethod, struct zwp_input_method_context_v1 *id) {
+ assert(inputMethod == inputMethodV1_);
+
+ auto ic = static_cast(zwp_input_method_context_v1_get_user_data(id));
+ delete ic;
+}
+
+void WaylandIMInputContextV1::surroundingTextCallback(struct zwp_input_method_context_v1 *inputContext,
+ const char *text, uint32_t cursor, uint32_t anchor) {
+ assert(ic_ == inputContext);
+ surroundingText().setText(text, cursor, anchor);
+ updateSurroundingText();
+}
+void WaylandIMInputContextV1::resetCallback(struct zwp_input_method_context_v1 *inputContext) {
+ assert(ic_ == inputContext);
+ reset();
+}
+void WaylandIMInputContextV1::contentTypeCallback(struct zwp_input_method_context_v1 *inputContext, uint32_t hint,
+ uint32_t purpose) {
+ assert(ic_ == inputContext);
+ CapabilityFlags flags;
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_PASSWORD) {
+ flags |= CapabilityFlag::Password;
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION) {
+ flags |= CapabilityFlag::WordCompletion;
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CORRECTION) {
+ flags |= CapabilityFlag::SpellCheck;
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CAPITALIZATION) {
+ flags |= CapabilityFlag::UppercaseWords;
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_LOWERCASE) {
+ flags |= CapabilityFlag::Lowercase;
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_UPPERCASE) {
+ flags |= CapabilityFlag::Uppercase;
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_TITLECASE) {
+ // ??
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_HIDDEN_TEXT) {
+ flags |= CapabilityFlag::HiddenText;
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_SENSITIVE_DATA) {
+ flags |= CapabilityFlag::Sensitive;
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_LATIN) {
+ flags |= CapabilityFlag::Alpha;
+ }
+ if (hint & ZWP_TEXT_INPUT_V1_CONTENT_HINT_MULTILINE) {
+ flags |= CapabilityFlag::Multiline;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_ALPHA) {
+ flags |= CapabilityFlag::Alpha;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DIGITS) {
+ flags |= CapabilityFlag::Digit;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER) {
+ flags |= CapabilityFlag::Number;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD) {
+ flags |= CapabilityFlag::Password;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PHONE) {
+ flags |= CapabilityFlag::Dialable;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_URL) {
+ flags |= CapabilityFlag::Url;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_EMAIL) {
+ flags |= CapabilityFlag::Email;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NAME) {
+ flags |= CapabilityFlag::Name;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DATE) {
+ flags |= CapabilityFlag::Date;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_TIME) {
+ flags |= CapabilityFlag::Time;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DATETIME) {
+ flags |= CapabilityFlag::Date;
+ flags |= CapabilityFlag::Time;
+ }
+ if (purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_TERMINAL) {
+ flags |= CapabilityFlag::Terminal;
+ }
+ setCapabilityFlags(flags);
+}
+void WaylandIMInputContextV1::invokeActionCallback(struct zwp_input_method_context_v1 *inputContext, uint32_t button,
+ uint32_t index) {
+ assert(ic_ == inputContext);
+ FCITX_UNUSED(button);
+ FCITX_UNUSED(index);
+}
+void WaylandIMInputContextV1::commitStateCallback(struct zwp_input_method_context_v1 *inputContext, uint32_t serial) {
+ assert(ic_ == inputContext);
+ serial_ = serial;
+}
+void WaylandIMInputContextV1::preferredLanguageCallback(struct zwp_input_method_context_v1 *inputContext,
+ const char *language) {
+ assert(ic_ == inputContext);
+ FCITX_UNUSED(language);
+}
+
+void WaylandIMInputContextV1::keymapCallback(struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) {
+ if (!server_->context_) {
+ server_->context_.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
+ xkb_context_set_log_level(server_->context_.get(), XKB_LOG_LEVEL_CRITICAL);
+ }
+
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ close(fd);
+ return;
+ }
+
+ if (server_->keymap_) {
+ server_->keymap_.reset();
+ }
+
+ std::vector buffer;
+ buffer.resize(1024);
+ int bufferPtr = 0;
+ int readSize;
+ while ((readSize = read(fd, &buffer.data()[bufferPtr], buffer.size() - bufferPtr)) > 0) {
+ bufferPtr += readSize;
+ if (bufferPtr == buffer.size()) {
+ buffer.resize(buffer.size() * 2);
+ }
+ }
+ buffer[bufferPtr] = 0;
+
+ server_->keymap_.reset(xkb_keymap_new_from_string (server_->context_.get(),
+ buffer.data(),
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ XKB_KEYMAP_COMPILE_NO_FLAGS));
+
+ close(fd);
+
+ if (!server_->keymap_) {
+ return;
+ }
+
+ server_->state_.reset(xkb_state_new (server_->keymap_.get()));
+ if (!server_->state_) {
+ server_->keymap_.reset();
+ return;
+ }
+
+ server_->stateMask_.shift_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Shift");
+ server_->stateMask_.lock_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Lock");
+ server_->stateMask_.control_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Control");
+ server_->stateMask_.mod1_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Mod1");
+ server_->stateMask_.mod2_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Mod2");
+ server_->stateMask_.mod3_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Mod3");
+ server_->stateMask_.mod4_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Mod4");
+ server_->stateMask_.mod5_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Mod5");
+ server_->stateMask_.super_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Super");
+ server_->stateMask_.hyper_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Hyper");
+ server_->stateMask_.meta_mask =
+ 1 << xkb_keymap_mod_get_index(server_->keymap_.get(), "Meta");
+}
+
+void WaylandIMInputContextV1::keyCallback(struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
+ if (!server_->state_) {
+ return;
+ }
+
+ // EVDEV OFFSET
+ uint32_t code = key + 8;
+ const xkb_keysym_t *syms;
+ KeyEvent event(
+ this,
+ Key(static_cast(xkb_state_key_get_one_sym(server_->state_.get(), code)), server_->modifiers_),
+ state == WL_KEYBOARD_KEY_STATE_RELEASED, code, time);
+
+ if (!keyEvent(event)) {
+ zwp_input_method_context_v1_key(ic_,
+ serial,
+ time,
+ key,
+ state);
+ }
+}
+void WaylandIMInputContextV1::modifiersCallback(struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
+
+ xkb_mod_mask_t mask;
+
+ xkb_state_update_mask (server_->state_.get(), mods_depressed,
+ mods_latched, mods_locked, 0, 0, group);
+ mask = xkb_state_serialize_mods (server_->state_.get(),
+ static_cast(XKB_STATE_DEPRESSED |
+ XKB_STATE_LATCHED));
+
+ server_->modifiers_ = 0;
+ if (mask & server_->stateMask_.shift_mask)
+ server_->modifiers_ |= KeyState::Shift;
+ if (mask & server_->stateMask_.lock_mask)
+ server_->modifiers_ |= KeyState::CapsLock;
+ if (mask & server_->stateMask_.control_mask)
+ server_->modifiers_ |= KeyState::Ctrl;
+ if (mask & server_->stateMask_.mod1_mask)
+ server_->modifiers_ |= KeyState::Alt;
+ if (mask & server_->stateMask_.super_mask)
+ server_->modifiers_ |= KeyState::Super;
+ if (mask & server_->stateMask_.hyper_mask)
+ server_->modifiers_ |= KeyState::Hyper;
+ if (mask & server_->stateMask_.meta_mask)
+ server_->modifiers_ |= KeyState::Meta;
+
+ zwp_input_method_context_v1_modifiers(ic_, serial,
+ mods_depressed, mods_depressed,
+ mods_latched, group);
+}
+
+WaylandIMModule::WaylandIMModule(Instance *instance)
+ : instance_(instance), createdCallback_(wayland()->call(
+ [this](const std::string &name, wl_display *display, FocusGroup *group) {
+ WaylandIMServer *server = new WaylandIMServer(display, group, name, this);
+ servers_[name].reset(server);
+ })),
+ closedCallback_(wayland()->call(
+ [this](const std::string &name, wl_display *) { servers_.erase(name); })) {}
+
+AddonInstance *WaylandIMModule::wayland() {
+ auto &addonManager = instance_->addonManager();
+ return addonManager.addon("wayland");
+}
+
+WaylandIMModule::~WaylandIMModule() {}
+}
diff --git a/src/frontend/waylandim/waylandim.h b/src/frontend/waylandim/waylandim.h
new file mode 100644
index 000000000..8abe18f52
--- /dev/null
+++ b/src/frontend/waylandim/waylandim.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016~2016 by CSSlayer
+ * wengxt@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING. If not,
+ * see .
+ */
+#ifndef _FCITX_FRONTEND_WAYLANDIM_WAYLANDIM_H_
+#define _FCITX_FRONTEND_WAYLANDIM_WAYLANDIM_H_
+
+#include
+#include
+#include
+#include
+#include "modules/wayland/wayland_public.h"
+#include "wayland-input-method-unstable-v1-client-protocol.h"
+
+namespace fcitx {
+
+class WaylandIMModule;
+class WaylandIMServer;
+
+class WaylandIMModule : public AddonInstance {
+public:
+ WaylandIMModule(Instance *instance);
+ ~WaylandIMModule();
+
+ AddonInstance *wayland();
+ Instance *instance() { return instance_; }
+
+private:
+ Instance *instance_;
+ std::unordered_map> servers_;
+ std::unique_ptr> createdCallback_;
+ std::unique_ptr> closedCallback_;
+};
+
+class WaylandIMModuleFactory : public AddonFactory {
+public:
+ AddonInstance *create(AddonManager *manager) override { return new WaylandIMModule(manager->instance()); }
+};
+}
+
+FCITX_ADDON_FACTORY(fcitx::WaylandIMModuleFactory);
+
+#endif // _FCITX_FRONTEND_WAYLANDIM_WAYLANDIM_H_
diff --git a/src/frontend/xim/xim.cpp b/src/frontend/xim/xim.cpp
index e63e92619..26a040a49 100644
--- a/src/frontend/xim/xim.cpp
+++ b/src/frontend/xim/xim.cpp
@@ -260,7 +260,7 @@ void XIMServer::callback(xcb_im_client_t *client, xcb_im_input_context_t *xic, c
KeyEvent event(
ic, Key(static_cast(xkb_state_key_get_one_sym(xkbState, xevent->detail)), KeyStates(xevent->state)),
(xevent->response_type & ~0x80) == XCB_KEY_RELEASE, xevent->detail, xevent->time);
- std::cout << event.key().toString() << std::endl;
+
if (!ic->keyEvent(event)) {
xcb_im_forward_event(im(), xic, xevent);
}
diff --git a/src/lib/fcitx-utils/utf8.h b/src/lib/fcitx-utils/utf8.h
index 7c797daa8..6d5fb2f0d 100644
--- a/src/lib/fcitx-utils/utf8.h
+++ b/src/lib/fcitx-utils/utf8.h
@@ -30,6 +30,10 @@ FCITXUTILS_EXPORT inline std::string::size_type lengthN(const std::string &s, si
return fcitx_utf8_strnlen(s.c_str(), n);
}
+FCITXUTILS_EXPORT inline bool validate(const std::string &s) {
+ return fcitx_utf8_check_string(s.c_str());
+}
+
FCITXUTILS_EXPORT std::string UCS4ToUTF8(uint32_t code);
FCITXUTILS_EXPORT inline uint32_t getCharValidated(const std::string &s, std::string::size_type off = 0,
diff --git a/src/lib/fcitx/candidatelist.h b/src/lib/fcitx/candidatelist.h
new file mode 100644
index 000000000..0220f4548
--- /dev/null
+++ b/src/lib/fcitx/candidatelist.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016~2016 by CSSlayer
+ * wengxt@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING. If not,
+ * see .
+ */
+#ifndef _FCITX_CANDIDATELIST_H_
+#define _FCITX_CANDIDATELIST_H_
+
+#include
+#include
+
+namespace fcitx
+{
+
+class CandidateWord : public DynamicTrackableObject {
+public:
+ CandidateWord();
+
+ void remove();
+
+ bool isPlaceHolder() const;
+ void setPlaceHolder(bool isPlaceHolder);
+
+ Text &text();
+
+ FCITX_DECLARE_SIGNAL(CandidateWord, Selected, void());
+
+private:
+
+};
+
+class CandidateListPrivate;
+
+class CandidateList {
+public:
+ CandidateList();
+ ~CandidateList();
+
+ CandidateWord &append();
+ CandidateWord &insert(int idx);
+ void clear();
+
+private:
+ std::unique_ptr d_ptr;
+ FCITX_DECLARE_PRIVATE(CandidateList);
+};
+
+}
+
+#endif // _FCITX_CANDIDATELIST_H_
diff --git a/src/lib/fcitx/inputcontext.h b/src/lib/fcitx/inputcontext.h
index 1307c073c..79c098865 100644
--- a/src/lib/fcitx/inputcontext.h
+++ b/src/lib/fcitx/inputcontext.h
@@ -63,6 +63,12 @@ enum class CapabilityFlag : uint64_t {
Alpha = (1 << 21),
Name = (1 << 22),
RelativeRect = (1 << 23),
+ Terminal = (1 << 24),
+ Date = (1 << 25),
+ Time = (1 << 26),
+ Multiline = (1 << 27),
+ Sensitive = (1 << 28),
+ HiddenText = (1 << 29),
};
enum class FocusGroupType { Global, Local, Independent };
diff --git a/src/lib/fcitx/userinterfacemanager.h b/src/lib/fcitx/userinterfacemanager.h
index ce26ee2e5..9a4a21afa 100644
--- a/src/lib/fcitx/userinterfacemanager.h
+++ b/src/lib/fcitx/userinterfacemanager.h
@@ -22,6 +22,7 @@
#include "fcitxcore_export.h"
#include
#include
+#include
#include
namespace fcitx {
@@ -34,6 +35,9 @@ class FCITXCORE_EXPORT UserInterfaceManager {
virtual ~UserInterfaceManager();
void init();
+
+ Menu *mainPanel();
+
private:
std::unique_ptr d_ptr;
diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt
index d14aa4ca1..e337c57ec 100644
--- a/src/modules/CMakeLists.txt
+++ b/src/modules/CMakeLists.txt
@@ -1,2 +1,3 @@
add_subdirectory(xcb)
add_subdirectory(dbus)
+add_subdirectory(wayland)
diff --git a/src/modules/wayland/CMakeLists.txt b/src/modules/wayland/CMakeLists.txt
new file mode 100644
index 000000000..110935d4d
--- /dev/null
+++ b/src/modules/wayland/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_library(wayland MODULE waylandmodule.cpp)
+target_link_libraries(wayland Fcitx5::Core Wayland::Client)
+set_target_properties(wayland PROPERTIES PREFIX "")
+install(TARGETS wayland DESTINATION "${FCITX_INSTALL_ADDONDIR}")
+install(FILES wayland.conf DESTINATION "${FCITX_INSTALL_PKGDATADIR}/addon")
diff --git a/src/modules/wayland/wayland.conf b/src/modules/wayland/wayland.conf
new file mode 100644
index 000000000..b7791a969
--- /dev/null
+++ b/src/modules/wayland/wayland.conf
@@ -0,0 +1,6 @@
+[Addon]
+Name=wayland
+Type=SharedLibrary
+Library=wayland
+Category=Module
+
diff --git a/src/modules/wayland/wayland_public.h b/src/modules/wayland/wayland_public.h
new file mode 100644
index 000000000..c5df93763
--- /dev/null
+++ b/src/modules/wayland/wayland_public.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016~2016 by CSSlayer
+ * wengxt@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING. If not,
+ * see .
+ */
+#ifndef _FCITX_MODULES_WAYLAND_WAYLAND_PUBLIC_H_
+#define _FCITX_MODULES_WAYLAND_WAYLAND_PUBLIC_H_
+
+#include
+#include
+#include
+#include
+#include
+
+namespace fcitx {
+
+typedef std::function WaylandConnectionCreated;
+typedef std::function WaylandConnectionClosed;
+
+}
+
+FCITX_ADDON_DECLARE_FUNCTION(WaylandModule, addConnectionCreatedCallback,
+ HandlerTableEntry *(WaylandConnectionCreated));
+FCITX_ADDON_DECLARE_FUNCTION(WaylandModule, addConnectionClosedCallback,
+ HandlerTableEntry *(WaylandConnectionClosed));
+
+#endif // _FCITX_MODULES_WAYLAND_WAYLAND_PUBLIC_H_
diff --git a/src/modules/wayland/waylandmodule.cpp b/src/modules/wayland/waylandmodule.cpp
new file mode 100644
index 000000000..f510758d1
--- /dev/null
+++ b/src/modules/wayland/waylandmodule.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016~2016 by CSSlayer
+ * wengxt@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING. If not,
+ * see .
+ */
+
+#include "waylandmodule.h"
+#include
+#include
+
+namespace fcitx {
+
+WaylandConnection::WaylandConnection(WaylandModule *wayland, const char *name) :
+ parent_(wayland), name_(name ? name : ""), display_(nullptr, wl_display_disconnect)
+{
+ display_.reset(wl_display_connect(name));
+ if (!display_) {
+ throw std::runtime_error("Failed to open wayland connection");
+ }
+
+ auto &eventLoop = parent_->instance()->eventLoop();
+ ioEvent_.reset(eventLoop.addIOEvent(wl_display_get_fd(display_.get()), IOEventFlag::In, [this](EventSource *, int, IOEventFlags) {
+ onIOEvent();
+ return true;
+ }));
+
+ group_ = new FocusGroup(wayland->instance()->inputContextManager());
+
+}
+
+WaylandConnection::~WaylandConnection() {
+}
+
+void WaylandConnection::onIOEvent() {
+ if (wl_display_dispatch(display_.get()) == -1) {
+ error_ = wl_display_get_error(display_.get());
+ if (error_ != 0) {
+ display_.reset();
+ parent_->removeDisplay(name_);
+ }
+ }
+}
+
+WaylandModule::WaylandModule(fcitx::Instance* instance): instance_(instance) {
+ openDisplay("");
+}
+
+void WaylandModule::openDisplay(const std::string &name) {
+ const char *displayString = nullptr;
+ if (!name.empty()) {
+ displayString = name.c_str();
+ }
+
+ try {
+ auto iter =
+ conns_.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(this, displayString));
+ onConnectionCreated(iter.first->second);
+ } catch (const std::exception &e) {
+ }
+}
+
+void WaylandModule::removeDisplay(const std::string &name) {
+ auto iter = conns_.find(name);
+ if (iter != conns_.end()) {
+ conns_.erase(iter);
+ }
+}
+
+HandlerTableEntry *WaylandModule::addConnectionCreatedCallback(WaylandConnectionCreated callback) {
+ auto result = createdCallbacks_.add(callback);
+
+ for (auto &p : conns_) {
+ auto &conn = p.second;
+ callback(conn.name(), conn.display(), conn.focusGroup());
+ }
+ return result;
+}
+
+HandlerTableEntry *WaylandModule::addConnectionClosedCallback(WaylandConnectionClosed callback) {
+ return closedCallbacks_.add(callback);
+}
+
+void WaylandModule::onConnectionCreated(WaylandConnection &conn) {
+ for (auto &callback : createdCallbacks_.view()) {
+ callback(conn.name(), conn.display(), conn.focusGroup());
+ }
+}
+
+void WaylandModule::onConnectionClosed(WaylandConnection &conn) {
+ for (auto &callback : closedCallbacks_.view()) {
+ callback(conn.name(), conn.display());
+ }
+}
+
+
+
+}
diff --git a/src/modules/wayland/waylandmodule.h b/src/modules/wayland/waylandmodule.h
new file mode 100644
index 000000000..e819e2835
--- /dev/null
+++ b/src/modules/wayland/waylandmodule.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016~2016 by CSSlayer
+ * wengxt@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING. If not,
+ * see .
+ */
+#ifndef _FCITX_MODULES_WAYLAND_WAYLANDMODULE_H_
+#define _FCITX_MODULES_WAYLAND_WAYLANDMODULE_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "wayland_public.h"
+#include
+
+namespace fcitx {
+
+class WaylandModule;
+
+class WaylandConnection {
+public:
+ WaylandConnection(WaylandModule *wayland, const char *name);
+ ~WaylandConnection();
+
+ const std::string &name() const { return name_; }
+ wl_display *display() const { return display_.get(); }
+ FocusGroup *focusGroup() const { return group_; }
+
+private:
+ void onIOEvent();
+
+ WaylandModule *parent_;
+ std::string name_;
+ std::unique_ptr display_;
+ std::unique_ptr ioEvent_;
+ FocusGroup *group_ = nullptr;
+ int error_ = 0;
+};
+
+class WaylandModule : public AddonInstance {
+public:
+ WaylandModule(Instance *instance);
+ Instance *instance() { return instance_; }
+
+ void openDisplay(const std::string &display);
+ void removeDisplay(const std::string &name);
+
+ HandlerTableEntry *addConnectionCreatedCallback(WaylandConnectionCreated callback);
+ HandlerTableEntry *addConnectionClosedCallback(WaylandConnectionClosed callback);
+private:
+ void onConnectionCreated(WaylandConnection &conn);
+ void onConnectionClosed(WaylandConnection &conn);
+
+
+ Instance *instance_;
+ std::unordered_map conns_;
+ HandlerTable createdCallbacks_;
+ HandlerTable closedCallbacks_;
+ FCITX_ADDON_EXPORT_FUNCTION(WaylandModule, addConnectionCreatedCallback);
+ FCITX_ADDON_EXPORT_FUNCTION(WaylandModule, addConnectionClosedCallback);
+};
+
+class WaylandModuleFactory : public AddonFactory {
+public:
+ AddonInstance *create(AddonManager *manager) override { return new WaylandModule(manager->instance()); }
+};
+}
+
+FCITX_ADDON_FACTORY(fcitx::WaylandModuleFactory);
+
+#endif // _FCITX_MODULES_WAYLAND_WAYLANDMODULE_H_