Skip to content

Instantly share code, notes, and snippets.

@sthairno
Created December 10, 2021 13:05
Show Gist options
  • Save sthairno/594ff542a81db317698dc2d3e1ec89d7 to your computer and use it in GitHub Desktop.
Save sthairno/594ff542a81db317698dc2d3e1ec89d7 to your computer and use it in GitHub Desktop.
XIMの検証に使ったコード
#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