diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a758ba2..556f7625 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,8 +55,18 @@ if (OS_LINUX) include(FindPkgConfig) find_package(PkgConfig) + + option(ENABLE_IBUS "Enable IBus support" On) + option(ENABLE_FCITX "Enable Fcitx support" On) + ## Find iBus ## - pkg_check_modules(IBUS REQUIRED ibus-1.0) + if (ENABLE_IBUS) + pkg_check_modules(IBUS REQUIRED ibus-1.0) + endif() + + if (ENABLE_FCITX) + find_package(Fcitx5Core REQUIRED) + endif() ## Find zstd ## pkg_check_modules(ZSTD REQUIRED libzstd) endif (OS_LINUX) diff --git a/cmake/FindDL.cmake b/cmake/FindDL.cmake new file mode 100644 index 00000000..847c7af8 --- /dev/null +++ b/cmake/FindDL.cmake @@ -0,0 +1,51 @@ +# - find where dlopen and friends are located. +# DL_FOUND - system has dynamic linking interface available +# DL_INCLUDE_DIR - where dlfcn.h is located. +# DL_LIBRARY - libraries needed to use dlopen + +include(CheckFunctionExists) +include(CheckSymbolExists) + +find_path(DL_INCLUDE_DIR NAMES dlfcn.h) +find_library(DL_LIBRARY NAMES dl) +if(DL_LIBRARY) + set(DL_FOUND TRUE) +else(DL_LIBRARY) + check_function_exists(dlopen DL_FOUND) + # If dlopen can be found without linking in dl then dlopen is part + # of libc, so don't need to link extra libs. + set(DL_LIBRARY "") +endif(DL_LIBRARY) + +if (DL_FOUND) + set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) + set(CMAKE_REQUIRED_LIBRARIES ${DL_LIBRARY}) + set(CMAKE_REQUIRED_INCLUDES ${DL_INCLUDE_DIR}) + check_symbol_exists(dlmopen "dlfcn.h" HAS_DLMOPEN) + unset(CMAKE_REQUIRED_DEFINITIONS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_INCLUDES) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DL + FOUND_VAR + DL_FOUND + REQUIRED_VARS + DL_INCLUDE_DIR +) + +mark_as_advanced(DL_INCLUDE_DIR DL_LIBRARY) + +if(DL_FOUND AND NOT TARGET DL::DL) + if (DL_LIBRARY) + add_library(DL::DL UNKNOWN IMPORTED) + set_target_properties(DL::DL PROPERTIES + IMPORTED_LOCATION "${DL_LIBRARY}") + else() + add_library(DL::DL INTERFACE IMPORTED ) + endif() + set_target_properties(DL::DL PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${DL_INCLUDE_DIR}" + ) +endif() diff --git a/cmake/FindPthread.cmake b/cmake/FindPthread.cmake new file mode 100644 index 00000000..349ba894 --- /dev/null +++ b/cmake/FindPthread.cmake @@ -0,0 +1,59 @@ +# Try to find Pthread functionality +# Once done this will define +# +# PTHREAD_FOUND - system has Pthread +# PTHREAD_INCLUDE_DIR - Pthread include directory +# PTHREAD_LIBRARIES - Libraries needed to use Pthread +# +# TODO: This will enable translations only if Gettext functionality is +# present in libc. Must have more robust system for release, where Gettext +# functionality can also reside in standalone Gettext library, or the one +# embedded within kdelibs (cf. gettext.m4 from Gettext source). +# +# Copyright (c) 2006, Chusslove Illich, +# Copyright (c) 2007, Alexander Neundorf, +# Copyright (c) 2016, Xuetian Weng +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +find_path(PTHREAD_INCLUDE_DIR NAMES pthread.h) + +if(PTHREAD_INCLUDE_DIR) + include(CheckFunctionExists) + check_function_exists(pthread_create PTHREAD_LIBC_HAS_PTHREAD_CREATE) + + if (PTHREAD_LIBC_HAS_PTHREAD_CREATE) + set(PTHREAD_LIBRARIES) + set(PTHREAD_LIB_FOUND TRUE) + else (PTHREAD_LIBC_HAS_PTHREAD_CREATE) + find_library(PTHREAD_LIBRARIES NAMES pthread libpthread ) + if(PTHREAD_LIBRARIES) + set(PTHREAD_LIB_FOUND TRUE) + endif(PTHREAD_LIBRARIES) + endif (PTHREAD_LIBC_HAS_PTHREAD_CREATE) + +endif(PTHREAD_INCLUDE_DIR) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Pthread + FOUND_VAR + PTHREAD_FOUND + REQUIRED_VARS + PTHREAD_INCLUDE_DIR PTHREAD_LIB_FOUND +) + +if(PTHREAD_FOUND AND NOT TARGET Pthread::Pthread) + if (PTHREAD_LIBRARIES) + add_library(Pthread::Pthread UNKNOWN IMPORTED) + set_target_properties(Pthread::Pthread PROPERTIES + IMPORTED_LOCATION "${PTHREAD_LIBRARIES}") + else() + add_library(Pthread::Pthread INTERFACE IMPORTED ) + endif() + set_target_properties(Pthread::Pthread PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${PTHREAD_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(PTHREAD_INCLUDE_DIR PTHREAD_LIBRARIES PTHREAD_LIBC_HAS_PTHREAD_CREATE PTHREAD_LIB_FOUND) diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index 0dd08fa8..0764ea14 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -1,7 +1,14 @@ ## Platform depended things if (OS_LINUX) + add_subdirectory(common) ## Platform is Linux - add_subdirectory(ibus) + if (ENABLE_IBUS) + add_subdirectory(ibus) + endif() + + if (ENABLE_FCITX) + add_subdirectory(fcitx) + endif() endif (OS_LINUX) ## Include subdirectories diff --git a/src/engine/common/CMakeLists.txt b/src/engine/common/CMakeLists.txt new file mode 100644 index 00000000..e5381f09 --- /dev/null +++ b/src/engine/common/CMakeLists.txt @@ -0,0 +1,69 @@ +## Install things +install(FILES ${CMAKE_SOURCE_DIR}/data/32.png + DESTINATION ${PROJECT_DATADIR}/icons + RENAME OpenBangla-Keyboard.png) + +install(FILES ${CMAKE_SOURCE_DIR}/data/1024.png + DESTINATION ${DATADIR}/pixmaps/ + RENAME openbangla-keyboard.png) + +install(FILES ${CMAKE_SOURCE_DIR}/data/16.png + DESTINATION ${DATADIR}/icons/hicolor/16x16/apps/ + RENAME openbangla-keyboard.png) + +install(FILES ${CMAKE_SOURCE_DIR}/data/32.png + DESTINATION ${DATADIR}/icons/hicolor/32x32/apps/ + RENAME openbangla-keyboard.png) + +install(FILES ${CMAKE_SOURCE_DIR}/data/48.png + DESTINATION ${DATADIR}/icons/hicolor/48x48/apps/ + RENAME openbangla-keyboard.png) + +install(FILES ${CMAKE_SOURCE_DIR}/data/128.png + DESTINATION ${DATADIR}/icons/hicolor/128x128/apps/ + RENAME openbangla-keyboard.png) + +install(FILES ${CMAKE_SOURCE_DIR}/data/512.png + DESTINATION ${DATADIR}/icons/hicolor/512x512/apps/ + RENAME openbangla-keyboard.png) + +install(FILES ${CMAKE_SOURCE_DIR}/data/1024.png + DESTINATION ${DATADIR}/icons/hicolor/1024x1024/apps/ + RENAME openbangla-keyboard.png) + +install(FILES ${CMAKE_SOURCE_DIR}/data/avrophonetic.json + DESTINATION ${PROJECT_DATADIR}/layouts) + +install(FILES ${CMAKE_SOURCE_DIR}/data/Borno.json + DESTINATION ${PROJECT_DATADIR}/layouts) + +install(FILES ${CMAKE_SOURCE_DIR}/data/Avro_Easy.json + DESTINATION ${PROJECT_DATADIR}/layouts) + +install(FILES ${CMAKE_SOURCE_DIR}/data/Munir_Optima.json + DESTINATION ${PROJECT_DATADIR}/layouts) + +install(FILES ${CMAKE_SOURCE_DIR}/data/National_Jatiya.json + DESTINATION ${PROJECT_DATADIR}/layouts) + +install(FILES ${CMAKE_SOURCE_DIR}/data/Probhat.json + DESTINATION ${PROJECT_DATADIR}/layouts) + +install(FILES ${CMAKE_SOURCE_DIR}/data/autocorrect.json + DESTINATION ${PROJECT_DATADIR}/data) + +install(FILES ${CMAKE_SOURCE_DIR}/data/dictionary.json + DESTINATION ${PROJECT_DATADIR}/data) + +install(FILES ${CMAKE_SOURCE_DIR}/data/suffix.json + DESTINATION ${PROJECT_DATADIR}/data) + +install(FILES ${CMAKE_SOURCE_DIR}/data/regex.json + DESTINATION ${PROJECT_DATADIR}/data) + +install(FILES ${CMAKE_BINARY_DIR}/data/openbangla-keyboard.desktop + DESTINATION "${DATADIR}/applications") + +install(FILES ${CMAKE_SOURCE_DIR}/data/io.github.openbangla.keyboard.metainfo.xml + DESTINATION ${DATADIR}/metainfo) + diff --git a/src/engine/fcitx/CMakeLists.txt b/src/engine/fcitx/CMakeLists.txt new file mode 100644 index 00000000..bfbbf785 --- /dev/null +++ b/src/engine/fcitx/CMakeLists.txt @@ -0,0 +1,12 @@ +include("${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cmake") + +find_package(Pthread REQUIRED) +find_package(DL REQUIRED) + +add_library(openbangla MODULE openbangla.cpp) +target_link_libraries(openbangla Fcitx5::Core riti Pthread::Pthread DL::DL) +target_include_directories(openbangla PRIVATE ../riti/include) +set_target_properties(openbangla PROPERTIES PREFIX "") +install(TARGETS openbangla DESTINATION "${FCITX_INSTALL_LIBDIR}/fcitx5") +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/openbangla.conf" DESTINATION "${FCITX_INSTALL_PKGDATADIR}/inputmethod") +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/openbangla-addon.conf" RENAME openbangla.conf DESTINATION "${FCITX_INSTALL_PKGDATADIR}/addon") diff --git a/src/engine/fcitx/keycode.h b/src/engine/fcitx/keycode.h new file mode 100644 index 00000000..2ac8c876 --- /dev/null +++ b/src/engine/fcitx/keycode.h @@ -0,0 +1,171 @@ +/* + * OpenBangla Keyboard + * Copyright (C) 2015-2020 Muhammad Mominul Huque + * Copyright (C) 2021 CSSlayer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEYCODE_H +#define KEYCODE_H + +#include "riti.h" +#include +#include +#include + +/* Here we map Fcitx keycodes with riti's. */ +constexpr uint16_t VC_UNKNOWN = 0x0046; + +uint16_t keySymToRitiKey(FcitxKeySym k) { + static const std::map key = { + // Begin Alphanumeric Zone + {FcitxKey_grave, VC_GRAVE}, + {FcitxKey_dead_grave, VC_GRAVE}, + {FcitxKey_asciitilde, VC_TILDE}, + + {FcitxKey_0, VC_0}, + {FcitxKey_1, VC_1}, + {FcitxKey_2, VC_2}, + {FcitxKey_3, VC_3}, + {FcitxKey_4, VC_4}, + {FcitxKey_5, VC_5}, + {FcitxKey_6, VC_6}, + {FcitxKey_7, VC_7}, + {FcitxKey_8, VC_8}, + {FcitxKey_9, VC_9}, + + {FcitxKey_parenright, VC_PAREN_RIGHT}, + {FcitxKey_exclam, VC_EXCLAIM}, + {FcitxKey_at, VC_AT}, + {FcitxKey_numbersign, VC_HASH}, + {FcitxKey_dollar, VC_DOLLAR}, + {FcitxKey_percent, VC_PERCENT}, + {FcitxKey_asciicircum, VC_CIRCUM}, + {FcitxKey_ampersand, VC_AMPERSAND}, + {FcitxKey_asterisk, VC_ASTERISK}, + {FcitxKey_parenleft, VC_PAREN_LEFT}, + + {FcitxKey_minus, VC_MINUS}, + {FcitxKey_underscore, VC_UNDERSCORE}, + + {FcitxKey_equal, VC_EQUALS}, + {FcitxKey_plus, VC_PLUS}, + + {FcitxKey_A, VC_A_SHIFT}, + {FcitxKey_B, VC_B_SHIFT}, + {FcitxKey_C, VC_C_SHIFT}, + {FcitxKey_D, VC_D_SHIFT}, + {FcitxKey_E, VC_E_SHIFT}, + {FcitxKey_F, VC_F_SHIFT}, + {FcitxKey_G, VC_G_SHIFT}, + {FcitxKey_H, VC_H_SHIFT}, + {FcitxKey_I, VC_I_SHIFT}, + {FcitxKey_J, VC_J_SHIFT}, + {FcitxKey_K, VC_K_SHIFT}, + {FcitxKey_L, VC_L_SHIFT}, + {FcitxKey_M, VC_M_SHIFT}, + {FcitxKey_N, VC_N_SHIFT}, + {FcitxKey_O, VC_O_SHIFT}, + {FcitxKey_P, VC_P_SHIFT}, + {FcitxKey_Q, VC_Q_SHIFT}, + {FcitxKey_R, VC_R_SHIFT}, + {FcitxKey_S, VC_S_SHIFT}, + {FcitxKey_T, VC_T_SHIFT}, + {FcitxKey_U, VC_U_SHIFT}, + {FcitxKey_V, VC_V_SHIFT}, + {FcitxKey_W, VC_W_SHIFT}, + {FcitxKey_X, VC_X_SHIFT}, + {FcitxKey_Y, VC_Y_SHIFT}, + {FcitxKey_Z, VC_Z_SHIFT}, + + {FcitxKey_a, VC_A}, + {FcitxKey_b, VC_B}, + {FcitxKey_c, VC_C}, + {FcitxKey_d, VC_D}, + {FcitxKey_e, VC_E}, + {FcitxKey_f, VC_F}, + {FcitxKey_g, VC_G}, + {FcitxKey_h, VC_H}, + {FcitxKey_i, VC_I}, + {FcitxKey_j, VC_J}, + {FcitxKey_k, VC_K}, + {FcitxKey_l, VC_L}, + {FcitxKey_m, VC_M}, + {FcitxKey_n, VC_N}, + {FcitxKey_o, VC_O}, + {FcitxKey_p, VC_P}, + {FcitxKey_q, VC_Q}, + {FcitxKey_r, VC_R}, + {FcitxKey_s, VC_S}, + {FcitxKey_t, VC_T}, + {FcitxKey_u, VC_U}, + {FcitxKey_v, VC_V}, + {FcitxKey_w, VC_W}, + {FcitxKey_x, VC_X}, + {FcitxKey_y, VC_Y}, + {FcitxKey_z, VC_Z}, + + {FcitxKey_bracketleft, VC_BRACKET_LEFT}, + {FcitxKey_braceleft, VC_BRACE_LEFT}, + + {FcitxKey_bracketright, VC_BRACKET_RIGHT}, + {FcitxKey_braceright, VC_BRACE_RIGHT}, + + {FcitxKey_backslash, VC_BACK_SLASH}, + {FcitxKey_bar, VC_BAR}, + + {FcitxKey_slash, VC_SLASH}, + {FcitxKey_question, VC_QUESTION}, + + {FcitxKey_semicolon, VC_SEMICOLON}, + {FcitxKey_colon, VC_COLON}, + + {FcitxKey_comma, VC_COMMA}, + {FcitxKey_less, VC_LESS}, + + {FcitxKey_period, VC_PERIOD}, + {FcitxKey_greater, VC_GREATER}, + + {FcitxKey_apostrophe, VC_APOSTROPHE}, + {FcitxKey_quotedbl, VC_QUOTE}, + + // Begin Numeric Zone + {FcitxKey_KP_Divide, VC_KP_DIVIDE}, + {FcitxKey_KP_Multiply, VC_KP_MULTIPLY}, + {FcitxKey_KP_Subtract, VC_KP_SUBTRACT}, + {FcitxKey_KP_Equal, VC_KP_EQUALS}, + {FcitxKey_KP_Add, VC_KP_ADD}, + {FcitxKey_KP_Enter, VC_KP_ENTER}, + {FcitxKey_KP_Decimal, VC_KP_DECIMAL}, + + {FcitxKey_KP_1, VC_KP_1}, + {FcitxKey_KP_2, VC_KP_2}, + {FcitxKey_KP_3, VC_KP_3}, + {FcitxKey_KP_4, VC_KP_4}, + {FcitxKey_KP_5, VC_KP_5}, + {FcitxKey_KP_6, VC_KP_6}, + {FcitxKey_KP_7, VC_KP_7}, + {FcitxKey_KP_8, VC_KP_8}, + {FcitxKey_KP_9, VC_KP_9}, + {FcitxKey_KP_0, VC_KP_0}, + // End Numeric Zone + }; + if (auto iter = key.find(k); iter != key.end()) { + return iter->second; + } + return VC_UNKNOWN; +} + +#endif /* end of include guard: KEYCODE_H */ diff --git a/src/engine/fcitx/openbangla-addon.conf b/src/engine/fcitx/openbangla-addon.conf new file mode 100644 index 00000000..123221c3 --- /dev/null +++ b/src/engine/fcitx/openbangla-addon.conf @@ -0,0 +1,7 @@ +[Addon] +Name=OpenBangla Input Method For Fcitx +Category=InputMethod +Library=openbangla +Type=SharedLibrary +OnDemand=True +Configurable=True diff --git a/src/engine/fcitx/openbangla.conf b/src/engine/fcitx/openbangla.conf new file mode 100644 index 00000000..77609101 --- /dev/null +++ b/src/engine/fcitx/openbangla.conf @@ -0,0 +1,7 @@ +[InputMethod] +Name=OpenBangla +Icon=openbangla-keyboard +Label=ও +LangCode=bn +Addon=openbangla +Configurable=True diff --git a/src/engine/fcitx/openbangla.cpp b/src/engine/fcitx/openbangla.cpp new file mode 100644 index 00000000..319ddff6 --- /dev/null +++ b/src/engine/fcitx/openbangla.cpp @@ -0,0 +1,388 @@ +/* + * OpenBangla Keyboard + * Copyright (C) 2012-2012 Yichao Yu + * Copyright (C) 2020-2021 CSSlayer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "openbangla.h" +#include "keycode.h" +#include "riti.h" +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx { + +class OpenBanglaState : public InputContextProperty { +public: + OpenBanglaState(OpenBanglaEngine *engine, InputContext &ic) + : engine_(engine), ic_(&ic), ctx_(riti_context_new()) {} + + ~OpenBanglaState() {} + + class OpenBanglaCandidate : public CandidateWord { + public: + OpenBanglaCandidate(OpenBanglaEngine *engine, std::string text, int index) + : CandidateWord(Text(std::move(text))), engine_(engine), index_(index) { + } + + void select(InputContext *inputContext) const override { + auto state = inputContext->propertyFor(engine_->factory()); + state->selectCandidate(text().toString(), index_); + } + + private: + OpenBanglaEngine *engine_; + int index_; + }; + + void selectCandidate(const std::string &text, int index) { + if (!suggestion_) { + return; + } + + ic_->commitString(text); + riti_context_candidate_committed(ctx_.get(), index); + reset(); + } + + void updatePreedit() { + if (!suggestion_) { + return; + } + + std::string text; + if (!riti_suggestion_is_lonely(suggestion_.get())) { + auto candidateList = ic_->inputPanel().candidateList(); + auto index = candidateList->cursorIndex(); + if (index >= 0 && index < candidateList->size()) { + text = candidateList->candidate(index).text().toString(); + } + } else { + auto txt = riti_suggestion_get_lonely_suggestion(suggestion_.get()); + text = txt; + // TODO: free txt + } + + Text preedit(std::move(text)); + preedit.setCursor(preedit.textLength()); + if (ic_->capabilityFlags().test(CapabilityFlag::Preedit)) { + ic_->inputPanel().setClientPreedit(preedit); + ic_->updatePreedit(); + } + // we don't use input panel preedit here, because the aux text is actually + // enough. + } + + void commit() { + if (!suggestion_) { + return; + } + auto candidateList = ic_->inputPanel().candidateList(); + + std::string text; + if (!riti_suggestion_is_lonely(suggestion_.get())) { + auto candidateList = ic_->inputPanel().candidateList(); + auto index = candidateList->cursorIndex(); + if (index >= 0 && index < candidateList->size()) { + candidateList->candidate(index).select(ic_); + } + } else { + char *txt = riti_suggestion_get_lonely_suggestion(suggestion_.get()); + text = txt; + // TODO: free txt. + ic_->commitString(text); + riti_context_candidate_committed(ctx_.get(), 0); + reset(); + } + } + void updateUI() { + ic_->inputPanel().reset(); + do { + if (!suggestion_) { + break; + } + if (!riti_suggestion_is_lonely(suggestion_.get())) { + char *aux = riti_suggestion_get_auxiliary_text(suggestion_.get()); + if (aux && aux[0]) { + ic_->inputPanel().setAuxUp(Text(std::string(aux))); + } + // TODO: free aux + auto candidateList = std::make_unique(); + auto len = riti_suggestion_get_length(suggestion_.get()); + char *const *suggestions = + riti_suggestion_get_suggestions(suggestion_.get()); + for (decltype(len) i = 0; i < len; i++) { + candidateList->append(engine_, suggestions[i], + i); + } + // TODO: free suggestions + + candidateList->setLayoutHint(*engine_->config().candidateWinHorizontal + ? CandidateLayoutHint::Horizontal + : CandidateLayoutHint::Vertical); + auto index = + riti_suggestion_previously_selected_index(suggestion_.get()); + if (index >= len) { + index = 0; + } + candidateList->setGlobalCursorIndex(index); + ic_->inputPanel().setCandidateList(std::move(candidateList)); + } + updatePreedit(); + } while (0); + ic_->updateUserInterface(UserInterfaceComponent::InputPanel); + } + + void reset() { + altGrPressed_ = false; + suggestion_.reset(); + if (riti_context_ongoing_input_session(ctx_.get())) { + riti_context_finish_input_session(ctx_.get()); + } + ic_->inputPanel().reset(); + ic_->updatePreedit(); + ic_->updateUserInterface(UserInterfaceComponent::InputPanel); + } + + void keyEvent(KeyEvent &keyEvent) { + auto key = keyEvent.rawKey(); + if (keyEvent.isRelease()) { + if (key.sym() == FcitxKey_Alt_R || + key.sym() == FcitxKey_ISO_Level3_Shift) { + altGrPressed_ = false; + } + return; + } + + auto ctx = ctx_.get(); + if (!riti_context_ongoing_input_session(ctx)) { + riti_context_update_engine(ctx); + } + auto candidateList = ic_->inputPanel().candidateList(); + // At first, handle the special keys. + switch (key.sym()) { + case FcitxKey_BackSpace: + if (riti_context_ongoing_input_session(ctx)) { + suggestion_.reset(riti_context_backspace_event(ctx)); + + if (!riti_suggestion_is_empty(suggestion_.get())) { + updateUI(); + } else { + reset(); + } + + return keyEvent.filterAndAccept(); + } + reset(); + return; + case FcitxKey_Return: + if (riti_context_ongoing_input_session(ctx)) { + commit(); + if (*engine_->config().enterKeyClosesPrevWin) { + keyEvent.filterAndAccept(); + } + } + return; + case FcitxKey_space: + case FcitxKey_KP_Enter: + if (riti_context_ongoing_input_session(ctx)) { + commit(); + } + return; + /** Arrow and Tab keys. + * We use the arrow keys and the tab key to move the selection + * in the preview window. So we have to ensure the preview + * window is shown by checking if the current suggestion is + * not a lonely one. Otherwise we don't handle it. + **/ + case FcitxKey_Right: + case FcitxKey_Left: + if (riti_context_ongoing_input_session(ctx)) { + if (*engine_->config().candidateWinHorizontal && + !riti_suggestion_is_lonely(suggestion_.get()) && candidateList && + candidateList->toCursorMovable()) { + if (key.sym() == FcitxKey_Right) { + candidateList->toCursorMovable()->nextCandidate(); + } else { + candidateList->toCursorMovable()->prevCandidate(); + } + updatePreedit(); + + ic_->updateUserInterface(UserInterfaceComponent::InputPanel); + return keyEvent.filterAndAccept(); + } else { + commit(); + } + } + return; + case FcitxKey_Up: + case FcitxKey_Down: + if (riti_context_ongoing_input_session(ctx)) { + if (!*engine_->config().candidateWinHorizontal && + !riti_suggestion_is_lonely(suggestion_.get()) && candidateList && + candidateList->toCursorMovable()) { + if (key.sym() == FcitxKey_Up) { + candidateList->toCursorMovable()->prevCandidate(); + } else { + candidateList->toCursorMovable()->nextCandidate(); + } + updatePreedit(); + ic_->updateUserInterface(UserInterfaceComponent::InputPanel); + keyEvent.filterAndAccept(); + } else { + commit(); + } + } + return; + case FcitxKey_Tab: + if (riti_context_ongoing_input_session(ctx)) { + if (!riti_suggestion_is_lonely(suggestion_.get()) && candidateList && + candidateList->toCursorMovable()) { + candidateList->toCursorMovable()->nextCandidate(); + updatePreedit(); + ic_->updateUserInterface(UserInterfaceComponent::InputPanel); + keyEvent.filterAndAccept(); + } else { + commit(); + } + } + return; + /** Modifier keys **/ + case FcitxKey_Alt_R: + case FcitxKey_ISO_Level3_Shift: + // Keep track of the right Alt key (also known as the AltGr key) + altGrPressed_ = true; + [[fallthrough]]; + case FcitxKey_Shift_L: + case FcitxKey_Shift_R: + case FcitxKey_Control_L: + case FcitxKey_Control_R: + case FcitxKey_Alt_L: + case FcitxKey_Meta_L: + case FcitxKey_Meta_R: + if (riti_context_ongoing_input_session(ctx)) { + keyEvent.filterAndAccept(); + } + return; + default: + break; + } + + uint8_t modifier = 0; + // Set modifiers + if (key.states().test(KeyState::Shift)) { + modifier |= MODIFIER_SHIFT; + } + + auto ctrlKey = key.states().test(KeyState::Ctrl); + auto altKey = key.states().test(KeyState::Alt); + + // Convert the key value into riti's key value. + auto ritiKey = keySymToRitiKey(key.sym()); + + // Reject the key which has only Ctrl or Alt (not the right one) combination + // and riti doesn't handle. + if ((ctrlKey && !altKey) || (!ctrlKey && altKey && !altGrPressed_) || + ritiKey == VC_UNKNOWN) { + if (riti_context_ongoing_input_session(ctx)) { + commit(); + } + return; + } + + // If we have Ctrl and Alt combination or the right Alt, set it as the AltGr + // modifier. + if ((ctrlKey && altKey) || altGrPressed_) { + modifier |= MODIFIER_ALT_GR; + } + + suggestion_.reset(riti_get_suggestion_for_key(ctx, ritiKey, modifier)); + + if (!riti_suggestion_is_empty(suggestion_.get())) { + updateUI(); + keyEvent.filterAndAccept(); + } + } + +private: + OpenBanglaEngine *engine_; + InputContext *ic_; + /* Unfortunately, we have to keep track of the right Alt Key. */ + bool altGrPressed_ = false; + UniqueCPtr ctx_; + UniqueCPtr suggestion_; +}; + +OpenBanglaEngine::OpenBanglaEngine(Instance *instance) + : instance_(instance), factory_([this](InputContext &ic) { + return new OpenBanglaState(this, ic); + }) { + if (!fs::makePath(stringutils::joinPath( + StandardPath::global().userDirectory(StandardPath::Type::Data), + "openbangla-keyboard"))) { + throw std::runtime_error("Failed to create user directory"); + } + reloadConfig(); + instance->inputContextManager().registerProperty("openbanglaState", + &factory_); +} + +OpenBanglaEngine::~OpenBanglaEngine() {} + +void OpenBanglaEngine::keyEvent(const InputMethodEntry &, KeyEvent &keyEvent) { + auto ic = keyEvent.inputContext(); + auto state = ic->propertyFor(&factory_); + state->keyEvent(keyEvent); +} + +void OpenBanglaEngine::reset(const InputMethodEntry &, + InputContextEvent &event) { + auto state = event.inputContext()->propertyFor(&factory_); + state->reset(); +} + +void OpenBanglaEngine::populateConfig() { + setenv("RITI_LAYOUT_FILE", config_.layoutPath->data(), 1); + setenv("RITI_PHONETIC_DATABASE_ON", + *config_.showCWPhonetic ? "true" : "false", 1); + setenv("RITI_PHONETIC_INCLUDE_ENGLISH", + *config_.includeEnglishPrevWin ? "true" : "false", 1); + setenv("RITI_DATABASE_DIR", config_.databasePath->data(), 1); + setenv("RITI_LAYOUT_FIXED_DATABASE_ON", + *config_.showPrevWinFixed ? "true" : "false", 1); + setenv("RITI_LAYOUT_FIXED_VOWEL", + *config_.autoVowelFormFixed ? "true" : "false", 1); + setenv("RITI_LAYOUT_FIXED_CHANDRA", + *config_.autoChandraPosFixed ? "true" : "false", 1); + setenv("RITI_LAYOUT_FIXED_KAR", + *config_.traditionalKarFixed ? "true" : "false", 1); + setenv("RITI_LAYOUT_FIXED_OLD_REPH", *config_.oldReph ? "true" : "false", 1); + setenv("RITI_LAYOUT_FIXED_NUMBERPAD", + *config_.numberPadFixed ? "true" : "false", 1); +} + +void OpenBanglaEngine::reloadConfig() { + readAsIni(config_, "conf/openbangla.conf"); + populateConfig(); +} + +} // namespace fcitx + +FCITX_ADDON_FACTORY(fcitx::OpenBanglaFactory); diff --git a/src/engine/fcitx/openbangla.h b/src/engine/fcitx/openbangla.h new file mode 100644 index 00000000..322edfb2 --- /dev/null +++ b/src/engine/fcitx/openbangla.h @@ -0,0 +1,97 @@ +/* + * OpenBangla Keyboard + * Copyright (C) 2012-2012 Yichao Yu + * Copyright (C) 2020-2021 CSSlayer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _FCITX5_OPENBANGLA_OPENBANGLA_H_ +#define _FCITX5_OPENBANGLA_OPENBANGLA_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx { + +class OpenBanglaState; + +FCITX_CONFIGURATION( + OpenBanglaConfig, + Option enterKeyClosesPrevWin{this, "enterKeyClosesPrevWin", + "enterKeyClosesPrevWin", false}; + Option candidateWinHorizontal{this, "CandidateWinHorizontal", + "CandidateWinHorizontal", true}; + Option layoutPath{ + this, "LayoutPath", "LayoutPath", + "/usr/share/openbangla-keyboard/layouts/avrophonetic.json"}; + Option showCWPhonetic{this, "ShowCWPhonetic", "ShowCWPhonetic", true}; + Option includeEnglishPrevWin{this, "IncludeEnglishPrevWin", + "IncludeEnglishPrevWin", true}; + Option databasePath{this, "databasePath", "DatabasePath", + "/usr/share/openbangla-keyboard/data"}; + Option showPrevWinFixed{this, "ShowPrevWinFixed", "ShowPrevWinFixed", + true}; + Option autoVowelFormFixed{this, "AutoVowelFormFixed", + "AutoVowelFormFixed", true}; + Option autoChandraPosFixed{this, "AutoChandraPosFixed", + "AutoChandraPosFixed", true}; + Option traditionalKarFixed{this, "TraditionalKarFixed", + "TraditionalKarFixed", false}; + Option oldReph{this, "OldReph", "OldReph", true}; + Option numberPadFixed{this, "NumberPadFixed", "NumberPadFixed", + true};); + +class OpenBanglaEngine final : public InputMethodEngine { +public: + OpenBanglaEngine(Instance *instance); + ~OpenBanglaEngine(); + + void keyEvent(const InputMethodEntry &entry, KeyEvent &keyEvent) override; + void reset(const InputMethodEntry &entry, InputContextEvent &event) override; + + const auto &config() const { return config_; } + auto *factory() { return &factory_; } + void reloadConfig() override; + + const Configuration *getConfig() const override { return &config_; } + void setConfig(const RawConfig &config) override { + config_.load(config, true); + safeSaveAsIni(config_, "conf/openbangla.conf"); + populateConfig(); + } + +private: + void populateConfig(); + + Instance *instance_; + OpenBanglaConfig config_; + FactoryFor factory_; +}; + +class OpenBanglaFactory : public AddonFactory { +public: + AddonInstance *create(AddonManager *manager) override { + return new OpenBanglaEngine(manager->instance()); + } +}; + +} // namespace fcitx + +#endif // _FCITX5_OPENBANGLA_OPENBANGLA_H_ diff --git a/src/engine/ibus/CMakeLists.txt b/src/engine/ibus/CMakeLists.txt index 1ba0eef6..9e3b16e5 100644 --- a/src/engine/ibus/CMakeLists.txt +++ b/src/engine/ibus/CMakeLists.txt @@ -4,79 +4,9 @@ target_link_libraries(ibus-openbangla riti libShared Qt5::Core ${IBUS_LIBRARIES} ## Configure the files configure_file(${CMAKE_SOURCE_DIR}/data/openbangla.xml.in ${CMAKE_BINARY_DIR}/data/openbangla.xml) -configure_file(${CMAKE_SOURCE_DIR}/data/openbangla-keyboard.desktop.in ${CMAKE_BINARY_DIR}/data/openbangla-keyboard.desktop) - -## Install things -install(FILES ${CMAKE_SOURCE_DIR}/data/32.png - DESTINATION ${PROJECT_DATADIR}/icons - RENAME OpenBangla-Keyboard.png) - -install(FILES ${CMAKE_SOURCE_DIR}/data/1024.png - DESTINATION ${DATADIR}/pixmaps/ - RENAME openbangla-keyboard.png) - -install(FILES ${CMAKE_SOURCE_DIR}/data/16.png - DESTINATION ${DATADIR}/icons/hicolor/16x16/apps/ - RENAME openbangla-keyboard.png) - -install(FILES ${CMAKE_SOURCE_DIR}/data/32.png - DESTINATION ${DATADIR}/icons/hicolor/32x32/apps/ - RENAME openbangla-keyboard.png) - -install(FILES ${CMAKE_SOURCE_DIR}/data/48.png - DESTINATION ${DATADIR}/icons/hicolor/48x48/apps/ - RENAME openbangla-keyboard.png) - -install(FILES ${CMAKE_SOURCE_DIR}/data/128.png - DESTINATION ${DATADIR}/icons/hicolor/128x128/apps/ - RENAME openbangla-keyboard.png) - -install(FILES ${CMAKE_SOURCE_DIR}/data/512.png - DESTINATION ${DATADIR}/icons/hicolor/512x512/apps/ - RENAME openbangla-keyboard.png) - -install(FILES ${CMAKE_SOURCE_DIR}/data/1024.png - DESTINATION ${DATADIR}/icons/hicolor/1024x1024/apps/ - RENAME openbangla-keyboard.png) - -install(FILES ${CMAKE_SOURCE_DIR}/data/avrophonetic.json - DESTINATION ${PROJECT_DATADIR}/layouts) - -install(FILES ${CMAKE_SOURCE_DIR}/data/Borno.json - DESTINATION ${PROJECT_DATADIR}/layouts) - -install(FILES ${CMAKE_SOURCE_DIR}/data/Avro_Easy.json - DESTINATION ${PROJECT_DATADIR}/layouts) - -install(FILES ${CMAKE_SOURCE_DIR}/data/Munir_Optima.json - DESTINATION ${PROJECT_DATADIR}/layouts) - -install(FILES ${CMAKE_SOURCE_DIR}/data/National_Jatiya.json - DESTINATION ${PROJECT_DATADIR}/layouts) - -install(FILES ${CMAKE_SOURCE_DIR}/data/Probhat.json - DESTINATION ${PROJECT_DATADIR}/layouts) - -install(FILES ${CMAKE_SOURCE_DIR}/data/autocorrect.json - DESTINATION ${PROJECT_DATADIR}/data) - -install(FILES ${CMAKE_SOURCE_DIR}/data/dictionary.json - DESTINATION ${PROJECT_DATADIR}/data) - -install(FILES ${CMAKE_SOURCE_DIR}/data/suffix.json - DESTINATION ${PROJECT_DATADIR}/data) - -install(FILES ${CMAKE_SOURCE_DIR}/data/regex.json - DESTINATION ${PROJECT_DATADIR}/data) install(FILES ${CMAKE_BINARY_DIR}/data/openbangla.xml DESTINATION "${DATADIR}/ibus/component") -install(FILES ${CMAKE_BINARY_DIR}/data/openbangla-keyboard.desktop - DESTINATION "${DATADIR}/applications") - -install(FILES ${CMAKE_SOURCE_DIR}/data/io.github.openbangla.keyboard.metainfo.xml - DESTINATION ${DATADIR}/metainfo) - install(TARGETS ibus-openbangla RUNTIME DESTINATION ${PROJECT_DATADIR})