Created
December 10, 2021 13:05
-
-
Save sthairno/594ff542a81db317698dc2d3e1ec89d7 to your computer and use it in GitHub Desktop.
XIMの検証に使ったコード
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <clocale> | |
#include <string> | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
#include <X11/keysym.h> | |
#include <list> | |
#include <cstdlib> | |
#include <vector> | |
#include <sstream> | |
void send_spot(XIC ic, XPoint spot) { | |
XVaNestedList preeditAttr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); | |
XSetICValues(ic, XNPreeditAttributes, preeditAttr, NULL); | |
XFree(preeditAttr); | |
} | |
std::wstring toWstring(const XIMText text) | |
{ | |
if(text.encoding_is_wchar) | |
{ | |
return std::wstring(text.string.wide_char); | |
} | |
else | |
{ | |
auto size = mbstowcs(NULL, text.string.multi_byte, 0); | |
std::wstring wstring(size, 0); | |
mbstowcs(wstring.data(), text.string.multi_byte, size + 1); | |
return wstring; | |
} | |
} | |
template <typename Iterator> | |
std::wstring join(Iterator begin, Iterator end, wchar_t separator = L'.') | |
{ | |
std::wstringstream o; | |
if(begin != end) | |
{ | |
o << *begin++; | |
for(;begin != end; ++begin) | |
o << separator << *begin; | |
} | |
return o.str(); | |
} | |
template <typename Container> | |
std::wstring join(Container const& c, wchar_t separator = L'.') // can pass array as reference, too | |
{ | |
using std::begin; | |
using std::end; | |
return join(begin(c), end(c), separator); | |
// not using std::... directly: | |
// there might be a non-std overload that wouldn't be found if we did | |
} | |
std::wstring preeditText = L""; | |
std::list<XIMFeedback> preeditTextStyle = {0}; | |
//前編集表示開始コールバック | |
//ic | |
// 入力コンテクストを指定する。 | |
//client_data | |
// 追加のクライアントデータを指定する。 | |
//call_data | |
// このコールバックでは使われず、必ず NULL が渡される。 | |
void preeditStartCallback(XIC, XPointer, XPointer) { | |
std::wcout << L"preeditStart" << std::endl; | |
} | |
//前編集描画コールバック | |
//ic | |
// 入力コンテクストを指定する。 | |
//client_data | |
// 追加のクライアントデータを指定する。 | |
//call_data | |
// 前編集の描画に関する情報を指定する。 | |
void preeditDrawCallback(XIC xic, XPointer, XIMPreeditDrawCallbackStruct *call_data) { | |
std::wcout << L"preeditDraw" << L"("; | |
std::wcout << L" caret: " << call_data->caret; | |
std::wcout << L", chg_first: " << call_data->chg_first; | |
std::wcout << L", chg_length: " << call_data->chg_length; | |
if(call_data->text) { | |
const auto& text = toWstring(*call_data->text); | |
std::wcout << L", text: " << text; | |
std::wcout << L", feedback: " << join(call_data->text->feedback, call_data->text->feedback + text.length(), L','); | |
preeditTextStyle.erase(std::next(preeditTextStyle.begin(), call_data->chg_first), std::next(preeditTextStyle.begin(), call_data->chg_first + call_data->chg_length)); | |
preeditTextStyle.insert(std::next(preeditTextStyle.begin(), call_data->chg_first), call_data->text->feedback, call_data->text->feedback + text.length()); | |
preeditText.replace(call_data->chg_first, call_data->chg_length, text); | |
} | |
else | |
{ | |
preeditTextStyle.erase(std::next(preeditTextStyle.begin(), call_data->chg_first), std::next(preeditTextStyle.begin(), call_data->chg_first + call_data->chg_length)); | |
preeditText.erase(call_data->chg_first, call_data->chg_length); | |
} | |
std::wcout << L" )" << std::endl; | |
std::wstring preeditTextwithSpace = preeditText + L" "; | |
std::wstring printText = L"\033[40m"; | |
size_t idx = 0; | |
int currntBgCol = 40; | |
XIMFeedback currentStyle = 0; | |
for(const auto& style : preeditTextStyle) | |
{ | |
int bgCol = call_data->caret == idx ? 47 : 40; | |
if(bgCol != currntBgCol || (style != 0 && style != currentStyle)) | |
{ | |
printText += L"\033[m\033["; | |
printText += std::to_wstring(bgCol); | |
if(style & XIMReverse) | |
{ | |
printText += L";7"; | |
} | |
if(style & XIMUnderline) | |
{ | |
printText += L";4"; | |
} | |
printText += L"m"; | |
currentStyle = style; | |
} | |
printText += preeditTextwithSpace.at(idx); | |
currntBgCol = bgCol; | |
idx++; | |
} | |
printText += L"\033[m"; | |
std::wcout << L"PreeditText: " << printText << std::endl; | |
send_spot(xic, {(short)(preeditText.length() * 10), 0}); | |
} | |
//前編集表示終了コールバック | |
//ic | |
// 入力コールバックを指定する。 | |
//client_data | |
// 追加のクライアントデータを指定する。 | |
//call_data | |
// このコールバックでは使われず、常に NULL を渡すこと。 | |
void preeditDoneCallback(XIC, XPointer, XPointer) { | |
std::wcout << L"preeditDone" << std::endl; | |
} | |
//前編集キャレット制御コールバック | |
void preeditCaretCallback(XIM, XPointer, XPointer) { | |
std::wcout << L"preeditCaret" << std::endl; | |
} | |
//前編集状態通知コールバック | |
//ic | |
// 入力コンテクストを指定する。 | |
//client_data | |
// 追加のクライアントデータを指定する。 | |
//call_data | |
// 現在の前編集状態を指定する。 | |
void preeditStateNotifyCallback(XIC ic, XPointer, XIMPreeditStateNotifyCallbackStruct *call_data) { | |
std::wcout << L"preeditStateNotify" << L"("; | |
if(call_data->state == XIMPreeditUnKnown) | |
{ | |
std::wcout << L" Unknown"; | |
} | |
else | |
{ | |
if(call_data->state & XIMPreeditEnable) | |
{ | |
std::wcout << L" Enable"; | |
} | |
if(call_data->state & XIMPreeditDisable) | |
{ | |
std::wcout << L" Disable"; | |
} | |
} | |
std::wcout << L" )" << std::endl; | |
} | |
//状態表示開始コールバック | |
void statusStartCallback(XIM, XPointer, XPointer) { | |
std::wcout << L"statusStart" << std::endl; | |
} | |
//状態表示描画コールバック | |
void statusDrawCallback(XIM, XPointer, XPointer) { | |
std::wcout << L"statusDraw" << std::endl; | |
} | |
//状態表示表示終了コールバック | |
void statusDoneCallback(XIM, XPointer, XPointer) { | |
std::wcout << L"statusDone" << std::endl; | |
} | |
int main() { | |
std::locale::global( std::locale("ja_JP.UTF-8") ); | |
Display* dpy = XOpenDisplay(NULL); | |
Window win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 100, 100, 0, 0, 0); | |
XMapRaised(dpy, win); | |
XSync(dpy, False); | |
XIMCallback preeditStart = {(XPointer)win, (XIMProc)preeditStartCallback}; | |
XIMCallback preeditDraw = {(XPointer)win, (XIMProc)preeditDrawCallback}; | |
XIMCallback preeditDone = {(XPointer)win, (XIMProc)preeditDoneCallback}; | |
XIMCallback preeditCaret = {(XPointer)win, (XIMProc)preeditCaretCallback}; | |
XIMCallback preeditStateNotify = {(XPointer)win, (XIMProc)preeditStateNotifyCallback}; | |
XIMCallback statusStart = {(XPointer)win, (XIMProc)statusStartCallback}; | |
XIMCallback statusDraw = {(XPointer)win, (XIMProc)statusDrawCallback}; | |
XIMCallback statusDone = {(XPointer)win, (XIMProc)statusDoneCallback}; | |
XSetLocaleModifiers(""); | |
XIM xim = XOpenIM(dpy, 0, 0, 0); | |
if(!xim){ | |
// fallback to internal input method | |
XSetLocaleModifiers("@im=none"); | |
xim = XOpenIM(dpy, 0, 0, 0); | |
} | |
XVaNestedList preeditAttr = XVaCreateNestedList(0, | |
XNPreeditStartCallback, &preeditStart.client_data, //前編集表示開始コールバック | |
XNPreeditDrawCallback, &preeditDraw.client_data, //前編集描画コールバック | |
XNPreeditDoneCallback, &preeditDone.client_data, //前編集表示終了コールバック | |
XNPreeditCaretCallback, &preeditCaret.client_data, //前編集キャレット制御コールバック | |
XNPreeditStateNotifyCallback, &preeditStateNotify.client_data, //前編集状態通知コールバック | |
NULL); | |
XVaNestedList statusAttr = XVaCreateNestedList(0, | |
XNStatusStartCallback, &statusStart.client_data, //状態表示開始コールバック | |
XNStatusDrawCallback, &statusDraw.client_data, //状態表示描画コールバック | |
XNStatusDoneCallback, &statusDone.client_data, //状態表示表示終了コールバック | |
NULL); | |
XIC xic = XCreateIC(xim, | |
XNInputStyle, XIMPreeditCallbacks | XIMStatusCallbacks, | |
XNClientWindow, win, | |
XNFocusWindow, win, | |
XNPreeditAttributes, preeditAttr, | |
XNStatusAttributes, statusAttr, | |
NULL); | |
XFree(preeditAttr); | |
XFree(statusAttr); | |
bool focus = true; | |
XSelectInput(dpy, win, KeyPressMask | KeyReleaseMask); | |
XEvent ev = {}; | |
while(1){ | |
XNextEvent(dpy, &ev); | |
if(XFilterEvent(&ev, None) == True) continue; | |
switch(ev.type){ | |
case KeyPress: { | |
KeySym keysym = NoSymbol; | |
wchar_t text[1024] = {}; | |
XwcLookupString(xic, &ev.xkey, text, sizeof(text) - 1, &keysym, NULL); | |
std::wcout << L"Got chars: (" << text << L")" << std::endl; | |
// example of responding to a key | |
if(keysym == XK_Escape){ | |
std::wcout << L"Exiting because escape was pressed." << std::endl; | |
return 0; | |
} | |
if(keysym == XK_Tab) { | |
if(focus) { | |
XUnsetICFocus(xic); | |
} | |
else { | |
XSetICFocus(xic); | |
} | |
focus = !focus; | |
} | |
} | |
break; | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment