mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-29 16:20:11 -04:00
x11: Implement ime-aware setting using XIM
This commit is contained in:
parent
4ba3df7082
commit
4cd579d0ba
@ -383,6 +383,11 @@ ConfigVariableFilename subprocess_window
|
|||||||
"and is not used or needed in other environments. See "
|
"and is not used or needed in other environments. See "
|
||||||
"WindowProperties::set_subprocess_window()."));
|
"WindowProperties::set_subprocess_window()."));
|
||||||
|
|
||||||
|
ConfigVariableBool ime_aware
|
||||||
|
("ime-aware", false,
|
||||||
|
PRC_DESC("Set this true to show candidate strings in Panda3D rather than via "
|
||||||
|
"an OS-provided external popup window."));
|
||||||
|
|
||||||
ConfigVariableString framebuffer_mode
|
ConfigVariableString framebuffer_mode
|
||||||
("framebuffer-mode", "",
|
("framebuffer-mode", "",
|
||||||
PRC_DESC("No longer has any effect. Do not use."));
|
PRC_DESC("No longer has any effect. Do not use."));
|
||||||
|
@ -84,6 +84,7 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableString window_title;
|
|||||||
extern EXPCL_PANDA_DISPLAY ConfigVariableInt parent_window_handle;
|
extern EXPCL_PANDA_DISPLAY ConfigVariableInt parent_window_handle;
|
||||||
extern EXPCL_PANDA_DISPLAY ConfigVariableBool win_unexposed_draw;
|
extern EXPCL_PANDA_DISPLAY ConfigVariableBool win_unexposed_draw;
|
||||||
extern EXPCL_PANDA_DISPLAY ConfigVariableFilename subprocess_window;
|
extern EXPCL_PANDA_DISPLAY ConfigVariableFilename subprocess_window;
|
||||||
|
extern EXPCL_PANDA_DISPLAY ConfigVariableBool ime_aware;
|
||||||
|
|
||||||
extern EXPCL_PANDA_DISPLAY ConfigVariableString framebuffer_mode;
|
extern EXPCL_PANDA_DISPLAY ConfigVariableString framebuffer_mode;
|
||||||
extern EXPCL_PANDA_DISPLAY ConfigVariableBool framebuffer_hardware;
|
extern EXPCL_PANDA_DISPLAY ConfigVariableBool framebuffer_hardware;
|
||||||
|
@ -49,12 +49,6 @@ ConfigVariableBool auto_cpu_data
|
|||||||
"require an explicit call to pipe->lookup_cpu_data(). Setting this "
|
"require an explicit call to pipe->lookup_cpu_data(). Setting this "
|
||||||
"true may slow down startup time by 1-2 seconds."));
|
"true may slow down startup time by 1-2 seconds."));
|
||||||
|
|
||||||
ConfigVariableBool ime_aware
|
|
||||||
("ime-aware", false,
|
|
||||||
PRC_DESC("Set this true to show ime texts on the chat panel and hide the "
|
|
||||||
"IME default windows. This is a mechanism to work around DX8/9 "
|
|
||||||
"interface."));
|
|
||||||
|
|
||||||
ConfigVariableBool ime_hide
|
ConfigVariableBool ime_hide
|
||||||
("ime-hide", false,
|
("ime-hide", false,
|
||||||
PRC_DESC("Set this true to hide ime windows."));
|
PRC_DESC("Set this true to hide ime windows."));
|
||||||
|
@ -25,8 +25,6 @@ extern ConfigVariableBool responsive_minimized_fullscreen_window;
|
|||||||
extern ConfigVariableBool hold_keys_across_windows;
|
extern ConfigVariableBool hold_keys_across_windows;
|
||||||
extern ConfigVariableBool do_vidmemsize_check;
|
extern ConfigVariableBool do_vidmemsize_check;
|
||||||
extern ConfigVariableBool auto_cpu_data;
|
extern ConfigVariableBool auto_cpu_data;
|
||||||
extern ConfigVariableBool ime_composition_w;
|
|
||||||
extern ConfigVariableBool ime_aware;
|
|
||||||
extern ConfigVariableBool ime_hide;
|
extern ConfigVariableBool ime_hide;
|
||||||
extern ConfigVariableBool request_dxdisplay_information;
|
extern ConfigVariableBool request_dxdisplay_information;
|
||||||
extern ConfigVariableBool dpi_aware;
|
extern ConfigVariableBool dpi_aware;
|
||||||
|
@ -493,7 +493,8 @@ process_events() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ClientMessage:
|
case ClientMessage:
|
||||||
if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
|
if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window &&
|
||||||
|
event.xany.window == _xwindow) {
|
||||||
// This is a message from the window manager indicating that the user
|
// This is a message from the window manager indicating that the user
|
||||||
// has requested to close the window.
|
// has requested to close the window.
|
||||||
string close_request_event = get_close_request_event();
|
string close_request_event = get_close_request_event();
|
||||||
@ -1182,15 +1183,41 @@ open_window() {
|
|||||||
|
|
||||||
set_wm_properties(_properties, false);
|
set_wm_properties(_properties, false);
|
||||||
|
|
||||||
// We don't specify any fancy properties of the XIC. It would be nicer if
|
// Initialize the input context, which (if enabled) will enable us to capture
|
||||||
// we could support fancy IM's that want preedit callbacks, etc., but that
|
// candidate strings and display them inside Panda3D rather than via an
|
||||||
// can wait until we have an X server that actually supports these to test
|
// external popup window.
|
||||||
// it on.
|
|
||||||
XIM im = x11_pipe->get_im();
|
XIM im = x11_pipe->get_im();
|
||||||
_ic = nullptr;
|
_ic = nullptr;
|
||||||
if (im) {
|
if (im) {
|
||||||
|
if (ime_aware) {
|
||||||
|
XIMCallback start_callback;
|
||||||
|
start_callback.client_data = (XPointer)this;
|
||||||
|
start_callback.callback = (XIMProc)xim_preedit_start;
|
||||||
|
XIMCallback draw_callback;
|
||||||
|
draw_callback.client_data = (XPointer)this;
|
||||||
|
draw_callback.callback = (XIMProc)xim_preedit_draw;
|
||||||
|
XIMCallback caret_callback;
|
||||||
|
caret_callback.client_data = (XPointer)this;
|
||||||
|
caret_callback.callback = (XIMProc)xim_preedit_caret;
|
||||||
|
XIMCallback done_callback;
|
||||||
|
done_callback.client_data = (XPointer)this;
|
||||||
|
done_callback.callback = (XIMProc)xim_preedit_done;
|
||||||
|
XVaNestedList preedit_attributes = XVaCreateNestedList(
|
||||||
|
0,
|
||||||
|
XNPreeditStartCallback, &start_callback,
|
||||||
|
XNPreeditDrawCallback, &draw_callback,
|
||||||
|
XNPreeditCaretCallback, &caret_callback,
|
||||||
|
XNPreeditDoneCallback, &done_callback,
|
||||||
|
nullptr);
|
||||||
|
_ic = XCreateIC(im,
|
||||||
|
XNInputStyle, XIMPreeditCallbacks | XIMStatusNothing,
|
||||||
|
XNClientWindow, _xwindow,
|
||||||
|
XNPreeditAttributes, preedit_attributes,
|
||||||
|
nullptr);
|
||||||
|
} else {
|
||||||
_ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
|
_ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
|
||||||
XNClientWindow, _xwindow, nullptr);
|
XNClientWindow, _xwindow, nullptr);
|
||||||
|
}
|
||||||
if (_ic == (XIC)nullptr) {
|
if (_ic == (XIC)nullptr) {
|
||||||
x11display_cat.warning()
|
x11display_cat.warning()
|
||||||
<< "Couldn't create input context.\n";
|
<< "Couldn't create input context.\n";
|
||||||
@ -1549,6 +1576,123 @@ open_raw_mice() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int x11GraphicsWindow::
|
||||||
|
handle_preedit_start() {
|
||||||
|
_preedit_state = new PreeditState;
|
||||||
|
|
||||||
|
if (x11display_cat.is_spam()) {
|
||||||
|
x11display_cat.spam()
|
||||||
|
<< "Preedit started\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return sizeof(_preedit_state->_buffer) / sizeof(wchar_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void x11GraphicsWindow::
|
||||||
|
handle_preedit_draw(XIMPreeditDrawCallbackStruct &data) {
|
||||||
|
nassertv_always(_preedit_state != nullptr);
|
||||||
|
PreeditState &state = *_preedit_state;
|
||||||
|
|
||||||
|
if (data.text != nullptr) {
|
||||||
|
// Replace characters in the preedit buffer.
|
||||||
|
int added_chars = data.text->length - data.chg_length;
|
||||||
|
memmove(state._buffer + data.chg_first,
|
||||||
|
state._buffer + data.chg_first + data.chg_length,
|
||||||
|
state._length - (size_t)(data.chg_first + data.chg_length) + data.text->length);
|
||||||
|
state._length += added_chars;
|
||||||
|
|
||||||
|
if (added_chars != 0) {
|
||||||
|
if (state._highlight_start > data.chg_first) {
|
||||||
|
state._highlight_start = std::max(data.chg_first, state._highlight_start + added_chars);
|
||||||
|
}
|
||||||
|
if (state._highlight_end > data.chg_first) {
|
||||||
|
state._highlight_end = std::max(data.chg_first, state._highlight_end + added_chars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.text->encoding_is_wchar) {
|
||||||
|
memcpy(state._buffer + data.chg_first, data.text->string.wide_char, data.text->length * sizeof(wchar_t));
|
||||||
|
} else {
|
||||||
|
mbstowcs(state._buffer + data.chg_first, data.text->string.multi_byte, data.text->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.text->feedback != nullptr) {
|
||||||
|
// Update the highlighted region.
|
||||||
|
for (int i = 0; i < data.text->length; ++i) {
|
||||||
|
if (data.text->feedback[i] & XIMReverse) {
|
||||||
|
if (state._highlight_end > state._highlight_start) {
|
||||||
|
state._highlight_start = std::min(state._highlight_start, data.chg_first + i);
|
||||||
|
state._highlight_end = std::max(state._highlight_end, data.chg_first + i + 1);
|
||||||
|
} else {
|
||||||
|
state._highlight_start = data.chg_first + i;
|
||||||
|
state._highlight_end = data.chg_first + i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (state._highlight_end > state._highlight_start) {
|
||||||
|
if (state._highlight_start == data.chg_first + i) {
|
||||||
|
++state._highlight_start;
|
||||||
|
}
|
||||||
|
if (state._highlight_end == data.chg_first + i + 1) {
|
||||||
|
--state._highlight_end;
|
||||||
|
}
|
||||||
|
if (state._highlight_end <= state._highlight_start) {
|
||||||
|
state._highlight_start = 0;
|
||||||
|
state._highlight_end = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Delete characters from the preedit buffer.
|
||||||
|
memmove(state._buffer + data.chg_first,
|
||||||
|
state._buffer + data.chg_first + data.chg_length,
|
||||||
|
state._length - (size_t)(data.chg_first + data.chg_length));
|
||||||
|
state._length -= data.chg_length;
|
||||||
|
|
||||||
|
if (state._highlight_start > data.chg_first) {
|
||||||
|
state._highlight_start = std::max(data.chg_first, state._highlight_start - data.chg_length);
|
||||||
|
}
|
||||||
|
if (state._highlight_end > data.chg_first) {
|
||||||
|
state._highlight_end = std::max(data.chg_first, state._highlight_end - data.chg_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_input->candidate(std::wstring(state._buffer, state._length),
|
||||||
|
state._highlight_start, state._highlight_end, data.caret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void x11GraphicsWindow::
|
||||||
|
handle_preedit_caret(XIMPreeditCaretCallbackStruct &data) {
|
||||||
|
nassertv_always(_preedit_state != nullptr);
|
||||||
|
PreeditState &state = *_preedit_state;
|
||||||
|
|
||||||
|
if (data.direction == XIMAbsolutePosition) {
|
||||||
|
_input->candidate(std::wstring(state._buffer, state._length),
|
||||||
|
state._highlight_start, state._highlight_end, data.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void x11GraphicsWindow::
|
||||||
|
handle_preedit_done() {
|
||||||
|
if (x11display_cat.is_spam()) {
|
||||||
|
x11display_cat.spam()
|
||||||
|
<< "Preedit done\n";
|
||||||
|
}
|
||||||
|
delete _preedit_state;
|
||||||
|
_preedit_state = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a keystroke corresponding to the indicated X KeyPress event.
|
* Generates a keystroke corresponding to the indicated X KeyPress event.
|
||||||
*/
|
*/
|
||||||
@ -2194,7 +2338,8 @@ check_event(X11_Display *display, XEvent *event, char *arg) {
|
|||||||
// We accept any event that is sent to our window. However, we have to let
|
// We accept any event that is sent to our window. However, we have to let
|
||||||
// raw mouse events through, since they're not associated with any window.
|
// raw mouse events through, since they're not associated with any window.
|
||||||
return (event->xany.window == self->_xwindow ||
|
return (event->xany.window == self->_xwindow ||
|
||||||
(event->type == GenericEvent && self->_raw_mouse_enabled));
|
(event->type == GenericEvent && self->_raw_mouse_enabled)) ||
|
||||||
|
(event->type == ClientMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2510,3 +2655,43 @@ cleanup:
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int x11GraphicsWindow::
|
||||||
|
xim_preedit_start(XIC ic, XPointer client_data, XPointer call_data) {
|
||||||
|
x11GraphicsWindow *window = (x11GraphicsWindow *)client_data;
|
||||||
|
return window->handle_preedit_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void x11GraphicsWindow::
|
||||||
|
xim_preedit_draw(XIC ic, XPointer client_data,
|
||||||
|
XIMPreeditDrawCallbackStruct *call_data) {
|
||||||
|
x11GraphicsWindow *window = (x11GraphicsWindow *)client_data;
|
||||||
|
nassertv_always(call_data != nullptr);
|
||||||
|
window->handle_preedit_draw(*call_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void x11GraphicsWindow::
|
||||||
|
xim_preedit_caret(XIC ic, XPointer client_data,
|
||||||
|
XIMPreeditCaretCallbackStruct *call_data) {
|
||||||
|
x11GraphicsWindow *window = (x11GraphicsWindow *)client_data;
|
||||||
|
nassertv_always(call_data != nullptr);
|
||||||
|
window->handle_preedit_caret(*call_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void x11GraphicsWindow::
|
||||||
|
xim_preedit_done(XIC ic, XPointer client_data, XPointer call_data) {
|
||||||
|
x11GraphicsWindow *window = (x11GraphicsWindow *)client_data;
|
||||||
|
window->handle_preedit_done();
|
||||||
|
}
|
||||||
|
@ -54,6 +54,10 @@ protected:
|
|||||||
bool already_mapped);
|
bool already_mapped);
|
||||||
|
|
||||||
virtual void setup_colormap(XVisualInfo *visual);
|
virtual void setup_colormap(XVisualInfo *visual);
|
||||||
|
int handle_preedit_start();
|
||||||
|
void handle_preedit_draw(XIMPreeditDrawCallbackStruct &data);
|
||||||
|
void handle_preedit_caret(XIMPreeditCaretCallbackStruct &data);
|
||||||
|
void handle_preedit_done();
|
||||||
void handle_keystroke(XKeyEvent &event);
|
void handle_keystroke(XKeyEvent &event);
|
||||||
void handle_keypress(XKeyEvent &event);
|
void handle_keypress(XKeyEvent &event);
|
||||||
void handle_keyrelease(XKeyEvent &event);
|
void handle_keyrelease(XKeyEvent &event);
|
||||||
@ -72,6 +76,11 @@ private:
|
|||||||
X11_Cursor get_cursor(const Filename &filename);
|
X11_Cursor get_cursor(const Filename &filename);
|
||||||
X11_Cursor read_ico(std::istream &ico);
|
X11_Cursor read_ico(std::istream &ico);
|
||||||
|
|
||||||
|
static int xim_preedit_start(XIC ic, XPointer client_data, XPointer call_data);
|
||||||
|
static void xim_preedit_draw(XIC ic, XPointer client_data, XIMPreeditDrawCallbackStruct *call_data);
|
||||||
|
static void xim_preedit_caret(XIC ic, XPointer client_data, XIMPreeditCaretCallbackStruct *call_data);
|
||||||
|
static void xim_preedit_done(XIC ic, XPointer client_data, XPointer call_data);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
X11_Display *_display;
|
X11_Display *_display;
|
||||||
int _screen;
|
int _screen;
|
||||||
@ -85,6 +94,13 @@ protected:
|
|||||||
LVecBase2i _fixed_size;
|
LVecBase2i _fixed_size;
|
||||||
|
|
||||||
GraphicsWindowInputDevice *_input;
|
GraphicsWindowInputDevice *_input;
|
||||||
|
struct PreeditState {
|
||||||
|
wchar_t _buffer[1024];
|
||||||
|
size_t _length = 0;
|
||||||
|
int _highlight_start = 0;
|
||||||
|
int _highlight_end = 0;
|
||||||
|
};
|
||||||
|
PreeditState *_preedit_state = nullptr;
|
||||||
|
|
||||||
long _event_mask;
|
long _event_mask;
|
||||||
bool _awaiting_configure;
|
bool _awaiting_configure;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user