The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

/*******************************************************************************/
/* */
/* XIM support */
/* */
/* Code courtesy Yin Maofan */
/* */
/*******************************************************************************/
#include "unix/guts.h"
#include "Application.h"
void
prima_xim_init(void)
{
if (
( guts.xic_buffer = malloc( guts.xic_bufsize = 256 )) != NULL
) {
char *loc = setlocale(LC_CTYPE, NULL);
setlocale(LC_CTYPE, "");
XSetLocaleModifiers("");
guts.xim = XOpenIM(DISP, NULL, NULL, NULL);
if ( guts.xim) {
guts.xic = XCreateIC(guts.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL);
guts.use_xim = true;
}
setlocale(LC_CTYPE, loc);
}
}
void
prima_xim_done(void)
{
XDestroyIC(guts.xic);
XCloseIM(guts.xim);
free(guts.xic_buffer);
guts.use_xim = false;
}
void
prima_xim_update_cursor( Handle self)
{
DEFXX;
XPoint spot;
XVaNestedList preedit_attr;
char *error;
spot.x = XX->cursor_pos.x;
spot.y = XX->size. y - XX->cursor_pos. y;
preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
error = XSetICValues(guts.xic, XNPreeditAttributes, preedit_attr, NULL);
if ( error != NULL )
Mdebug("XSetICValues(XNSpotLocation) error: %s", error);
XFree(preedit_attr);
}
void
prima_xim_focus_in(Handle self)
{
char *error;
Handle toplevel = prima_find_root_parent(self);
error = XSetICValues(guts.xic, XNClientWindow, PWidget(toplevel)->handle, NULL);
if ( error != NULL )
Mdebug("XSetICValues(XNClientWindow) error: %s", error);
error = XSetICValues(guts.xic, XNFocusWindow, PWidget(self)->handle, NULL);
if ( error != NULL )
Mdebug("XSetICValues(XNFocusWindow) error: %s", error);
XSetICFocus(guts.xic);
#ifdef X_HAVE_UTF8_STRING
XFree(Xutf8ResetIC(guts.xic));
#endif
XCHECKPOINT;
}
void
prima_xim_focus_out(void)
{
XUnsetICFocus(guts.xic);
XSetICValues( guts.xic, XNClientWindow, 0, NULL);
XSetICValues( guts.xic, XNFocusWindow, 0, NULL);
}
Bool
prima_xim_handle_key_press( Handle self, XKeyEvent *xev, Event *e, KeySym *sym)
{
#ifdef X_HAVE_UTF8_STRING
Status status;
int c, n;
Bool ok = true;
semistatic_t local_buf_ptr;
char local_buf[256], *buf;
while ( 1 ) {
char *b;
c = Xutf8LookupString(guts.xic, xev, guts.xic_buffer, guts.xic_bufsize - 1, sym, &status);
Mdebug("Xutf8LookupString: nc=%d status=%d", c, status);
switch ( status ) {
case XLookupNone:
case XLookupKeySym:
return false;
}
if (status != XBufferOverflow)
break;
if ( ( b = realloc( guts.xic_buffer, guts.xic_bufsize * 2)) == NULL ) {
Mdebug("cannot realloc %d bytes for XIC buffer", guts.xic_bufsize * 2);
return false;
}
guts.xic_bufsize *= 2;
guts.xic_buffer = b;
}
if ( c == 0 ) return true;
semistatic_init( &local_buf_ptr, &local_buf, sizeof(char), sizeof(local_buf));
if ( !semistatic_expand( &local_buf_ptr, c + 1 ))
return false;
buf = (char*) local_buf_ptr.heap;
memcpy( buf, guts.xic_buffer, c);
guts.xic_buffer[0] = 0;
buf[c] = 0;
Mdebug("Xutf8LookupString: string=%s", buf);
/* send events */
protect_object(self);
n = 0;
while ( *buf ) {
UV uv;
unsigned int charlen;
Event ev;
uv = prima_utf8_uvchr(buf, c, &charlen);
buf += charlen;
c -= charlen;
if ( charlen == 0 ) break;
n++;
if ( uv > 0x10FFFF ) continue;
ev.cmd = cmKeyDown;
ev.key.code = uv;
ev.key.mod = kmUnicode;
ev.key.repeat = 1;
ev.key.key = kbNoKey;
if ( n == 1 && status == XLookupBoth ) {
U32 key = prima_keysym_to_keycode(*sym, xev, 0) & kbCodeMask;
if ( key != 0 ) ev.key.key = key;
}
Mdebug("Xutf8LookupString: char(%d)=%x key=%x", n, uv, ev.key.key);
CComponent(self)-> message(self,&ev);
if (PWidget(self)-> stage != csNormal)
break;
}
unprotect_object(self);
semistatic_done( &local_buf_ptr);
XCHECKPOINT;
return ok;
#else
return false;
#endif
}