Path: blob/master/thirdparty/sdl/core/linux/SDL_fcitx.c
10279 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122#include <unistd.h>2324#include "SDL_fcitx.h"25//#include "../../video/SDL_sysvideo.h"26#include "SDL_dbus.h"2728#ifdef SDL_VIDEO_DRIVER_X1129#include "../../video/x11/SDL_x11video.h"30#endif3132#define FCITX_DBUS_SERVICE "org.freedesktop.portal.Fcitx"3334#define FCITX_IM_DBUS_PATH "/org/freedesktop/portal/inputmethod"3536#define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod1"37#define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext1"3839#define DBUS_TIMEOUT 5004041typedef struct FcitxClient42{43SDL_DBusContext *dbus;4445char *ic_path;4647int id;4849SDL_Rect cursor_rect;50} FcitxClient;5152static FcitxClient fcitx_client;5354static char *GetAppName(void)55{56#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD)57char *spot;58char procfile[1024];59char linkfile[1024];60int linksize;6162#ifdef SDL_PLATFORM_LINUX63(void)SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/exe", getpid());64#elif defined(SDL_PLATFORM_FREEBSD)65(void)SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/file", getpid());66#endif67linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);68if (linksize > 0) {69linkfile[linksize] = '\0';70spot = SDL_strrchr(linkfile, '/');71if (spot) {72return SDL_strdup(spot + 1);73} else {74return SDL_strdup(linkfile);75}76}77#endif // SDL_PLATFORM_LINUX || SDL_PLATFORM_FREEBSD7879return SDL_strdup("SDL_App");80}8182static size_t Fcitx_GetPreeditString(SDL_DBusContext *dbus,83DBusMessage *msg,84char **ret,85Sint32 *start_pos,86Sint32 *end_pos)87{88char *text = NULL, *subtext;89size_t text_bytes = 0;90DBusMessageIter iter, array, sub;91Sint32 p_start_pos = -1;92Sint32 p_end_pos = -1;9394dbus->message_iter_init(msg, &iter);95// Message type is a(si)i, we only need string part96if (dbus->message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {97size_t pos = 0;98// First pass: calculate string length99dbus->message_iter_recurse(&iter, &array);100while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {101dbus->message_iter_recurse(&array, &sub);102subtext = NULL;103if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {104dbus->message_iter_get_basic(&sub, &subtext);105if (subtext && *subtext) {106text_bytes += SDL_strlen(subtext);107}108}109dbus->message_iter_next(&sub);110if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_INT32 && p_end_pos == -1) {111// Type is a bit field defined as follows:112// bit 3: Underline, bit 4: HighLight, bit 5: DontCommit,113// bit 6: Bold, bit 7: Strike, bit 8: Italic114Sint32 type;115dbus->message_iter_get_basic(&sub, &type);116// We only consider highlight117if (type & (1 << 4)) {118if (p_start_pos == -1) {119p_start_pos = pos;120}121} else if (p_start_pos != -1 && p_end_pos == -1) {122p_end_pos = pos;123}124}125dbus->message_iter_next(&array);126if (subtext && *subtext) {127pos += SDL_utf8strlen(subtext);128}129}130if (p_start_pos != -1 && p_end_pos == -1) {131p_end_pos = pos;132}133if (text_bytes) {134text = SDL_malloc(text_bytes + 1);135}136137if (text) {138char *pivot = text;139// Second pass: join all the sub string140dbus->message_iter_recurse(&iter, &array);141while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {142dbus->message_iter_recurse(&array, &sub);143if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {144dbus->message_iter_get_basic(&sub, &subtext);145if (subtext && *subtext) {146size_t length = SDL_strlen(subtext);147SDL_strlcpy(pivot, subtext, length + 1);148pivot += length;149}150}151dbus->message_iter_next(&array);152}153} else {154text_bytes = 0;155}156}157158*ret = text;159*start_pos = p_start_pos;160*end_pos = p_end_pos;161return text_bytes;162}163164static Sint32 Fcitx_GetPreeditCursorByte(SDL_DBusContext *dbus, DBusMessage *msg)165{166Sint32 byte = -1;167DBusMessageIter iter;168169dbus->message_iter_init(msg, &iter);170171dbus->message_iter_next(&iter);172173if (dbus->message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {174return -1;175}176177dbus->message_iter_get_basic(&iter, &byte);178179return byte;180}181182static DBusHandlerResult DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)183{184SDL_DBusContext *dbus = (SDL_DBusContext *)data;185186if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "CommitString")) {187DBusMessageIter iter;188const char *text = NULL;189190dbus->message_iter_init(msg, &iter);191dbus->message_iter_get_basic(&iter, &text);192193//SDL_SendKeyboardText(text);194195return DBUS_HANDLER_RESULT_HANDLED;196}197198if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdateFormattedPreedit")) {199char *text = NULL;200Sint32 start_pos, end_pos;201size_t text_bytes = Fcitx_GetPreeditString(dbus, msg, &text, &start_pos, &end_pos);202if (text_bytes) {203if (start_pos == -1) {204Sint32 byte_pos = Fcitx_GetPreeditCursorByte(dbus, msg);205start_pos = byte_pos >= 0 ? SDL_utf8strnlen(text, byte_pos) : -1;206}207//SDL_SendEditingText(text, start_pos, end_pos >= 0 ? end_pos - start_pos : -1);208SDL_free(text);209} else {210//SDL_SendEditingText("", 0, 0);211}212213//SDL_Fcitx_UpdateTextInputArea(SDL_GetKeyboardFocus());214return DBUS_HANDLER_RESULT_HANDLED;215}216217return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;218}219220static void FcitxClientICCallMethod(FcitxClient *client, const char *method)221{222if (!client->ic_path) {223return;224}225SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, method, DBUS_TYPE_INVALID);226}227228static void SDLCALL Fcitx_SetCapabilities(void *data,229const char *name,230const char *old_val,231const char *hint)232{233FcitxClient *client = (FcitxClient *)data;234Uint64 caps = 0;235if (!client->ic_path) {236return;237}238239if (hint && SDL_strstr(hint, "composition")) {240caps |= (1 << 1); // Preedit Flag241caps |= (1 << 4); // Formatted Preedit Flag242}243if (hint && SDL_strstr(hint, "candidates")) {244// FIXME, turn off native candidate rendering245}246247SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, "SetCapability", DBUS_TYPE_UINT64, &caps, DBUS_TYPE_INVALID);248}249250static bool FcitxCreateInputContext(SDL_DBusContext *dbus, const char *appname, char **ic_path)251{252const char *program = "program";253bool result = false;254255if (dbus && dbus->session_conn) {256DBusMessage *msg = dbus->message_new_method_call(FCITX_DBUS_SERVICE, FCITX_IM_DBUS_PATH, FCITX_IM_DBUS_INTERFACE, "CreateInputContext");257if (msg) {258DBusMessage *reply = NULL;259DBusMessageIter args, array, sub;260dbus->message_iter_init_append(msg, &args);261dbus->message_iter_open_container(&args, DBUS_TYPE_ARRAY, "(ss)", &array);262dbus->message_iter_open_container(&array, DBUS_TYPE_STRUCT, 0, &sub);263dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &program);264dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appname);265dbus->message_iter_close_container(&array, &sub);266dbus->message_iter_close_container(&args, &array);267reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, 300, NULL);268if (reply) {269if (dbus->message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, ic_path, DBUS_TYPE_INVALID)) {270result = true;271}272dbus->message_unref(reply);273}274dbus->message_unref(msg);275}276}277return result;278}279280static bool FcitxClientCreateIC(FcitxClient *client)281{282char *appname = GetAppName();283char *ic_path = NULL;284SDL_DBusContext *dbus = client->dbus;285286// SDL_DBus_CallMethod cannot handle a(ss) type, call dbus function directly287if (!FcitxCreateInputContext(dbus, appname, &ic_path)) {288ic_path = NULL; // just in case.289}290291SDL_free(appname);292293if (ic_path) {294SDL_free(client->ic_path);295client->ic_path = SDL_strdup(ic_path);296297dbus->bus_add_match(dbus->session_conn,298"type='signal', interface='org.fcitx.Fcitx.InputContext1'",299NULL);300dbus->connection_add_filter(dbus->session_conn,301&DBus_MessageFilter, dbus,302NULL);303dbus->connection_flush(dbus->session_conn);304305SDL_AddHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, Fcitx_SetCapabilities, client);306return true;307}308309return false;310}311312static Uint32 Fcitx_ModState(void)313{314Uint32 fcitx_mods = 0;315SDL_Keymod sdl_mods = SDL_GetModState();316317if (sdl_mods & SDL_KMOD_SHIFT) {318fcitx_mods |= (1 << 0);319}320if (sdl_mods & SDL_KMOD_CAPS) {321fcitx_mods |= (1 << 1);322}323if (sdl_mods & SDL_KMOD_CTRL) {324fcitx_mods |= (1 << 2);325}326if (sdl_mods & SDL_KMOD_ALT) {327fcitx_mods |= (1 << 3);328}329if (sdl_mods & SDL_KMOD_NUM) {330fcitx_mods |= (1 << 4);331}332if (sdl_mods & SDL_KMOD_MODE) {333fcitx_mods |= (1 << 7);334}335if (sdl_mods & SDL_KMOD_LGUI) {336fcitx_mods |= (1 << 6);337}338if (sdl_mods & SDL_KMOD_RGUI) {339fcitx_mods |= (1 << 28);340}341342return fcitx_mods;343}344345bool SDL_Fcitx_Init(void)346{347fcitx_client.dbus = SDL_DBus_GetContext();348349fcitx_client.cursor_rect.x = -1;350fcitx_client.cursor_rect.y = -1;351fcitx_client.cursor_rect.w = 0;352fcitx_client.cursor_rect.h = 0;353354return FcitxClientCreateIC(&fcitx_client);355}356357void SDL_Fcitx_Quit(void)358{359FcitxClientICCallMethod(&fcitx_client, "DestroyIC");360if (fcitx_client.ic_path) {361SDL_free(fcitx_client.ic_path);362fcitx_client.ic_path = NULL;363}364}365366void SDL_Fcitx_SetFocus(bool focused)367{368if (focused) {369FcitxClientICCallMethod(&fcitx_client, "FocusIn");370} else {371FcitxClientICCallMethod(&fcitx_client, "FocusOut");372}373}374375void SDL_Fcitx_Reset(void)376{377FcitxClientICCallMethod(&fcitx_client, "Reset");378}379380bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)381{382Uint32 mod_state = Fcitx_ModState();383Uint32 handled = false;384Uint32 is_release = !down;385Uint32 event_time = 0;386387if (!fcitx_client.ic_path) {388return false;389}390391if (SDL_DBus_CallMethod(FCITX_DBUS_SERVICE, fcitx_client.ic_path, FCITX_IC_DBUS_INTERFACE, "ProcessKeyEvent",392DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &keycode, DBUS_TYPE_UINT32, &mod_state, DBUS_TYPE_BOOLEAN, &is_release, DBUS_TYPE_UINT32, &event_time, DBUS_TYPE_INVALID,393DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID)) {394if (handled) {395//SDL_Fcitx_UpdateTextInputArea(SDL_GetKeyboardFocus());396return true;397}398}399400return false;401}402403void SDL_Fcitx_PumpEvents(void)404{405SDL_DBusContext *dbus = fcitx_client.dbus;406DBusConnection *conn = dbus->session_conn;407408dbus->connection_read_write(conn, 0);409410while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {411// Do nothing, actual work happens in DBus_MessageFilter412}413}414415416