Fixed bug 5103 - Port fcitx support to both fcitx 4 & 5

wengxt

Due to the new major fcitx version is coming close, the existing code need to be ported to use new Fcitx dbus interface.

The new dbus interface is supported by both fcitx 4 and 5, and has a good side effect, which is that it will work with flatpak for free. Also the patch remove the dependency on fcitx header. Instead, it just hardcodes a few enum value in the code so need to handle the different header for fcitx4 or 5.
This commit is contained in:
Sam Lantinga 2020-05-11 14:31:04 -07:00
parent 33642b47b2
commit eadc8693dd
9 changed files with 160 additions and 113 deletions

View File

@ -1213,6 +1213,8 @@ elseif(UNIX AND NOT APPLE AND NOT ANDROID AND NOT RISCOS)
set(HAVE_DBUS_DBUS_H TRUE)
include_directories(${DBUS_INCLUDE_DIRS})
list(APPEND EXTRA_LIBS ${DBUS_LIBRARIES})
# Fcitx need only dbus.
set(HAVE_FCITX TRUE)
endif()
pkg_search_module(IBUS ibus-1.0 ibus)
@ -1220,6 +1222,9 @@ elseif(UNIX AND NOT APPLE AND NOT ANDROID AND NOT RISCOS)
set(HAVE_IBUS_IBUS_H TRUE)
include_directories(${IBUS_INCLUDE_DIRS})
list(APPEND EXTRA_LIBS ${IBUS_LIBRARIES})
endif()
if (HAVE_IBUS_IBUS_H OR HAVE_FCITX)
add_definitions(-DSDL_USE_IME)
endif()
if(HAVE_LIBUNWIND_H)
@ -1229,8 +1234,6 @@ elseif(UNIX AND NOT APPLE AND NOT ANDROID AND NOT RISCOS)
list(APPEND EXTRA_LIBS ${UNWIND_LIBRARIES} ${UNWIND_GENERIC_LIBRARIES})
endif()
endif()
check_include_file("fcitx/frontend.h" HAVE_FCITX_FRONTEND_H)
endif()
if(INPUT_TSLIB)

View File

@ -2731,26 +2731,15 @@ CheckFcitx()
AS_HELP_STRING([--enable-fcitx], [enable fcitx support [[default=yes]]]),
, enable_fcitx=yes)
if test x$enable_fcitx = xyes; then
PKG_CHECK_MODULES([FCITX], [fcitx], have_fcitx=yes, have_fcitx=no)
CFLAGS="$CFLAGS $FCITX_CFLAGS"
AC_CHECK_HEADER(fcitx/frontend.h,
have_fcitx_frontend_h_hdr=yes,
have_fcitx_frontend_h_hdr=no)
CFLAGS="$save_CFLAGS"
if test x$have_fcitx_frontend_h_hdr = xyes; then
if test x$enable_ime != xyes; then
AC_MSG_WARN([IME support is required for fcitx.])
have_fcitx_frontend_h_hdr=no
elif test x$enable_dbus != xyes; then
AC_MSG_WARN([DBus support is required for fcitx.])
have_fcitx_frontend_h_hdr=no
else
AC_DEFINE(HAVE_FCITX_FRONTEND_H, 1, [ ])
EXTRA_CFLAGS="$EXTRA_CFLAGS $FCITX_CFLAGS"
AC_DEFINE(HAVE_FCITX, 1, [ ])
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_fcitx.c"
fi
fi
fi
}
dnl See if we can use the Touchscreen input library
@ -4408,7 +4397,7 @@ if test x$have_ibus_ibus_h_hdr = xyes; then
else
SUMMARY="${SUMMARY}Using ibus : NO\n"
fi
if test x$have_fcitx_frontend_h_hdr = xyes; then
if test x$enable_fcitx = xyes; then
SUMMARY="${SUMMARY}Using fcitx : YES\n"
else
SUMMARY="${SUMMARY}Using fcitx : NO\n"

View File

@ -203,7 +203,7 @@
#cmakedefine HAVE_ALTIVEC_H 1
#cmakedefine HAVE_DBUS_DBUS_H 1
#cmakedefine HAVE_FCITX_FRONTEND_H 1
#cmakedefine HAVE_FCITX 1
#cmakedefine HAVE_IBUS_IBUS_H 1
#cmakedefine HAVE_IMMINTRIN_H 1
#cmakedefine HAVE_LIBSAMPLERATE_H 1

