init commit
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(fcitx5-t9 VERSION 0.0.1)
|
||||
|
||||
find_package(Fcitx5Core REQUIRED)
|
||||
|
||||
# Setup some compiler option that is generally useful and compatible with Fcitx 5 (C++17)
|
||||
# include("${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cmake")
|
||||
|
||||
add_subdirectory(src)
|
||||
@@ -0,0 +1,12 @@
|
||||
add_library(t9 SHARED t9.cpp)
|
||||
target_link_libraries(t9 PRIVATE Fcitx5::Core)
|
||||
set_target_properties(t9 PROPERTIES PREFIX "")
|
||||
install(TARGETS t9 DESTINATION "${FCITX_INSTALL_LIBDIR}/fcitx5")
|
||||
|
||||
# Addon config file
|
||||
configure_file(t9-addon.conf t9-addon.conf)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/t9-addon.conf" RENAME t9.conf DESTINATION "${FCITX_INSTALL_PKGDATADIR}/addon")
|
||||
|
||||
# Input Method registration file
|
||||
configure_file(t9-inputmethod.conf t9-inputmethod.conf)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/t9-inputmethod.conf" RENAME t9.conf DESTINATION "${FCITX_INSTALL_PKGDATADIR}/inputmethod")
|
||||
@@ -0,0 +1,12 @@
|
||||
[Addon]
|
||||
Name=t9
|
||||
Category=InputMethod
|
||||
Enabled=True
|
||||
Library=t9
|
||||
Type=SharedLibrary
|
||||
OnDemand=False
|
||||
RegisterInputMethod=True
|
||||
Configurable=False
|
||||
|
||||
[Addon/Dependencies]
|
||||
0=core
|
||||
@@ -0,0 +1,7 @@
|
||||
[InputMethod]
|
||||
Name=T9
|
||||
Icon=input-keyboard
|
||||
Label=T9
|
||||
LangCode=en
|
||||
Addon=t9
|
||||
Configurable=False
|
||||
@@ -0,0 +1,922 @@
|
||||
#include "t9.h"
|
||||
|
||||
#include <fcitx-utils/event.h>
|
||||
#include <fcitx/inputcontext.h>
|
||||
#include <fcitx/inputpanel.h>
|
||||
#include <fcitx/text.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <dlfcn.h>
|
||||
#include <string_view>
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
namespace {
|
||||
|
||||
struct wl_proxy;
|
||||
struct wl_interface;
|
||||
|
||||
using WlProxyGetVersionFn = uint32_t (*)(struct wl_proxy*);
|
||||
using WlProxyMarshalFlagsFn = struct wl_proxy* (*)(
|
||||
struct wl_proxy*, uint32_t, const struct wl_interface*, uint32_t, uint32_t,
|
||||
...);
|
||||
|
||||
void sendWaylandVkModifiers(fcitx::InputContext* ic, uint32_t modsDepressed) {
|
||||
if (!ic || ic->frontendName() != std::string_view("wayland_v2")) {
|
||||
return;
|
||||
}
|
||||
static void* wlHandle = dlopen("libwayland-client.so.0", RTLD_LAZY);
|
||||
static auto getVer =
|
||||
wlHandle ? reinterpret_cast<WlProxyGetVersionFn>(
|
||||
dlsym(wlHandle, "wl_proxy_get_version"))
|
||||
: nullptr;
|
||||
static auto marshalFlags =
|
||||
wlHandle ? reinterpret_cast<WlProxyMarshalFlagsFn>(
|
||||
dlsym(wlHandle, "wl_proxy_marshal_flags"))
|
||||
: nullptr;
|
||||
if (!getVer || !marshalFlags) {
|
||||
return;
|
||||
}
|
||||
auto* raw = reinterpret_cast<uint8_t*>(ic);
|
||||
std::string_view typeName(typeid(*ic).name());
|
||||
if (typeName.find("VirtualInputContext") != std::string_view::npos) {
|
||||
raw = *reinterpret_cast<uint8_t**>(raw + 3 * sizeof(void*));
|
||||
if (!raw) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!raw[54]) {
|
||||
return;
|
||||
}
|
||||
void* vkObj = *reinterpret_cast<void**>(raw + 40);
|
||||
if (!vkObj) {
|
||||
return;
|
||||
}
|
||||
auto* proxy = *reinterpret_cast<struct wl_proxy**>(
|
||||
reinterpret_cast<uint8_t*>(vkObj) + 2 * sizeof(void*));
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
constexpr uint32_t kZwpVirtualKeyboardV1ModifiersOpcode = 2;
|
||||
uint32_t ver = getVer(proxy);
|
||||
marshalFlags(proxy, kZwpVirtualKeyboardV1ModifiersOpcode, nullptr, ver, 0,
|
||||
modsDepressed, 0u, 0u, 0u);
|
||||
}
|
||||
|
||||
constexpr int kKey1 = 1;
|
||||
constexpr int kKey2 = 2;
|
||||
constexpr int kKey3 = 3;
|
||||
constexpr int kKey4 = 4;
|
||||
constexpr int kKey5 = 5;
|
||||
constexpr int kKey6 = 6;
|
||||
constexpr int kKey7 = 7;
|
||||
constexpr int kKey8 = 8;
|
||||
constexpr int kKey9 = 9;
|
||||
constexpr int kKey0 = 10;
|
||||
constexpr int kKeyStar = 11;
|
||||
constexpr int kKeyPound = 12;
|
||||
|
||||
T9Item makeChar(char c) {
|
||||
std::string s(1, c);
|
||||
std::string lbl = (c == ' ') ? "SPC" : s;
|
||||
return T9Item{lbl, s, FcitxKey_None, fcitx::KeyState::NoState, 0};
|
||||
}
|
||||
|
||||
std::vector<T9Item> makeCharItems(const std::string& chars) {
|
||||
std::vector<T9Item> out;
|
||||
out.reserve(chars.size());
|
||||
for (char c : chars) {
|
||||
out.push_back(makeChar(c));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
T9Item makeKey(const std::string& label, fcitx::KeySym sym, uint32_t xkbCode,
|
||||
fcitx::KeyStates states = fcitx::KeyState::NoState) {
|
||||
return T9Item{label, "", sym, states, xkbCode};
|
||||
}
|
||||
|
||||
T9Item makeCtrlKey(const std::string& label, fcitx::KeySym sym,
|
||||
uint32_t xkbCode) {
|
||||
return T9Item{label, "", sym, fcitx::KeyState::Ctrl, xkbCode};
|
||||
}
|
||||
|
||||
bool charToCtrlKey(char ch, fcitx::KeySym* symOut, uint32_t* xkbCodeOut) {
|
||||
char lower = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
|
||||
switch (lower) {
|
||||
case 'a':
|
||||
*symOut = FcitxKey_a;
|
||||
*xkbCodeOut = 38;
|
||||
return true;
|
||||
case 'b':
|
||||
*symOut = FcitxKey_b;
|
||||
*xkbCodeOut = 56;
|
||||
return true;
|
||||
case 'c':
|
||||
*symOut = FcitxKey_c;
|
||||
*xkbCodeOut = 54;
|
||||
return true;
|
||||
case 'd':
|
||||
*symOut = FcitxKey_d;
|
||||
*xkbCodeOut = 40;
|
||||
return true;
|
||||
case 'e':
|
||||
*symOut = FcitxKey_e;
|
||||
*xkbCodeOut = 26;
|
||||
return true;
|
||||
case 'f':
|
||||
*symOut = FcitxKey_f;
|
||||
*xkbCodeOut = 41;
|
||||
return true;
|
||||
case 'g':
|
||||
*symOut = FcitxKey_g;
|
||||
*xkbCodeOut = 42;
|
||||
return true;
|
||||
case 'h':
|
||||
*symOut = FcitxKey_h;
|
||||
*xkbCodeOut = 43;
|
||||
return true;
|
||||
case 'i':
|
||||
*symOut = FcitxKey_i;
|
||||
*xkbCodeOut = 31;
|
||||
return true;
|
||||
case 'j':
|
||||
*symOut = FcitxKey_j;
|
||||
*xkbCodeOut = 44;
|
||||
return true;
|
||||
case 'k':
|
||||
*symOut = FcitxKey_k;
|
||||
*xkbCodeOut = 45;
|
||||
return true;
|
||||
case 'l':
|
||||
*symOut = FcitxKey_l;
|
||||
*xkbCodeOut = 46;
|
||||
return true;
|
||||
case 'm':
|
||||
*symOut = FcitxKey_m;
|
||||
*xkbCodeOut = 58;
|
||||
return true;
|
||||
case 'n':
|
||||
*symOut = FcitxKey_n;
|
||||
*xkbCodeOut = 57;
|
||||
return true;
|
||||
case 'o':
|
||||
*symOut = FcitxKey_o;
|
||||
*xkbCodeOut = 32;
|
||||
return true;
|
||||
case 'p':
|
||||
*symOut = FcitxKey_p;
|
||||
*xkbCodeOut = 33;
|
||||
return true;
|
||||
case 'q':
|
||||
*symOut = FcitxKey_q;
|
||||
*xkbCodeOut = 24;
|
||||
return true;
|
||||
case 'r':
|
||||
*symOut = FcitxKey_r;
|
||||
*xkbCodeOut = 27;
|
||||
return true;
|
||||
case 's':
|
||||
*symOut = FcitxKey_s;
|
||||
*xkbCodeOut = 39;
|
||||
return true;
|
||||
case 't':
|
||||
*symOut = FcitxKey_t;
|
||||
*xkbCodeOut = 28;
|
||||
return true;
|
||||
case 'u':
|
||||
*symOut = FcitxKey_u;
|
||||
*xkbCodeOut = 30;
|
||||
return true;
|
||||
case 'v':
|
||||
*symOut = FcitxKey_v;
|
||||
*xkbCodeOut = 55;
|
||||
return true;
|
||||
case 'w':
|
||||
*symOut = FcitxKey_w;
|
||||
*xkbCodeOut = 25;
|
||||
return true;
|
||||
case 'x':
|
||||
*symOut = FcitxKey_x;
|
||||
*xkbCodeOut = 53;
|
||||
return true;
|
||||
case 'y':
|
||||
*symOut = FcitxKey_y;
|
||||
*xkbCodeOut = 29;
|
||||
return true;
|
||||
case 'z':
|
||||
*symOut = FcitxKey_z;
|
||||
*xkbCodeOut = 52;
|
||||
return true;
|
||||
case '[':
|
||||
*symOut = FcitxKey_bracketleft;
|
||||
*xkbCodeOut = 34;
|
||||
return true;
|
||||
case '\\':
|
||||
*symOut = FcitxKey_backslash;
|
||||
*xkbCodeOut = 51;
|
||||
return true;
|
||||
case ']':
|
||||
*symOut = FcitxKey_bracketright;
|
||||
*xkbCodeOut = 35;
|
||||
return true;
|
||||
case '^':
|
||||
case '6':
|
||||
*symOut = FcitxKey_6;
|
||||
*xkbCodeOut = 15;
|
||||
return true;
|
||||
case '_':
|
||||
case '-':
|
||||
*symOut = FcitxKey_minus;
|
||||
*xkbCodeOut = 20;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int mapHardwareKey(const fcitx::Key& key) {
|
||||
uint32_t code = key.code();
|
||||
// XKB keycodes: KEY_1..KEY_9 = 10..18, KEY_0 = 19
|
||||
if (code >= 10 && code <= 19) {
|
||||
return static_cast<int>(code - 9);
|
||||
}
|
||||
// KEY_NUMERIC_STAR (evdev 522 -> XKB 530)
|
||||
if (code == 530 || key.sym() == FcitxKey_asterisk ||
|
||||
static_cast<uint32_t>(key.sym()) == 0x1008ffaa) {
|
||||
return kKeyStar;
|
||||
}
|
||||
// KEY_NUMERIC_POUND (evdev 523 -> XKB 531)
|
||||
if (code == 531 || key.sym() == FcitxKey_numbersign ||
|
||||
static_cast<uint32_t>(key.sym()) == 0x1008ffab) {
|
||||
return kKeyPound;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
T9Engine::T9Engine(fcitx::Instance* instance)
|
||||
: instance(instance), factory([this](fcitx::InputContext& ic) {
|
||||
return new T9State(this, &ic);
|
||||
}) {
|
||||
instance->inputContextManager().registerProperty("t9State", &factory);
|
||||
}
|
||||
|
||||
std::vector<fcitx::InputMethodEntry> T9Engine::listInputMethods() {
|
||||
std::vector<fcitx::InputMethodEntry> result;
|
||||
result.emplace_back("t9", "T9", "en", "t9")
|
||||
.setLabel("T9")
|
||||
.setIcon("input-keyboard")
|
||||
.setConfigurable(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
void T9Engine::keyEvent(const fcitx::InputMethodEntry& entry,
|
||||
fcitx::KeyEvent& keyEvent) {
|
||||
FCITX_UNUSED(entry);
|
||||
FCITX_INFO() << "T9 keyEvent code=" << keyEvent.key().code()
|
||||
<< " sym=" << keyEvent.key().sym()
|
||||
<< " isRelease=" << keyEvent.isRelease();
|
||||
auto* ic = keyEvent.inputContext();
|
||||
auto* state = ic->propertyFor(&factory);
|
||||
if (state->handleKeyEvent(keyEvent)) {
|
||||
keyEvent.filterAndAccept();
|
||||
}
|
||||
}
|
||||
|
||||
void T9Engine::reset(const fcitx::InputMethodEntry& entry,
|
||||
fcitx::InputContextEvent& event) {
|
||||
FCITX_UNUSED(entry);
|
||||
auto* ic = event.inputContext();
|
||||
auto* state = ic->propertyFor(&factory);
|
||||
state->reset();
|
||||
}
|
||||
|
||||
FCITX_ADDON_FACTORY(T9EngineFactory);
|
||||
|
||||
std::string T9State::modePrefix() const {
|
||||
std::string p;
|
||||
switch (mode) {
|
||||
case T9Mode::Lower:
|
||||
p = "abc";
|
||||
break;
|
||||
case T9Mode::Shift:
|
||||
p = "Abc";
|
||||
break;
|
||||
case T9Mode::Upper:
|
||||
p = "ABC";
|
||||
break;
|
||||
case T9Mode::Sym:
|
||||
p = "Sym";
|
||||
break;
|
||||
case T9Mode::Ctrl:
|
||||
p = "Ctl";
|
||||
break;
|
||||
case T9Mode::Num:
|
||||
p = "123";
|
||||
break;
|
||||
}
|
||||
if (ctrlLatch) {
|
||||
p += "+^";
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
std::vector<T9Item> T9State::getItemsForKey(int keyId) const {
|
||||
if (mode == T9Mode::Sym) {
|
||||
switch (keyId) {
|
||||
case kKey1:
|
||||
return makeCharItems(".,!?:;-_1");
|
||||
case kKey2:
|
||||
return makeCharItems("@#$%^&2");
|
||||
case kKey3:
|
||||
return makeCharItems("()[]{}<>3");
|
||||
case kKey4:
|
||||
return makeCharItems("+-*/=_4");
|
||||
case kKey5:
|
||||
return makeCharItems("|\\~`'\"5");
|
||||
case kKey6:
|
||||
return makeCharItems("'\"`~^6");
|
||||
case kKey7:
|
||||
return makeCharItems("/\\|_-+7");
|
||||
case kKey8:
|
||||
return makeCharItems("<>!=?&8");
|
||||
case kKey9:
|
||||
return makeCharItems("$%^&*#@9");
|
||||
case kKey0: {
|
||||
auto v = makeCharItems(" 0");
|
||||
v.push_back(makeKey("Tab", FcitxKey_Tab, 23));
|
||||
v.push_back(makeKey("Ent", FcitxKey_Return, 36));
|
||||
v.push_back(makeKey("Esc", FcitxKey_Escape, 9));
|
||||
return v;
|
||||
}
|
||||
case kKeyStar:
|
||||
return makeCharItems("*#+-=_/\\|~`@$%^&()[]{}<>'\".,!?:;");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == T9Mode::Ctrl) {
|
||||
switch (keyId) {
|
||||
case kKey1:
|
||||
return {
|
||||
makeKey("Esc", FcitxKey_Escape, 9),
|
||||
makeKey("Tab", FcitxKey_Tab, 23),
|
||||
makeKey("Del", FcitxKey_Delete, 119),
|
||||
makeKey("Ins", FcitxKey_Insert, 118),
|
||||
makeKey("Bksp", FcitxKey_BackSpace, 22),
|
||||
};
|
||||
case kKey2:
|
||||
return {
|
||||
makeCtrlKey("^C", FcitxKey_c, 54),
|
||||
makeCtrlKey("^D", FcitxKey_d, 40),
|
||||
makeCtrlKey("^Z", FcitxKey_z, 52),
|
||||
makeCtrlKey("^X", FcitxKey_x, 53),
|
||||
makeCtrlKey("^\\", FcitxKey_backslash, 51),
|
||||
};
|
||||
case kKey3:
|
||||
return {
|
||||
makeCtrlKey("^A", FcitxKey_a, 38),
|
||||
makeCtrlKey("^E", FcitxKey_e, 26),
|
||||
makeCtrlKey("^U", FcitxKey_u, 30),
|
||||
makeCtrlKey("^K", FcitxKey_k, 45),
|
||||
makeCtrlKey("^W", FcitxKey_w, 25),
|
||||
};
|
||||
case kKey4:
|
||||
return {
|
||||
makeCtrlKey("^R", FcitxKey_r, 27),
|
||||
makeCtrlKey("^L", FcitxKey_l, 46),
|
||||
makeCtrlKey("^P", FcitxKey_p, 33),
|
||||
makeCtrlKey("^N", FcitxKey_n, 57),
|
||||
makeCtrlKey("^G", FcitxKey_g, 42),
|
||||
};
|
||||
case kKey5:
|
||||
return {
|
||||
makeCtrlKey("^S", FcitxKey_s, 39),
|
||||
makeCtrlKey("^Q", FcitxKey_q, 24),
|
||||
makeCtrlKey("^O", FcitxKey_o, 32),
|
||||
makeCtrlKey("^T", FcitxKey_t, 28),
|
||||
makeCtrlKey("^Y", FcitxKey_y, 29),
|
||||
};
|
||||
case kKey6:
|
||||
return {
|
||||
makeCtrlKey("^B", FcitxKey_b, 56),
|
||||
makeCtrlKey("^F", FcitxKey_f, 41),
|
||||
makeCtrlKey("^V", FcitxKey_v, 55),
|
||||
makeCtrlKey("^H", FcitxKey_h, 43),
|
||||
makeCtrlKey("^[", FcitxKey_bracketleft, 34),
|
||||
makeCtrlKey("^]", FcitxKey_bracketright, 35),
|
||||
makeCtrlKey("^^", FcitxKey_6, 15),
|
||||
makeCtrlKey("^_", FcitxKey_minus, 20),
|
||||
};
|
||||
case kKey7:
|
||||
return {
|
||||
makeKey("PgUp", FcitxKey_Page_Up, 112),
|
||||
makeKey("PgDn", FcitxKey_Page_Down, 117),
|
||||
makeKey("Home", FcitxKey_Home, 110),
|
||||
makeKey("End", FcitxKey_End, 115),
|
||||
};
|
||||
case kKey8:
|
||||
return {
|
||||
makeKey("Up", FcitxKey_Up, 111),
|
||||
makeKey("Down", FcitxKey_Down, 116),
|
||||
makeKey("Left", FcitxKey_Left, 113),
|
||||
makeKey("Right", FcitxKey_Right, 114),
|
||||
};
|
||||
case kKey9:
|
||||
return {
|
||||
makeKey("F1", FcitxKey_F1, 67), makeKey("F2", FcitxKey_F2, 68),
|
||||
makeKey("F3", FcitxKey_F3, 69), makeKey("F4", FcitxKey_F4, 70),
|
||||
makeKey("F5", FcitxKey_F5, 71), makeKey("F6", FcitxKey_F6, 72),
|
||||
makeKey("F7", FcitxKey_F7, 73), makeKey("F8", FcitxKey_F8, 74),
|
||||
makeKey("F9", FcitxKey_F9, 75), makeKey("F10", FcitxKey_F10, 76),
|
||||
makeKey("F11", FcitxKey_F11, 95), makeKey("F12", FcitxKey_F12, 96),
|
||||
};
|
||||
case kKey0:
|
||||
return {
|
||||
makeChar(' '),
|
||||
makeKey("Tab", FcitxKey_Tab, 23),
|
||||
makeKey("Ent", FcitxKey_Return, 36),
|
||||
makeKey("Esc", FcitxKey_Escape, 9),
|
||||
};
|
||||
case kKeyStar:
|
||||
return {
|
||||
makeKey("Esc", FcitxKey_Escape, 9),
|
||||
makeKey("Tab", FcitxKey_Tab, 23),
|
||||
makeCtrlKey("^C", FcitxKey_c, 54),
|
||||
makeCtrlKey("^D", FcitxKey_d, 40),
|
||||
makeCtrlKey("^Z", FcitxKey_z, 52),
|
||||
makeCtrlKey("^X", FcitxKey_x, 53),
|
||||
makeCtrlKey("^L", FcitxKey_l, 46),
|
||||
makeCtrlKey("^R", FcitxKey_r, 27),
|
||||
makeCtrlKey("^W", FcitxKey_w, 25),
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool upperFirst = (mode == T9Mode::Shift || mode == T9Mode::Upper);
|
||||
switch (keyId) {
|
||||
case kKey1:
|
||||
return makeCharItems(".,-_!?/@:;'\"+=*#$%^&()[]{}<>|\\~`1");
|
||||
case kKey2:
|
||||
return makeCharItems(upperFirst ? "ABC2abc" : "abc2ABC");
|
||||
case kKey3:
|
||||
return makeCharItems(upperFirst ? "DEF3def" : "def3DEF");
|
||||
case kKey4:
|
||||
return makeCharItems(upperFirst ? "GHI4ghi" : "ghi4GHI");
|
||||
case kKey5:
|
||||
return makeCharItems(upperFirst ? "JKL5jkl" : "jkl5JKL");
|
||||
case kKey6:
|
||||
return makeCharItems(upperFirst ? "MNO6mno" : "mno6MNO");
|
||||
case kKey7:
|
||||
return makeCharItems(upperFirst ? "PQRS7pqrs" : "pqrs7PQRS");
|
||||
case kKey8:
|
||||
return makeCharItems(upperFirst ? "TUV8tuv" : "tuv8TUV");
|
||||
case kKey9:
|
||||
return makeCharItems(upperFirst ? "WXYZ9wxyz" : "wxyz9WXYZ");
|
||||
case kKey0: {
|
||||
auto v = makeCharItems(" 0");
|
||||
v.push_back(makeKey("Ent", FcitxKey_Return, 36));
|
||||
v.push_back(makeKey("Tab", FcitxKey_Tab, 23));
|
||||
return v;
|
||||
}
|
||||
case kKeyStar: {
|
||||
auto v = makeCharItems("*#+-=_/\\|~`@$%^&()[]{}<>'\".,!?:;");
|
||||
v.push_back(makeKey("Tab", FcitxKey_Tab, 23));
|
||||
v.push_back(makeKey("Esc", FcitxKey_Escape, 9));
|
||||
v.push_back(makeCtrlKey("^C", FcitxKey_c, 54));
|
||||
v.push_back(makeCtrlKey("^D", FcitxKey_d, 40));
|
||||
v.push_back(makeCtrlKey("^Z", FcitxKey_z, 52));
|
||||
return v;
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void T9State::cycleMode() {
|
||||
switch (mode) {
|
||||
case T9Mode::Lower:
|
||||
mode = T9Mode::Shift;
|
||||
break;
|
||||
case T9Mode::Shift:
|
||||
mode = T9Mode::Upper;
|
||||
break;
|
||||
case T9Mode::Upper:
|
||||
mode = T9Mode::Sym;
|
||||
break;
|
||||
case T9Mode::Sym:
|
||||
mode = T9Mode::Ctrl;
|
||||
break;
|
||||
case T9Mode::Ctrl:
|
||||
mode = T9Mode::Num;
|
||||
break;
|
||||
case T9Mode::Num:
|
||||
mode = T9Mode::Lower;
|
||||
break;
|
||||
}
|
||||
showStatusBanner("[Mode: " + modePrefix() + "]");
|
||||
}
|
||||
|
||||
void T9State::toggleCurrentCase() {
|
||||
if (activeItems.empty() || currentIndex < 0 ||
|
||||
static_cast<size_t>(currentIndex) >= activeItems.size()) {
|
||||
return;
|
||||
}
|
||||
const std::string& cur = activeItems[currentIndex].commitText;
|
||||
if (cur.size() == 1 && std::isalpha(static_cast<unsigned char>(cur[0]))) {
|
||||
char target = std::isupper(static_cast<unsigned char>(cur[0]))
|
||||
? static_cast<char>(
|
||||
std::tolower(static_cast<unsigned char>(cur[0])))
|
||||
: static_cast<char>(
|
||||
std::toupper(static_cast<unsigned char>(cur[0])));
|
||||
for (size_t i = 0; i < activeItems.size(); ++i) {
|
||||
if (activeItems[i].commitText.size() == 1 &&
|
||||
activeItems[i].commitText[0] == target) {
|
||||
currentIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
scheduleCommit();
|
||||
showPreview(activeItems, currentIndex);
|
||||
} else {
|
||||
stepCandidate(4);
|
||||
}
|
||||
}
|
||||
|
||||
void T9State::stepCandidate(int delta) {
|
||||
if (activeItems.empty()) {
|
||||
return;
|
||||
}
|
||||
int n = static_cast<int>(activeItems.size());
|
||||
currentIndex = ((currentIndex + delta) % n + n) % n;
|
||||
scheduleCommit();
|
||||
showPreview(activeItems, currentIndex);
|
||||
}
|
||||
|
||||
bool T9State::handleKeyEvent(fcitx::KeyEvent& keyEvent) {
|
||||
fcitx::Key key = keyEvent.key();
|
||||
uint32_t code = key.code();
|
||||
fcitx::KeySym sym = key.sym();
|
||||
|
||||
// Handle preedit navigation when an uncommitted character is active
|
||||
if (!activeItems.empty()) {
|
||||
if (sym == FcitxKey_BackSpace || code == 22) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
cancelCommit();
|
||||
lastKeyId = -1;
|
||||
currentIndex = 0;
|
||||
activeItems.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (sym == FcitxKey_Left || code == 113) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
stepCandidate(-1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (sym == FcitxKey_Right || code == 114 || sym == FcitxKey_Return ||
|
||||
code == 36) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
commit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (sym == FcitxKey_Up || code == 111) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
toggleCurrentCase();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (sym == FcitxKey_Down || code == 116) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
if (activeItems[currentIndex].commitText.size() == 1 &&
|
||||
std::isalpha(static_cast<unsigned char>(
|
||||
activeItems[currentIndex].commitText[0]))) {
|
||||
toggleCurrentCase();
|
||||
} else {
|
||||
stepCandidate(-4);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == T9Mode::Ctrl) {
|
||||
if (sym == FcitxKey_Up || code == 111) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
forwardKeyTap(FcitxKey_Page_Up, fcitx::KeyState::NoState, 112);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (sym == FcitxKey_Down || code == 116) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
forwardKeyTap(FcitxKey_Page_Down, fcitx::KeyState::NoState, 117);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (sym == FcitxKey_Left || code == 113) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
forwardKeyTap(FcitxKey_Home, fcitx::KeyState::NoState, 110);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (sym == FcitxKey_Right || code == 114) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
forwardKeyTap(FcitxKey_End, fcitx::KeyState::NoState, 115);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int keyId = mapHardwareKey(key);
|
||||
if (keyId < 0) {
|
||||
if (!keyEvent.isRelease()) {
|
||||
commit();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyEvent.isRelease()) {
|
||||
cancelLongPressTimer();
|
||||
int releasedId = pressedKeyId;
|
||||
pressedKeyId = -1;
|
||||
if (isLongPressHandled) {
|
||||
isLongPressHandled = false;
|
||||
return true;
|
||||
}
|
||||
if (releasedId != keyId && releasedId != -1) {
|
||||
return true;
|
||||
}
|
||||
if (keyId == kKeyPound) {
|
||||
if (!activeItems.empty()) {
|
||||
toggleCurrentCase();
|
||||
} else {
|
||||
cycleMode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (mode == T9Mode::Num) {
|
||||
if (bannerTimer) {
|
||||
bannerTimer.reset();
|
||||
resetUI();
|
||||
}
|
||||
if (keyId >= kKey1 && keyId <= kKey9) {
|
||||
sendItem(makeChar(static_cast<char>('0' + keyId)));
|
||||
} else if (keyId == kKey0) {
|
||||
sendItem(makeChar('0'));
|
||||
} else if (keyId == kKeyStar) {
|
||||
sendItem(makeChar('*'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
handleMultiTap(keyId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Key press event
|
||||
if (pressedKeyId != keyId) {
|
||||
pressedKeyId = keyId;
|
||||
isLongPressHandled = false;
|
||||
startLongPressTimer(keyId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void T9State::startLongPressTimer(int keyId) {
|
||||
cancelLongPressTimer();
|
||||
longPressTimer = engine->instance->eventLoop().addTimeEvent(
|
||||
CLOCK_MONOTONIC, fcitx::now(CLOCK_MONOTONIC) + 450000, 0,
|
||||
[this, keyId](fcitx::EventSourceTime*, uint64_t) {
|
||||
onLongPress(keyId);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void T9State::cancelLongPressTimer() {
|
||||
if (longPressTimer) {
|
||||
longPressTimer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void T9State::onLongPress(int keyId) {
|
||||
isLongPressHandled = true;
|
||||
if (!activeItems.empty()) {
|
||||
commit();
|
||||
}
|
||||
if (keyId >= kKey1 && keyId <= kKey9) {
|
||||
sendItem(makeChar(static_cast<char>('0' + keyId)));
|
||||
resetUI();
|
||||
} else if (keyId == kKey0) {
|
||||
sendItem(makeChar('0'));
|
||||
resetUI();
|
||||
} else if (keyId == kKeyStar) {
|
||||
if (mode == T9Mode::Num) {
|
||||
sendItem(makeChar('.'));
|
||||
resetUI();
|
||||
} else {
|
||||
ctrlLatch = !ctrlLatch;
|
||||
showStatusBanner(ctrlLatch ? "[Ctrl+ LATCH ON]" : "[Ctrl+ LATCH OFF]");
|
||||
}
|
||||
} else if (keyId == kKeyPound) {
|
||||
sendItem(makeChar('#'));
|
||||
resetUI();
|
||||
}
|
||||
}
|
||||
|
||||
void T9State::handleMultiTap(int keyId) {
|
||||
if (bannerTimer) {
|
||||
bannerTimer.reset();
|
||||
}
|
||||
if (lastKeyId != -1 && keyId != lastKeyId) {
|
||||
commit();
|
||||
}
|
||||
if (keyId == lastKeyId && !activeItems.empty()) {
|
||||
currentIndex = (currentIndex + 1) % static_cast<int>(activeItems.size());
|
||||
} else {
|
||||
activeItems = getItemsForKey(keyId);
|
||||
currentIndex = 0;
|
||||
lastKeyId = keyId;
|
||||
}
|
||||
if (activeItems.empty()) {
|
||||
return;
|
||||
}
|
||||
uint64_t delayUs = (activeItems.size() > 10) ? 750000 : 600000;
|
||||
scheduleCommit(delayUs);
|
||||
showPreview(activeItems, currentIndex);
|
||||
}
|
||||
|
||||
void T9State::reset() {
|
||||
cancelLongPressTimer();
|
||||
cancelCommit();
|
||||
if (bannerTimer) {
|
||||
bannerTimer.reset();
|
||||
}
|
||||
lastKeyId = -1;
|
||||
pressedKeyId = -1;
|
||||
currentIndex = 0;
|
||||
activeItems.clear();
|
||||
isLongPressHandled = false;
|
||||
ctrlLatch = false;
|
||||
}
|
||||
|
||||
void T9State::forwardKeyTap(fcitx::KeySym sym, fcitx::KeyStates states,
|
||||
uint32_t xkbCode) {
|
||||
if (states.test(fcitx::KeyState::Ctrl)) {
|
||||
constexpr uint32_t kXkbModCtrl = (1u << 2);
|
||||
sendWaylandVkModifiers(ic, kXkbModCtrl);
|
||||
ic->forwardKey(fcitx::Key(sym, fcitx::KeyState::Ctrl, xkbCode), false);
|
||||
sendWaylandVkModifiers(ic, 0u);
|
||||
} else {
|
||||
ic->forwardKey(fcitx::Key(sym, states, xkbCode), false);
|
||||
}
|
||||
}
|
||||
|
||||
void T9State::sendItem(const T9Item& item) {
|
||||
if (ctrlLatch && !item.commitText.empty()) {
|
||||
fcitx::KeySym cSym = FcitxKey_None;
|
||||
uint32_t cCode = 0;
|
||||
ctrlLatch = false;
|
||||
if (charToCtrlKey(item.commitText[0], &cSym, &cCode)) {
|
||||
forwardKeyTap(cSym, fcitx::KeyState::Ctrl, cCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ctrlLatch = false;
|
||||
if (item.sym != FcitxKey_None) {
|
||||
forwardKeyTap(item.sym, item.states, item.xkbCode);
|
||||
} else if (!item.commitText.empty()) {
|
||||
ic->commitString(item.commitText);
|
||||
if (mode == T9Mode::Shift &&
|
||||
std::isalpha(static_cast<unsigned char>(item.commitText[0]))) {
|
||||
mode = T9Mode::Lower;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void T9State::commit() {
|
||||
if (commitTimer) {
|
||||
commitTimer.reset();
|
||||
}
|
||||
if (!activeItems.empty() && currentIndex >= 0 &&
|
||||
static_cast<size_t>(currentIndex) < activeItems.size()) {
|
||||
T9Item chosen = activeItems[currentIndex];
|
||||
activeItems.clear();
|
||||
lastKeyId = -1;
|
||||
currentIndex = 0;
|
||||
resetUI();
|
||||
sendItem(chosen);
|
||||
} else {
|
||||
activeItems.clear();
|
||||
lastKeyId = -1;
|
||||
currentIndex = 0;
|
||||
resetUI();
|
||||
}
|
||||
}
|
||||
|
||||
void T9State::scheduleCommit(uint64_t delayUs) {
|
||||
if (commitTimer) {
|
||||
commitTimer.reset();
|
||||
}
|
||||
commitTimer = engine->instance->eventLoop().addTimeEvent(
|
||||
CLOCK_MONOTONIC, fcitx::now(CLOCK_MONOTONIC) + delayUs, 0,
|
||||
[this](fcitx::EventSourceTime*, uint64_t) {
|
||||
commit();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void T9State::cancelCommit() {
|
||||
if (commitTimer) {
|
||||
commitTimer.reset();
|
||||
}
|
||||
resetUI();
|
||||
}
|
||||
|
||||
void T9State::resetUI() {
|
||||
ic->inputPanel().reset();
|
||||
ic->updatePreedit();
|
||||
ic->updateUserInterface(fcitx::UserInterfaceComponent::InputPanel);
|
||||
}
|
||||
|
||||
void T9State::showStatusBanner(const std::string& msg) {
|
||||
if (bannerTimer) {
|
||||
bannerTimer.reset();
|
||||
}
|
||||
auto& inputPanel = ic->inputPanel();
|
||||
inputPanel.reset();
|
||||
fcitx::Text text;
|
||||
text.append(msg);
|
||||
if (ic->capabilityFlags().test(fcitx::CapabilityFlag::Preedit)) {
|
||||
inputPanel.setClientPreedit(text);
|
||||
ic->updatePreedit();
|
||||
} else {
|
||||
inputPanel.setPreedit(text);
|
||||
}
|
||||
ic->updateUserInterface(fcitx::UserInterfaceComponent::InputPanel);
|
||||
bannerTimer = engine->instance->eventLoop().addTimeEvent(
|
||||
CLOCK_MONOTONIC, fcitx::now(CLOCK_MONOTONIC) + 900000, 0,
|
||||
[this](fcitx::EventSourceTime*, uint64_t) {
|
||||
resetUI();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void T9State::showPreview(const std::vector<T9Item>& items, int index) {
|
||||
int total = static_cast<int>(items.size());
|
||||
if (total == 0) {
|
||||
return;
|
||||
}
|
||||
constexpr int kMaxVisible = 7;
|
||||
int start = 0;
|
||||
int end = total;
|
||||
if (total > kMaxVisible) {
|
||||
start = index - (kMaxVisible / 2);
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
end = start + kMaxVisible;
|
||||
if (end > total) {
|
||||
end = total;
|
||||
start = end - kMaxVisible;
|
||||
}
|
||||
}
|
||||
|
||||
std::string preedit = modePrefix() + ": ";
|
||||
if (start > 0) {
|
||||
preedit += "< ";
|
||||
}
|
||||
for (int i = start; i < end; ++i) {
|
||||
if (i == index) {
|
||||
preedit += "[";
|
||||
preedit += items[i].label;
|
||||
preedit += "]";
|
||||
} else {
|
||||
preedit += items[i].label;
|
||||
}
|
||||
if (i < end - 1) {
|
||||
preedit += " ";
|
||||
}
|
||||
}
|
||||
if (end < total) {
|
||||
preedit += " >";
|
||||
}
|
||||
|
||||
auto& inputPanel = ic->inputPanel();
|
||||
inputPanel.reset();
|
||||
fcitx::Text text;
|
||||
text.append(preedit);
|
||||
if (ic->capabilityFlags().test(fcitx::CapabilityFlag::Preedit)) {
|
||||
inputPanel.setClientPreedit(text);
|
||||
ic->updatePreedit();
|
||||
} else {
|
||||
inputPanel.setPreedit(text);
|
||||
}
|
||||
ic->updateUserInterface(fcitx::UserInterfaceComponent::InputPanel);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef _FCITX5_T9_T9_H_
|
||||
#define _FCITX5_T9_T9_H_
|
||||
|
||||
#include <fcitx-utils/event.h>
|
||||
#include <fcitx-utils/key.h>
|
||||
#include <fcitx-utils/keysym.h>
|
||||
#include <fcitx/addonfactory.h>
|
||||
#include <fcitx/addonmanager.h>
|
||||
#include <fcitx/inputcontext.h>
|
||||
#include <fcitx/inputmethodengine.h>
|
||||
#include <fcitx/instance.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class T9Engine;
|
||||
|
||||
enum class T9Mode {
|
||||
Lower = 0, // abc: lowercase first, then digit, then uppercase
|
||||
Shift = 1, // Abc: uppercase for next character, then auto-reverts to Lower
|
||||
Upper = 2, // ABC: uppercase first, then digit, then lowercase
|
||||
Sym = 3, // Sym: full ASCII punctuation & symbols grouped across 1-9, 0, *
|
||||
Ctrl = 4, // Ctl: terminal control chars (^A-^Z) & navigation/function keys
|
||||
Num = 5 // 123: instant single-tap numeric entry
|
||||
};
|
||||
|
||||
struct T9Item {
|
||||
std::string label;
|
||||
std::string commitText;
|
||||
fcitx::KeySym sym = FcitxKey_None;
|
||||
fcitx::KeyStates states = fcitx::KeyState::NoState;
|
||||
uint32_t xkbCode = 0;
|
||||
};
|
||||
|
||||
class T9State : public fcitx::InputContextProperty {
|
||||
public:
|
||||
T9State(T9Engine* engine, fcitx::InputContext* ic) : engine(engine), ic(ic) {}
|
||||
|
||||
bool handleKeyEvent(fcitx::KeyEvent& keyEvent);
|
||||
void cancelCommit();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::vector<T9Item> getItemsForKey(int keyId) const;
|
||||
std::string modePrefix() const;
|
||||
void cycleMode();
|
||||
void toggleCurrentCase();
|
||||
void stepCandidate(int delta);
|
||||
void startLongPressTimer(int keyId);
|
||||
void cancelLongPressTimer();
|
||||
void onLongPress(int keyId);
|
||||
void scheduleCommit(uint64_t delayUs = 600000);
|
||||
void commit();
|
||||
void sendItem(const T9Item& item);
|
||||
void forwardKeyTap(fcitx::KeySym sym, fcitx::KeyStates states,
|
||||
uint32_t xkbCode);
|
||||
void resetUI();
|
||||
void showPreview(const std::vector<T9Item>& items, int index);
|
||||
void showStatusBanner(const std::string& msg);
|
||||
void handleMultiTap(int keyId);
|
||||
|
||||
T9Engine* engine;
|
||||
fcitx::InputContext* ic;
|
||||
T9Mode mode = T9Mode::Lower;
|
||||
bool ctrlLatch = false;
|
||||
int lastKeyId = -1;
|
||||
int pressedKeyId = -1;
|
||||
int currentIndex = 0;
|
||||
std::vector<T9Item> activeItems;
|
||||
std::unique_ptr<fcitx::EventSourceTime> commitTimer;
|
||||
std::unique_ptr<fcitx::EventSourceTime> longPressTimer;
|
||||
std::unique_ptr<fcitx::EventSourceTime> bannerTimer;
|
||||
bool isLongPressHandled = false;
|
||||
};
|
||||
|
||||
class T9Engine : public fcitx::InputMethodEngineV2 {
|
||||
public:
|
||||
explicit T9Engine(fcitx::Instance* instance);
|
||||
std::vector<fcitx::InputMethodEntry> listInputMethods() override;
|
||||
void keyEvent(const fcitx::InputMethodEntry& entry,
|
||||
fcitx::KeyEvent& keyEvent) override;
|
||||
void reset(const fcitx::InputMethodEntry& entry,
|
||||
fcitx::InputContextEvent& event) override;
|
||||
fcitx::Instance* instance;
|
||||
|
||||
private:
|
||||
fcitx::FactoryFor<T9State> factory;
|
||||
};
|
||||
|
||||
class T9EngineFactory : public fcitx::AddonFactory {
|
||||
public:
|
||||
fcitx::AddonInstance* create(fcitx::AddonManager* manager) override {
|
||||
FCITX_UNUSED(manager);
|
||||
return new T9Engine(manager->instance());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _FCITX5_T9_T9_H_
|
||||
@@ -0,0 +1 @@
|
||||
obj-m += leds-msm8905-torch.o
|
||||
@@ -0,0 +1,258 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Qualcomm MSM8905 / Nokia 8110 4G (nokia-argon) SY7807 Rear Flashlight Driver
|
||||
*
|
||||
* Drives the SY7807 boost LED controller on TLMM GPIO_32 (cci_timer1) using
|
||||
* GCC camss_gp1_clk_src at 150 kHz (50% duty cycle in continuous Torch mode),
|
||||
* matching the hardware sequence from the stock Nokia 8110 4G kernel.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define MSM8905_TLMM_GPIO32_PHYS 0x01020000UL
|
||||
#define MSM8905_TLMM_GPIO32_SIZE 0x1000UL
|
||||
#define GPIO32_CFG_REG 0x0000U
|
||||
#define GPIO32_CFG_CCI_TIMER1 0x0008U /* 2mA, no pull, func=2 (cci_timer1) */
|
||||
#define GPIO32_CFG_GPIO_OFF 0x0001U /* 2mA, pull-down, func=0 (gpio in) */
|
||||
|
||||
#define MSM8905_GCC_CAMSS_PHYS 0x01850000UL
|
||||
#define MSM8905_GCC_CAMSS_SIZE 0x6000UL
|
||||
#define GCC_CAMSS_GP1_CBCR_STOCK 0x0004U /* Stock MSM8909 gcc_camss_gp1_clk CBCR */
|
||||
#define GCC_CAMSS_GP1_CMD_RCGR 0x5000U /* camss_gp1_clk_src CMD_RCGR */
|
||||
#define GCC_CAMSS_GP1_CFG_RCGR 0x5004U /* camss_gp1_clk_src CFG_RCGR */
|
||||
#define GCC_CAMSS_GP1_M_REG 0x5008U /* M = 1 */
|
||||
#define GCC_CAMSS_GP1_N_REG 0x500cU /* ~(128 - 1) = 0xff80 (19.2MHz / 128 = 150kHz) */
|
||||
#define GCC_CAMSS_GP1_D_REG 0x5010U /* ~128 = 0xff7f (50% duty cycle) */
|
||||
#define GCC_CAMSS_GP1_CBCR_MAIN 0x5018U /* Mainline gcc_camss_gp1_clk CBCR */
|
||||
|
||||
#define RCGR_CFG_150KHZ_XO 0x2001U /* Dual-edge M/N:D (mode=2), XO (src=0), div=1 */
|
||||
#define RCGR_M_150KHZ 0x0001U
|
||||
#define RCGR_N_150KHZ 0xff80U
|
||||
#define RCGR_D_TORCH_50PCT 0xff7fU
|
||||
#define RCGR_CMD_ENABLE_UPDATE 0x0003U /* ROOT_EN | CMD_UPDATE */
|
||||
|
||||
/* PM8909 PON registers for PS_HOLD shutdown vs warm reset configuration */
|
||||
#define QPNP_PON_PS_HOLD_RST_CTL 0x085aU
|
||||
#define QPNP_PON_PS_HOLD_RST_CTL2 0x085bU
|
||||
#define QPNP_PON_S2_CNTL_EN 0x80U
|
||||
#define QPNP_PON_RST_TYPE_MASK 0x0fU
|
||||
#define PON_POWER_OFF_WARM_RESET 0x01U
|
||||
#define PON_POWER_OFF_SHUTDOWN 0x04U
|
||||
#define PON_POWER_OFF_HARD_RESET 0x07U
|
||||
|
||||
static void __iomem *tlmm_gpio32_base;
|
||||
static void __iomem *gcc_camss_base;
|
||||
static struct regmap *pon_regmap;
|
||||
static DEFINE_SPINLOCK(torch_lock);
|
||||
static enum led_brightness current_brightness = LED_OFF;
|
||||
|
||||
static void msm8905_torch_hw_enable(enum led_brightness value)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 d_count;
|
||||
u32 d_reg;
|
||||
|
||||
/*
|
||||
* Scale PWM duty cycle up to 50% (d_count = 64 of N = 128) at max brightness
|
||||
* so the SY7807 remains in continuous Torch mode without triggering its
|
||||
* high-current Flash Strobe safety timeout.
|
||||
*/
|
||||
d_count = (u32)value * 64U / 255U;
|
||||
if (d_count < 8U)
|
||||
d_count = 8U;
|
||||
if (d_count > 64U)
|
||||
d_count = 64U;
|
||||
d_reg = (~(2U * d_count)) & 0xffffU;
|
||||
|
||||
spin_lock_irqsave(&torch_lock, flags);
|
||||
|
||||
writel_relaxed(RCGR_CFG_150KHZ_XO, gcc_camss_base + GCC_CAMSS_GP1_CFG_RCGR);
|
||||
writel_relaxed(RCGR_M_150KHZ, gcc_camss_base + GCC_CAMSS_GP1_M_REG);
|
||||
writel_relaxed(RCGR_N_150KHZ, gcc_camss_base + GCC_CAMSS_GP1_N_REG);
|
||||
writel_relaxed(d_reg, gcc_camss_base + GCC_CAMSS_GP1_D_REG);
|
||||
writel_relaxed(RCGR_CMD_ENABLE_UPDATE, gcc_camss_base + GCC_CAMSS_GP1_CMD_RCGR);
|
||||
wmb();
|
||||
|
||||
writel_relaxed(0x1U, gcc_camss_base + GCC_CAMSS_GP1_CBCR_STOCK);
|
||||
writel_relaxed(0x1U, gcc_camss_base + GCC_CAMSS_GP1_CBCR_MAIN);
|
||||
writel_relaxed(GPIO32_CFG_CCI_TIMER1, tlmm_gpio32_base + GPIO32_CFG_REG);
|
||||
wmb();
|
||||
|
||||
current_brightness = value;
|
||||
spin_unlock_irqrestore(&torch_lock, flags);
|
||||
}
|
||||
|
||||
static void msm8905_torch_hw_disable(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&torch_lock, flags);
|
||||
|
||||
writel_relaxed(GPIO32_CFG_GPIO_OFF, tlmm_gpio32_base + GPIO32_CFG_REG);
|
||||
writel_relaxed(0x0U, gcc_camss_base + GCC_CAMSS_GP1_CBCR_STOCK);
|
||||
writel_relaxed(0x0U, gcc_camss_base + GCC_CAMSS_GP1_CBCR_MAIN);
|
||||
writel_relaxed(0x0U, gcc_camss_base + GCC_CAMSS_GP1_CMD_RCGR);
|
||||
wmb();
|
||||
|
||||
current_brightness = LED_OFF;
|
||||
spin_unlock_irqrestore(&torch_lock, flags);
|
||||
}
|
||||
|
||||
static void msm8905_torch_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
if (value == LED_OFF)
|
||||
msm8905_torch_hw_disable();
|
||||
else
|
||||
msm8905_torch_hw_enable(value);
|
||||
}
|
||||
|
||||
static enum led_brightness msm8905_torch_brightness_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
return current_brightness;
|
||||
}
|
||||
|
||||
static struct led_classdev msm8905_torch_led = {
|
||||
.name = "white:torch",
|
||||
.max_brightness = LED_FULL,
|
||||
.brightness_set = msm8905_torch_brightness_set,
|
||||
.brightness_get = msm8905_torch_brightness_get,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
};
|
||||
|
||||
static int msm8905_pon_reboot_notify(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
unsigned int ctl1 = 0;
|
||||
unsigned int ctl2 = 0;
|
||||
unsigned int rst_type;
|
||||
|
||||
msm8905_torch_hw_disable();
|
||||
|
||||
if (!pon_regmap)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (action == SYS_POWER_OFF || action == SYS_HALT)
|
||||
rst_type = PON_POWER_OFF_SHUTDOWN;
|
||||
else
|
||||
rst_type = PON_POWER_OFF_WARM_RESET;
|
||||
|
||||
/*
|
||||
* Match Qualcomm qpnp_pon_system_pwr_off():
|
||||
* 1. Disable S2_CNTL_EN (bit 7 of 0x085b)
|
||||
* 2. Wait >10 sleep clock cycles (~500 us)
|
||||
* 3. Program PS_HOLD_RST_CTL (0x085a) to SHUTDOWN (0x04) or WARM_RESET (0x01)
|
||||
* 4. Re-enable S2_CNTL_EN (bit 7 of 0x085b) and wait ~500 us
|
||||
*/
|
||||
if (regmap_read(pon_regmap, QPNP_PON_PS_HOLD_RST_CTL2, &ctl2) == 0)
|
||||
regmap_write(pon_regmap, QPNP_PON_PS_HOLD_RST_CTL2, ctl2 & ~QPNP_PON_S2_CNTL_EN);
|
||||
else
|
||||
regmap_write(pon_regmap, QPNP_PON_PS_HOLD_RST_CTL2, 0x00U);
|
||||
|
||||
udelay(500);
|
||||
|
||||
if (regmap_read(pon_regmap, QPNP_PON_PS_HOLD_RST_CTL, &ctl1) == 0)
|
||||
ctl1 = (ctl1 & ~QPNP_PON_RST_TYPE_MASK) | rst_type;
|
||||
else
|
||||
ctl1 = rst_type;
|
||||
regmap_write(pon_regmap, QPNP_PON_PS_HOLD_RST_CTL, ctl1);
|
||||
|
||||
udelay(500);
|
||||
|
||||
regmap_write(pon_regmap, QPNP_PON_PS_HOLD_RST_CTL2,
|
||||
(ctl2 & ~QPNP_PON_S2_CNTL_EN) | QPNP_PON_S2_CNTL_EN);
|
||||
udelay(500);
|
||||
|
||||
pr_info("leds-msm8905-torch: configured PM8909 PON PS_HOLD_RST_CTL=0x%02x for %s\n",
|
||||
ctl1, (rst_type == PON_POWER_OFF_SHUTDOWN) ? "shutdown" : "reboot");
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block msm8905_pon_reboot_nb = {
|
||||
.notifier_call = msm8905_pon_reboot_notify,
|
||||
.priority = 128,
|
||||
};
|
||||
|
||||
static void msm8905_pon_lookup_regmap(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct platform_device *pon_pdev;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "qcom,pm8916-pon");
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
pon_pdev = of_find_device_by_node(np);
|
||||
of_node_put(np);
|
||||
if (!pon_pdev)
|
||||
return;
|
||||
|
||||
if (pon_pdev->dev.parent)
|
||||
pon_regmap = dev_get_regmap(pon_pdev->dev.parent, NULL);
|
||||
|
||||
put_device(&pon_pdev->dev);
|
||||
}
|
||||
|
||||
static int __init msm8905_torch_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!of_machine_is_compatible("qcom,msm8905") &&
|
||||
!of_machine_is_compatible("qcom,msm8909") &&
|
||||
!of_machine_is_compatible("nokia,argon")) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tlmm_gpio32_base = ioremap(MSM8905_TLMM_GPIO32_PHYS, MSM8905_TLMM_GPIO32_SIZE);
|
||||
if (!tlmm_gpio32_base)
|
||||
return -ENOMEM;
|
||||
|
||||
gcc_camss_base = ioremap(MSM8905_GCC_CAMSS_PHYS, MSM8905_GCC_CAMSS_SIZE);
|
||||
if (!gcc_camss_base) {
|
||||
iounmap(tlmm_gpio32_base);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
msm8905_torch_hw_disable();
|
||||
|
||||
ret = led_classdev_register(NULL, &msm8905_torch_led);
|
||||
if (ret) {
|
||||
iounmap(gcc_camss_base);
|
||||
iounmap(tlmm_gpio32_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msm8905_pon_lookup_regmap();
|
||||
register_reboot_notifier(&msm8905_pon_reboot_nb);
|
||||
|
||||
pr_info("leds-msm8905-torch: registered white:torch on GPIO_32 (cci_timer1 150kHz PWM), pon_regmap=%s\n",
|
||||
pon_regmap ? "ready" : "none");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit msm8905_torch_exit(void)
|
||||
{
|
||||
unregister_reboot_notifier(&msm8905_pon_reboot_nb);
|
||||
led_classdev_unregister(&msm8905_torch_led);
|
||||
msm8905_torch_hw_disable();
|
||||
iounmap(gcc_camss_base);
|
||||
iounmap(tlmm_gpio32_base);
|
||||
}
|
||||
|
||||
module_init(msm8905_torch_init);
|
||||
module_exit(msm8905_torch_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm MSM8905 / Nokia 8110 4G SY7807 Torch LED & PM8909 PON Shutdown driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:leds-msm8905-torch");
|
||||
Reference in New Issue
Block a user