feat(x11): migrate to pure Xlibre + dwm/dmenu/st stack, fix fcitx5-t9 bottom bar, PM8909 PON shutdown, and remove Wayland
This commit is contained in:
+133
-127
@@ -5,64 +5,18 @@
|
||||
#include <fcitx/inputpanel.h>
|
||||
#include <fcitx/text.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <dlfcn.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
|
||||
#include <typeinfo>
|
||||
#include <sys/stat.h>
|
||||
|
||||
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);
|
||||
}
|
||||
const fcitx::Rect kFixedPopupRect(1, 289, 2, 290);
|
||||
constexpr size_t kFixedPreeditChars = 28;
|
||||
|
||||
constexpr int kKey1 = 1;
|
||||
constexpr int kKey2 = 2;
|
||||
@@ -237,24 +191,50 @@ bool charToCtrlKey(char ch, fcitx::KeySym* symOut, uint32_t* xkbCodeOut) {
|
||||
}
|
||||
|
||||
int mapHardwareKey(const fcitx::Key& key) {
|
||||
if (key.states().testAny(fcitx::KeyState::Ctrl_Alt) ||
|
||||
key.states().test(fcitx::KeyState::Super)) {
|
||||
return -1;
|
||||
}
|
||||
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 ||
|
||||
// KEY_NUMERIC_STAR (evdev 522 -> XKB 530, or remapped KEY_KPASTERISK -> XKB 63)
|
||||
if (code == 530 || code == 63 || key.sym() == FcitxKey_asterisk ||
|
||||
key.sym() == FcitxKey_KP_Multiply ||
|
||||
static_cast<uint32_t>(key.sym()) == 0x1008ffaa) {
|
||||
return kKeyStar;
|
||||
}
|
||||
// KEY_NUMERIC_POUND (evdev 523 -> XKB 531)
|
||||
if (code == 531 || key.sym() == FcitxKey_numbersign ||
|
||||
// KEY_NUMERIC_POUND (evdev 523 -> XKB 531, or remapped KEY_KPSLASH -> XKB 106)
|
||||
if (code == 531 || code == 106 || key.sym() == FcitxKey_numbersign ||
|
||||
key.sym() == FcitxKey_KP_Divide ||
|
||||
static_cast<uint32_t>(key.sym()) == 0x1008ffab) {
|
||||
return kKeyPound;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void writeStatusIndicator(const std::string& badge) {
|
||||
const char* runtimeDir = std::getenv("XDG_RUNTIME_DIR");
|
||||
std::string base = (runtimeDir && runtimeDir[0]) ? runtimeDir : "/run/user/10000";
|
||||
std::string statusDir = base + "/sxmo_status/default";
|
||||
::mkdir((base + "/sxmo_status").c_str(), 0700);
|
||||
::mkdir(statusDir.c_str(), 0700);
|
||||
|
||||
std::string modeFile = base + "/sxmo_t9_mode";
|
||||
if (FILE* fp = std::fopen(modeFile.c_str(), "w")) {
|
||||
std::fprintf(fp, "%s\n", badge.c_str());
|
||||
std::fclose(fp);
|
||||
}
|
||||
|
||||
std::string barFile = statusDir + "/005-input-method";
|
||||
if (FILE* fp = std::fopen(barFile.c_str(), "w")) {
|
||||
std::fprintf(fp, "%s\n", badge.c_str());
|
||||
std::fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
T9Engine::T9Engine(fcitx::Instance* instance)
|
||||
@@ -262,6 +242,17 @@ T9Engine::T9Engine(fcitx::Instance* instance)
|
||||
return new T9State(this, &ic);
|
||||
}) {
|
||||
instance->inputContextManager().registerProperty("t9State", &factory);
|
||||
cursorWatcher = instance->watchEvent(
|
||||
fcitx::EventType::InputContextCursorRectChanged,
|
||||
fcitx::EventWatcherPhase::PreInputMethod,
|
||||
[](fcitx::Event& event) {
|
||||
auto& icEvent = static_cast<fcitx::InputContextEvent&>(event);
|
||||
auto* ic = icEvent.inputContext();
|
||||
if (ic && ic->cursorRect() != kFixedPopupRect) {
|
||||
ic->setCursorRect(kFixedPopupRect);
|
||||
}
|
||||
});
|
||||
writeStatusIndicator("[abc]");
|
||||
}
|
||||
|
||||
std::vector<fcitx::InputMethodEntry> T9Engine::listInputMethods() {
|
||||
@@ -273,6 +264,14 @@ std::vector<fcitx::InputMethodEntry> T9Engine::listInputMethods() {
|
||||
return result;
|
||||
}
|
||||
|
||||
void T9Engine::activate(const fcitx::InputMethodEntry& entry,
|
||||
fcitx::InputContextEvent& event) {
|
||||
FCITX_UNUSED(entry);
|
||||
auto* ic = event.inputContext();
|
||||
auto* state = ic->propertyFor(&factory);
|
||||
state->reset();
|
||||
}
|
||||
|
||||
void T9Engine::keyEvent(const fcitx::InputMethodEntry& entry,
|
||||
fcitx::KeyEvent& keyEvent) {
|
||||
FCITX_UNUSED(entry);
|
||||
@@ -296,6 +295,10 @@ void T9Engine::reset(const fcitx::InputMethodEntry& entry,
|
||||
|
||||
FCITX_ADDON_FACTORY(T9EngineFactory);
|
||||
|
||||
void T9State::updateStatusBar() const {
|
||||
writeStatusIndicator("[" + modePrefix() + "]");
|
||||
}
|
||||
|
||||
std::string T9State::modePrefix() const {
|
||||
std::string p;
|
||||
switch (mode) {
|
||||
@@ -520,6 +523,8 @@ void T9State::cycleMode() {
|
||||
mode = T9Mode::Lower;
|
||||
break;
|
||||
}
|
||||
engine->sharedMode = mode;
|
||||
updateStatusBar();
|
||||
showStatusBanner("[Mode: " + modePrefix() + "]");
|
||||
}
|
||||
|
||||
@@ -560,6 +565,7 @@ void T9State::stepCandidate(int delta) {
|
||||
}
|
||||
|
||||
bool T9State::handleKeyEvent(fcitx::KeyEvent& keyEvent) {
|
||||
mode = engine->sharedMode;
|
||||
fcitx::Key key = keyEvent.key();
|
||||
uint32_t code = key.code();
|
||||
fcitx::KeySym sym = key.sym();
|
||||
@@ -654,45 +660,52 @@ bool T9State::handleKeyEvent(fcitx::KeyEvent& keyEvent) {
|
||||
if (releasedId != keyId && releasedId != -1) {
|
||||
return true;
|
||||
}
|
||||
if (keyId == kKeyPound) {
|
||||
if (!activeItems.empty()) {
|
||||
toggleCurrentCase();
|
||||
} else {
|
||||
cycleMode();
|
||||
}
|
||||
return true;
|
||||
if (!activeItems.empty()) {
|
||||
uint64_t delayUs = (activeItems.size() > 10) ? 1800000 : 1500000;
|
||||
scheduleCommit(delayUs);
|
||||
}
|
||||
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);
|
||||
if (pressedKeyId == keyId) {
|
||||
// Ignore X11 auto-repeat key presses while holding the same key
|
||||
return true;
|
||||
}
|
||||
pressedKeyId = keyId;
|
||||
isLongPressHandled = false;
|
||||
startLongPressTimer(keyId);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void T9State::startLongPressTimer(int keyId) {
|
||||
cancelLongPressTimer();
|
||||
longPressTimer = engine->instance->eventLoop().addTimeEvent(
|
||||
CLOCK_MONOTONIC, fcitx::now(CLOCK_MONOTONIC) + 450000, 0,
|
||||
CLOCK_MONOTONIC, fcitx::now(CLOCK_MONOTONIC) + 550000, 0,
|
||||
[this, keyId](fcitx::EventSourceTime*, uint64_t) {
|
||||
onLongPress(keyId);
|
||||
return true;
|
||||
@@ -707,9 +720,11 @@ void T9State::cancelLongPressTimer() {
|
||||
|
||||
void T9State::onLongPress(int keyId) {
|
||||
isLongPressHandled = true;
|
||||
if (!activeItems.empty()) {
|
||||
commit();
|
||||
}
|
||||
// Discard current key's uncommitted multi-tap candidate and commit long-press action
|
||||
cancelCommit();
|
||||
activeItems.clear();
|
||||
lastKeyId = -1;
|
||||
currentIndex = 0;
|
||||
if (keyId >= kKey1 && keyId <= kKey9) {
|
||||
sendItem(makeChar(static_cast<char>('0' + keyId)));
|
||||
resetUI();
|
||||
@@ -722,6 +737,7 @@ void T9State::onLongPress(int keyId) {
|
||||
resetUI();
|
||||
} else {
|
||||
ctrlLatch = !ctrlLatch;
|
||||
updateStatusBar();
|
||||
showStatusBanner(ctrlLatch ? "[Ctrl+ LATCH ON]" : "[Ctrl+ LATCH OFF]");
|
||||
}
|
||||
} else if (keyId == kKeyPound) {
|
||||
@@ -747,7 +763,7 @@ void T9State::handleMultiTap(int keyId) {
|
||||
if (activeItems.empty()) {
|
||||
return;
|
||||
}
|
||||
uint64_t delayUs = (activeItems.size() > 10) ? 750000 : 600000;
|
||||
uint64_t delayUs = (activeItems.size() > 10) ? 1800000 : 1500000;
|
||||
scheduleCommit(delayUs);
|
||||
showPreview(activeItems, currentIndex);
|
||||
}
|
||||
@@ -758,21 +774,20 @@ void T9State::reset() {
|
||||
if (bannerTimer) {
|
||||
bannerTimer.reset();
|
||||
}
|
||||
mode = engine->sharedMode;
|
||||
lastKeyId = -1;
|
||||
pressedKeyId = -1;
|
||||
currentIndex = 0;
|
||||
activeItems.clear();
|
||||
isLongPressHandled = false;
|
||||
ctrlLatch = false;
|
||||
updateStatusBar();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -783,12 +798,16 @@ void T9State::sendItem(const T9Item& item) {
|
||||
fcitx::KeySym cSym = FcitxKey_None;
|
||||
uint32_t cCode = 0;
|
||||
ctrlLatch = false;
|
||||
updateStatusBar();
|
||||
if (charToCtrlKey(item.commitText[0], &cSym, &cCode)) {
|
||||
forwardKeyTap(cSym, fcitx::KeyState::Ctrl, cCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ctrlLatch = false;
|
||||
if (ctrlLatch) {
|
||||
ctrlLatch = false;
|
||||
updateStatusBar();
|
||||
}
|
||||
if (item.sym != FcitxKey_None) {
|
||||
forwardKeyTap(item.sym, item.states, item.xkbCode);
|
||||
} else if (!item.commitText.empty()) {
|
||||
@@ -796,6 +815,8 @@ void T9State::sendItem(const T9Item& item) {
|
||||
if (mode == T9Mode::Shift &&
|
||||
std::isalpha(static_cast<unsigned char>(item.commitText[0]))) {
|
||||
mode = T9Mode::Lower;
|
||||
engine->sharedMode = mode;
|
||||
updateStatusBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -849,19 +870,19 @@ void T9State::showStatusBanner(const std::string& msg) {
|
||||
if (bannerTimer) {
|
||||
bannerTimer.reset();
|
||||
}
|
||||
std::string padded = msg;
|
||||
if (padded.size() < kFixedPreeditChars) {
|
||||
padded.append(kFixedPreeditChars - padded.size(), ' ');
|
||||
}
|
||||
ic->setCursorRect(kFixedPopupRect);
|
||||
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);
|
||||
}
|
||||
text.append(padded);
|
||||
inputPanel.setPreedit(text);
|
||||
ic->updateUserInterface(fcitx::UserInterfaceComponent::InputPanel);
|
||||
bannerTimer = engine->instance->eventLoop().addTimeEvent(
|
||||
CLOCK_MONOTONIC, fcitx::now(CLOCK_MONOTONIC) + 900000, 0,
|
||||
CLOCK_MONOTONIC, fcitx::now(CLOCK_MONOTONIC) + 1500000, 0,
|
||||
[this](fcitx::EventSourceTime*, uint64_t) {
|
||||
resetUI();
|
||||
return true;
|
||||
@@ -873,50 +894,35 @@ void T9State::showPreview(const std::vector<T9Item>& items, int index) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
constexpr int kPageSize = 7;
|
||||
int page = index / kPageSize;
|
||||
int start = page * kPageSize;
|
||||
int end = std::min(start + kPageSize, total);
|
||||
|
||||
std::string preedit = modePrefix() + ": ";
|
||||
if (start > 0) {
|
||||
preedit += "< ";
|
||||
}
|
||||
std::string preedit = modePrefix() + ":";
|
||||
for (int i = start; i < end; ++i) {
|
||||
if (i == index) {
|
||||
preedit += "[";
|
||||
preedit += items[i].label;
|
||||
preedit += "]";
|
||||
} else {
|
||||
preedit += " ";
|
||||
preedit += items[i].label;
|
||||
}
|
||||
if (i < end - 1) {
|
||||
preedit += " ";
|
||||
}
|
||||
}
|
||||
if (end < total) {
|
||||
preedit += " >";
|
||||
if (total > kPageSize) {
|
||||
preedit += (end < total) ? ">" : "<";
|
||||
}
|
||||
if (preedit.size() < kFixedPreeditChars) {
|
||||
preedit.append(kFixedPreeditChars - preedit.size(), ' ');
|
||||
}
|
||||
|
||||
ic->setCursorRect(kFixedPopupRect);
|
||||
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);
|
||||
}
|
||||
inputPanel.setPreedit(text);
|
||||
ic->updateUserInterface(fcitx::UserInterfaceComponent::InputPanel);
|
||||
}
|
||||
|
||||
@@ -46,13 +46,14 @@ class T9State : public fcitx::InputContextProperty {
|
||||
private:
|
||||
std::vector<T9Item> getItemsForKey(int keyId) const;
|
||||
std::string modePrefix() const;
|
||||
void updateStatusBar() 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 scheduleCommit(uint64_t delayUs = 1500000);
|
||||
void commit();
|
||||
void sendItem(const T9Item& item);
|
||||
void forwardKeyTap(fcitx::KeySym sym, fcitx::KeyStates states,
|
||||
@@ -80,14 +81,18 @@ class T9Engine : public fcitx::InputMethodEngineV2 {
|
||||
public:
|
||||
explicit T9Engine(fcitx::Instance* instance);
|
||||
std::vector<fcitx::InputMethodEntry> listInputMethods() override;
|
||||
void activate(const fcitx::InputMethodEntry& entry,
|
||||
fcitx::InputContextEvent& event) override;
|
||||
void keyEvent(const fcitx::InputMethodEntry& entry,
|
||||
fcitx::KeyEvent& keyEvent) override;
|
||||
void reset(const fcitx::InputMethodEntry& entry,
|
||||
fcitx::InputContextEvent& event) override;
|
||||
fcitx::Instance* instance;
|
||||
T9Mode sharedMode = T9Mode::Lower;
|
||||
|
||||
private:
|
||||
fcitx::FactoryFor<T9State> factory;
|
||||
std::unique_ptr<fcitx::HandlerTableEntry<fcitx::EventHandler>> cursorWatcher;
|
||||
};
|
||||
|
||||
class T9EngineFactory : public fcitx::AddonFactory {
|
||||
|
||||
Reference in New Issue
Block a user