View File

@ -204,7 +204,7 @@
#undef HAVE_ALTIVEC_H
#undef HAVE_DBUS_DBUS_H
#undef HAVE_FCITX_FRONTEND_H
#undef HAVE_FCITX
#undef HAVE_IBUS_IBUS_H
#undef HAVE_IMMINTRIN_H
#undef HAVE_LIBSAMPLERATE_H

View File

@ -57,6 +57,10 @@ LoadDBUSSyms(void)
SDL_DBUS_SYM(message_new_method_call);
SDL_DBUS_SYM(message_append_args);
SDL_DBUS_SYM(message_append_args_valist);
SDL_DBUS_SYM(message_iter_init_append);
SDL_DBUS_SYM(message_iter_open_container);
SDL_DBUS_SYM(message_iter_append_basic);
SDL_DBUS_SYM(message_iter_close_container);
SDL_DBUS_SYM(message_get_args);
SDL_DBUS_SYM(message_get_args_valist);
SDL_DBUS_SYM(message_iter_init);

View File

@ -54,6 +54,10 @@ typedef struct SDL_DBusContext {
DBusMessage *(*message_new_method_call)(const char *, const char *, const char *, const char *);
dbus_bool_t (*message_append_args)(DBusMessage *, int, ...);
dbus_bool_t (*message_append_args_valist)(DBusMessage *, int, va_list);
void (*message_iter_init_append)(DBusMessage *, DBusMessageIter *);
dbus_bool_t (*message_iter_open_container)(DBusMessageIter *, int, const char *, DBusMessageIter *);
dbus_bool_t (*message_iter_append_basic)(DBusMessageIter *, int, const void *);
dbus_bool_t (*message_iter_close_container)(DBusMessageIter *, DBusMessageIter *);
dbus_bool_t (*message_get_args)(DBusMessage *, DBusError *, int, ...);
dbus_bool_t (*message_get_args_valist)(DBusMessage *, DBusError *, int, va_list);
dbus_bool_t (*message_iter_init)(DBusMessage *, DBusMessageIter *);

View File

@ -20,9 +20,6 @@
*/
#include "../../SDL_internal.h"
#ifdef HAVE_FCITX_FRONTEND_H
#include <fcitx/frontend.h>
#include <unistd.h>
#include "SDL_fcitx.h"
@ -36,23 +33,20 @@
#endif
#include "SDL_hints.h"
#define FCITX_DBUS_SERVICE "org.fcitx.Fcitx"
#define FCITX_DBUS_SERVICE "org.freedesktop.portal.Fcitx"
#define FCITX_IM_DBUS_PATH "/inputmethod"
#define FCITX_IC_DBUS_PATH "/inputcontext_%d"
#define FCITX_IM_DBUS_PATH "/org/freedesktop/portal/inputmethod"
#define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod"
#define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext"
#define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod1"
#define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext1"
#define IC_NAME_MAX 64
#define DBUS_TIMEOUT 500
typedef struct _FcitxClient
{
SDL_DBusContext *dbus;
char servicename[IC_NAME_MAX];
char icname[IC_NAME_MAX];
char *ic_path;
int id;
@ -61,34 +55,6 @@ typedef struct _FcitxClient
static FcitxClient fcitx_client;
static int
GetDisplayNumber()
{
const char *display = SDL_getenv("DISPLAY");
const char *p = NULL;
int number = 0;
if (display == NULL)
return 0;
display = SDL_strchr(display, ':');
if (display == NULL)
return 0;
display++;
p = SDL_strchr(display, '.');
if (p == NULL && display != NULL) {
number = SDL_strtod(display, NULL);
} else {
char *buffer = SDL_strdup(display);
buffer[p - display] = '\0';
number = SDL_strtod(buffer, NULL);
SDL_free(buffer);
}
return number;
}
static char*
GetAppName()
{
@ -118,6 +84,54 @@ GetAppName()
return SDL_strdup("SDL_App");
}
size_t Fcitx_GetPreeditString(SDL_DBusContext *dbus, DBusMessage *msg, char **ret) {
char *text = NULL, *subtext;
size_t text_bytes = 0;
DBusMessageIter iter, array, sub;
dbus->message_iter_init(msg, &iter);
/* Message type is a(si)i, we only need string part */
if (dbus->message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
/* First pass: calculate string length */
dbus->message_iter_recurse(&iter, &array);
while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
dbus->message_iter_recurse(&array, &sub);
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
dbus->message_iter_get_basic(&sub, &subtext);
if (subtext && *subtext) {
text_bytes += SDL_strlen(subtext);
}
}
dbus->message_iter_next(&array);
}
if (text_bytes) {
text = SDL_malloc(text_bytes + 1);
}
if (text) {
char* pivot = text;
/* Second pass: join all the sub string */
dbus->message_iter_recurse(&iter, &array);
while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
dbus->message_iter_recurse(&array, &sub);
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
dbus->message_iter_get_basic(&sub, &subtext);
if (subtext && *subtext) {
size_t length = SDL_strlen(subtext);
SDL_strlcpy(pivot, subtext, length + 1);
pivot += length;
}
}
dbus->message_iter_next(&array);
}
} else {
text_bytes = 0;
}
}
*ret= text;
return text_bytes;
}
static DBusHandlerResult
DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
{
@ -136,16 +150,12 @@ DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
return DBUS_HANDLER_RESULT_HANDLED;
}
if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdatePreedit")) {
DBusMessageIter iter;
const char *text;
dbus->message_iter_init(msg, &iter);
dbus->message_iter_get_basic(&iter, &text);
if (text && *text) {
if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdateFormattedPreedit")) {
char *text = NULL;
size_t text_bytes = Fcitx_GetPreeditString(dbus, msg, &text);
if (text_bytes) {
char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
size_t text_bytes = SDL_strlen(text), i = 0;
size_t i = 0;
size_t cursor = 0;
while (i < text_bytes) {
@ -157,6 +167,9 @@ DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
i += sz;
cursor += chars;
}
SDL_free(text);
} else {
SDL_SendEditingText("", 0, 0);
}
SDL_Fcitx_UpdateTextRect(NULL);
@ -169,7 +182,10 @@ DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
static void
FcitxClientICCallMethod(FcitxClient *client, const char *method)
{
SDL_DBus_CallVoidMethod(client->servicename, client->icname, FCITX_IC_DBUS_INTERFACE, method, DBUS_TYPE_INVALID);
if (!client->ic_path) {
return;
}
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, method, DBUS_TYPE_INVALID);
}
static void SDLCALL
@ -179,40 +195,68 @@ Fcitx_SetCapabilities(void *data,
const char *internal_editing)
{
FcitxClient *client = (FcitxClient *)data;
Uint32 caps = CAPACITY_NONE;
if (!(internal_editing && *internal_editing == '1')) {
caps |= CAPACITY_PREEDIT;
Uint32 caps = 0;
if (!client->ic_path) {
return;
}
SDL_DBus_CallVoidMethod(client->servicename, client->icname, FCITX_IC_DBUS_INTERFACE, "SetCapacity", DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID);
if (!(internal_editing && *internal_editing == '1')) {
caps |= (1 << 1); /* Preedit Flag */
caps |= (1 << 4); /* Formatted Preedit Flag */
}
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, "SetCapability", DBUS_TYPE_UINT64, &caps, DBUS_TYPE_INVALID);
}
static SDL_bool
FcitxCreateInputContext(SDL_DBusContext* dbus, const char *appname, char **ic_path) {
const char *program = "program";
SDL_bool retval = SDL_FALSE;
if (dbus->session_conn) {
DBusMessage *msg = dbus->message_new_method_call(FCITX_DBUS_SERVICE, FCITX_IM_DBUS_PATH, FCITX_IM_DBUS_INTERFACE, "CreateInputContext");
if (msg) {
DBusMessage *reply = NULL;
DBusMessageIter args, array, sub;
dbus->message_iter_init_append(msg, &args);
dbus->message_iter_open_container(&args, DBUS_TYPE_ARRAY, "(ss)", &array);
dbus->message_iter_open_container(&array, DBUS_TYPE_STRUCT, 0, &sub);
dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &program);
dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appname);
dbus->message_iter_close_container(&array, &sub);
dbus->message_iter_close_container(&args, &array);
reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, 300, NULL);
if (reply) {
if (dbus->message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, ic_path, DBUS_TYPE_INVALID)) {
retval = SDL_TRUE;
}
dbus->message_unref(reply);
}
dbus->message_unref(msg);
}
}
return retval;
}
static SDL_bool
FcitxClientCreateIC(FcitxClient *client)
{
char *appname = GetAppName();
pid_t pid = getpid();
int id = -1;
Uint32 enable, arg1, arg2, arg3, arg4;
char *ic_path = NULL;
SDL_DBusContext *dbus = client->dbus;
if (!SDL_DBus_CallMethod(client->servicename, FCITX_IM_DBUS_PATH, FCITX_IM_DBUS_INTERFACE, "CreateICv3",
DBUS_TYPE_STRING, &appname, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID,
DBUS_TYPE_INT32, &id, DBUS_TYPE_BOOLEAN, &enable, DBUS_TYPE_UINT32, &arg1, DBUS_TYPE_UINT32, &arg2, DBUS_TYPE_UINT32, &arg3, DBUS_TYPE_UINT32, &arg4, DBUS_TYPE_INVALID)) {
id = -1; /* just in case. */
/* SDL_DBus_CallMethod cannot handle a(ss) type, call dbus function directly */
if (!FcitxCreateInputContext(dbus, appname, &ic_path)) {
ic_path = NULL; /* just in case. */
}
SDL_free(appname);
if (id >= 0) {
SDL_DBusContext *dbus = client->dbus;
client->id = id;
SDL_snprintf(client->icname, IC_NAME_MAX, FCITX_IC_DBUS_PATH, client->id);
if (ic_path) {
SDL_free(client->ic_path);
client->ic_path = SDL_strdup(ic_path);
dbus->bus_add_match(dbus->session_conn,
"type='signal', interface='org.fcitx.Fcitx.InputContext'",
"type='signal', interface='org.fcitx.Fcitx.InputContext1'",
NULL);
dbus->connection_add_filter(dbus->session_conn,
&DBus_MessageFilter, dbus,
@ -232,13 +276,14 @@ Fcitx_ModState(void)
Uint32 fcitx_mods = 0;
SDL_Keymod sdl_mods = SDL_GetModState();
if (sdl_mods & KMOD_SHIFT) fcitx_mods |= FcitxKeyState_Shift;
if (sdl_mods & KMOD_CAPS) fcitx_mods |= FcitxKeyState_CapsLock;
if (sdl_mods & KMOD_CTRL) fcitx_mods |= FcitxKeyState_Ctrl;
if (sdl_mods & KMOD_ALT) fcitx_mods |= FcitxKeyState_Alt;
if (sdl_mods & KMOD_NUM) fcitx_mods |= FcitxKeyState_NumLock;
if (sdl_mods & KMOD_LGUI) fcitx_mods |= FcitxKeyState_Super;
if (sdl_mods & KMOD_RGUI) fcitx_mods |= FcitxKeyState_Meta;
if (sdl_mods & KMOD_SHIFT) fcitx_mods |= (1 << 0);
if (sdl_mods & KMOD_CAPS) fcitx_mods |= (1 << 1);
if (sdl_mods & KMOD_CTRL) fcitx_mods |= (1 << 2);
if (sdl_mods & KMOD_ALT) fcitx_mods |= (1 << 3);
if (sdl_mods & KMOD_NUM) fcitx_mods |= (1 << 4);
if (sdl_mods & KMOD_MODE) fcitx_mods |= (1 << 7);
if (sdl_mods & KMOD_LGUI) fcitx_mods |= (1 << 6);
if (sdl_mods & KMOD_RGUI) fcitx_mods |= (1 << 28);
return fcitx_mods;
}
@ -253,10 +298,6 @@ SDL_Fcitx_Init()
fcitx_client.cursor_rect.w = 0;
fcitx_client.cursor_rect.h = 0;
SDL_snprintf(fcitx_client.servicename, IC_NAME_MAX,
"%s-%d",
FCITX_DBUS_SERVICE, GetDisplayNumber());
return FcitxClientCreateIC(&fcitx_client);
}
@ -264,6 +305,10 @@ void
SDL_Fcitx_Quit()
{
FcitxClientICCallMethod(&fcitx_client, "DestroyIC");
if (fcitx_client.ic_path) {
SDL_free(fcitx_client.ic_path);
fcitx_client.ic_path = NULL;
}
}
void
@ -288,12 +333,16 @@ SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
{
Uint32 state = Fcitx_ModState();
Uint32 handled = SDL_FALSE;
int type = FCITX_PRESS_KEY;
Uint32 is_release = SDL_FALSE;
Uint32 event_time = 0;
if (SDL_DBus_CallMethod(fcitx_client.servicename, fcitx_client.icname, FCITX_IC_DBUS_INTERFACE, "ProcessKeyEvent",
DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &keycode, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INT32, &type, DBUS_TYPE_UINT32, &event_time, DBUS_TYPE_INVALID,
DBUS_TYPE_INT32, &handled, DBUS_TYPE_INVALID)) {
if (!fcitx_client.ic_path) {
return SDL_FALSE;
}
if (SDL_DBus_CallMethod(FCITX_DBUS_SERVICE, fcitx_client.ic_path, FCITX_IC_DBUS_INTERFACE, "ProcessKeyEvent",
DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &keycode, DBUS_TYPE_UINT32, &state, DBUS_TYPE_BOOLEAN, &is_release, DBUS_TYPE_UINT32, &event_time, DBUS_TYPE_INVALID,
DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID)) {
if (handled) {
SDL_Fcitx_UpdateTextRect(NULL);
return SDL_TRUE;
@ -350,7 +399,7 @@ SDL_Fcitx_UpdateTextRect(SDL_Rect *rect)
x += cursor->x;
y += cursor->y;
SDL_DBus_CallVoidMethod(fcitx_client.servicename, fcitx_client.icname, FCITX_IC_DBUS_INTERFACE, "SetCursorRect",
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, fcitx_client.ic_path, FCITX_IC_DBUS_INTERFACE, "SetCursorRect",
DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &cursor->w, DBUS_TYPE_INT32, &cursor->h, DBUS_TYPE_INVALID);
}
@ -368,6 +417,4 @@ SDL_Fcitx_PumpEvents(void)
}
}
#endif /* HAVE_FCITX_FRONTEND_H */
/* vi: set ts=4 sw=4 expandtab: */

View File

@ -43,7 +43,7 @@ static void
InitIME()
{
static SDL_bool inited = SDL_FALSE;
#ifdef HAVE_FCITX_FRONTEND_H
#ifdef HAVE_FCITX
const char *im_module = SDL_getenv("SDL_IM_MODULE");
const char *xmodifiers = SDL_getenv("XMODIFIERS");
#endif
@ -54,7 +54,7 @@ InitIME()
inited = SDL_TRUE;
/* See if fcitx IME support is being requested */
#ifdef HAVE_FCITX_FRONTEND_H
#ifdef HAVE_FCITX
if (!SDL_IME_Init_Real &&
((im_module && SDL_strcmp(im_module, "fcitx") == 0) ||
(!im_module && xmodifiers && SDL_strstr(xmodifiers, "@im=fcitx") != NULL))) {
@ -66,7 +66,7 @@ InitIME()
SDL_IME_UpdateTextRect_Real = SDL_Fcitx_UpdateTextRect;
SDL_IME_PumpEvents_Real = SDL_Fcitx_PumpEvents;
}
#endif /* HAVE_FCITX_FRONTEND_H */
#endif /* HAVE_FCITX */
/* default to IBus */
#ifdef HAVE_IBUS_IBUS_H

View File

@ -683,7 +683,7 @@ X11_DispatchEvent(_THIS)
/* But only if we're using one of the DBus IMEs, otherwise
some XIM IMEs will generate duplicate events */
if (orig_keycode) {
#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX_FRONTEND_H)
#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX)
SDL_Scancode scancode = videodata->key_layout[orig_keycode];
videodata->filter_code = orig_keycode;
videodata->filter_time = xevent.xkey.time;