summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/SDL2/src/core/linux/SDL_fcitx.c
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2016-11-16 16:26:13 +0100
committer Miodrag Milanovic <mmicko@gmail.com>2016-11-16 16:28:01 +0100
commit47a05778bff9388fc6c479461dd3804c79d7b539 (patch)
treebb3d4dc8df676b8aa478339b3fd0cd305dc4ac4d /3rdparty/SDL2/src/core/linux/SDL_fcitx.c
parente61b392edfbfe5b9d70908c087632da915a3abd8 (diff)
Updated SDL2 to 2.0.5 (nw)
Diffstat (limited to '3rdparty/SDL2/src/core/linux/SDL_fcitx.c')
-rw-r--r--3rdparty/SDL2/src/core/linux/SDL_fcitx.c553
1 files changed, 553 insertions, 0 deletions
diff --git a/3rdparty/SDL2/src/core/linux/SDL_fcitx.c b/3rdparty/SDL2/src/core/linux/SDL_fcitx.c
new file mode 100644
index 00000000000..83d19e690c5
--- /dev/null
+++ b/3rdparty/SDL2/src/core/linux/SDL_fcitx.c
@@ -0,0 +1,553 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef HAVE_FCITX_FRONTEND_H
+
+#include <fcitx/frontend.h>
+#include <unistd.h>
+
+#include "SDL_fcitx.h"
+#include "SDL_keycode.h"
+#include "SDL_keyboard.h"
+#include "../../events/SDL_keyboard_c.h"
+#include "SDL_dbus.h"
+#include "SDL_syswm.h"
+#if SDL_VIDEO_DRIVER_X11
+# include "../../video/x11/SDL_x11video.h"
+#endif
+#include "SDL_hints.h"
+
+#define FCITX_DBUS_SERVICE "org.fcitx.Fcitx"
+
+#define FCITX_IM_DBUS_PATH "/inputmethod"
+#define FCITX_IC_DBUS_PATH "/inputcontext_%d"
+
+#define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod"
+#define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext"
+
+#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];
+
+ int id;
+
+ SDL_Rect cursor_rect;
+} 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()
+{
+#if defined(__LINUX__) || defined(__FREEBSD__)
+ char *spot;
+ char procfile[1024];
+ char linkfile[1024];
+ int linksize;
+
+#if defined(__LINUX__)
+ SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/exe", getpid());
+#elif defined(__FREEBSD__)
+ SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/file", getpid());
+#endif
+ linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
+ if (linksize > 0) {
+ linkfile[linksize] = '\0';
+ spot = SDL_strrchr(linkfile, '/');
+ if (spot) {
+ return SDL_strdup(spot + 1);
+ } else {
+ return SDL_strdup(linkfile);
+ }
+ }
+#endif /* __LINUX__ || __FREEBSD__ */
+
+ return SDL_strdup("SDL_App");
+}
+
+/*
+ * Copied from fcitx source
+ */
+#define CONT(i) ISUTF8_CB(in[i])
+#define VAL(i, s) ((in[i]&0x3f) << s)
+
+static char *
+_fcitx_utf8_get_char(const char *i, uint32_t *chr)
+{
+ const unsigned char* in = (const unsigned char *)i;
+ if (!(in[0] & 0x80)) {
+ *(chr) = *(in);
+ return (char *)in + 1;
+ }
+
+ /* 2-byte, 0x80-0x7ff */
+ if ((in[0] & 0xe0) == 0xc0 && CONT(1)) {
+ *chr = ((in[0] & 0x1f) << 6) | VAL(1, 0);
+ return (char *)in + 2;
+ }
+
+ /* 3-byte, 0x800-0xffff */
+ if ((in[0] & 0xf0) == 0xe0 && CONT(1) && CONT(2)) {
+ *chr = ((in[0] & 0xf) << 12) | VAL(1, 6) | VAL(2, 0);
+ return (char *)in + 3;
+ }
+
+ /* 4-byte, 0x10000-0x1FFFFF */
+ if ((in[0] & 0xf8) == 0xf0 && CONT(1) && CONT(2) && CONT(3)) {
+ *chr = ((in[0] & 0x7) << 18) | VAL(1, 12) | VAL(2, 6) | VAL(3, 0);
+ return (char *)in + 4;
+ }
+
+ /* 5-byte, 0x200000-0x3FFFFFF */
+ if ((in[0] & 0xfc) == 0xf8 && CONT(1) && CONT(2) && CONT(3) && CONT(4)) {
+ *chr = ((in[0] & 0x3) << 24) | VAL(1, 18) | VAL(2, 12) | VAL(3, 6) | VAL(4, 0);
+ return (char *)in + 5;
+ }
+
+ /* 6-byte, 0x400000-0x7FFFFFF */
+ if ((in[0] & 0xfe) == 0xfc && CONT(1) && CONT(2) && CONT(3) && CONT(4) && CONT(5)) {
+ *chr = ((in[0] & 0x1) << 30) | VAL(1, 24) | VAL(2, 18) | VAL(3, 12) | VAL(4, 6) | VAL(5, 0);
+ return (char *)in + 6;
+ }
+
+ *chr = *in;
+
+ return (char *)in + 1;
+}
+
+static size_t
+_fcitx_utf8_strlen(const char *s)
+{
+ unsigned int l = 0;
+
+ while (*s) {
+ uint32_t chr;
+
+ s = _fcitx_utf8_get_char(s, &chr);
+ l++;
+ }
+
+ return l;
+}
+
+static DBusHandlerResult
+DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+ SDL_DBusContext *dbus = (SDL_DBusContext *)data;
+
+ if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "CommitString")) {
+ DBusMessageIter iter;
+ const char *text = NULL;
+
+ dbus->message_iter_init(msg, &iter);
+ dbus->message_iter_get_basic(&iter, &text);
+
+ if (text)
+ SDL_SendKeyboardText(text);
+
+ 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) {
+ char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
+ size_t text_bytes = SDL_strlen(text), i = 0;
+ size_t cursor = 0;
+
+ while (i < text_bytes) {
+ size_t sz = SDL_utf8strlcpy(buf, text + i, sizeof(buf));
+ size_t chars = _fcitx_utf8_strlen(buf);
+
+ SDL_SendEditingText(buf, cursor, chars);
+
+ i += sz;
+ cursor += chars;
+ }
+ }
+
+ SDL_Fcitx_UpdateTextRect(NULL);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusMessage*
+FcitxClientICNewMethod(FcitxClient *client,
+ const char *method)
+{
+ SDL_DBusContext *dbus = client->dbus;
+ return dbus->message_new_method_call(
+ client->servicename,
+ client->icname,
+ FCITX_IC_DBUS_INTERFACE,
+ method);
+}
+
+static void
+FcitxClientICCallMethod(FcitxClient *client,
+ const char *method)
+{
+ SDL_DBusContext *dbus = client->dbus;
+ DBusMessage *msg = FcitxClientICNewMethod(client, method);
+
+ if (msg == NULL)
+ return ;
+
+ if (dbus->connection_send(dbus->session_conn, msg, NULL)) {
+ dbus->connection_flush(dbus->session_conn);
+ }
+
+ dbus->message_unref(msg);
+}
+
+static void
+Fcitx_SetCapabilities(void *data,
+ const char *name,
+ const char *old_val,
+ const char *internal_editing)
+{
+ FcitxClient *client = (FcitxClient *)data;
+ SDL_DBusContext *dbus = client->dbus;
+ Uint32 caps = CAPACITY_NONE;
+
+ DBusMessage *msg = FcitxClientICNewMethod(client, "SetCapacity");
+ if (msg == NULL)
+ return ;
+
+ if (!(internal_editing && *internal_editing == '1')) {
+ caps |= CAPACITY_PREEDIT;
+ }
+
+ dbus->message_append_args(msg,
+ DBUS_TYPE_UINT32, &caps,
+ DBUS_TYPE_INVALID);
+ if (dbus->connection_send(dbus->session_conn, msg, NULL)) {
+ dbus->connection_flush(dbus->session_conn);
+ }
+
+ dbus->message_unref(msg);
+}
+
+static void
+FcitxClientCreateIC(FcitxClient *client)
+{
+ char *appname = NULL;
+ pid_t pid = 0;
+ int id = 0;
+ SDL_bool enable;
+ Uint32 arg1, arg2, arg3, arg4;
+
+ SDL_DBusContext *dbus = client->dbus;
+ DBusMessage *reply = NULL;
+ DBusMessage *msg = dbus->message_new_method_call(
+ client->servicename,
+ FCITX_IM_DBUS_PATH,
+ FCITX_IM_DBUS_INTERFACE,
+ "CreateICv3"
+ );
+
+ if (msg == NULL)
+ return ;
+
+ appname = GetAppName();
+ pid = getpid();
+ dbus->message_append_args(msg,
+ DBUS_TYPE_STRING, &appname,
+ DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_INVALID);
+
+ do {
+ reply = dbus->connection_send_with_reply_and_block(
+ dbus->session_conn,
+ msg,
+ DBUS_TIMEOUT,
+ NULL);
+
+ if (!reply)
+ break;
+ if (!dbus->message_get_args(reply, NULL,
+ 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))
+ break;
+
+ if (id < 0)
+ break;
+ client->id = id;
+
+ SDL_snprintf(client->icname, IC_NAME_MAX,
+ FCITX_IC_DBUS_PATH, client->id);
+
+ dbus->bus_add_match(dbus->session_conn,
+ "type='signal', interface='org.fcitx.Fcitx.InputContext'",
+ NULL);
+ dbus->connection_add_filter(dbus->session_conn,
+ &DBus_MessageFilter, dbus,
+ NULL);
+ dbus->connection_flush(dbus->session_conn);
+
+ SDL_AddHintCallback(SDL_HINT_IME_INTERNAL_EDITING, &Fcitx_SetCapabilities, client);
+ }
+ while (0);
+
+ if (reply)
+ dbus->message_unref(reply);
+ dbus->message_unref(msg);
+ SDL_free(appname);
+}
+
+static Uint32
+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;
+
+ return fcitx_mods;
+}
+
+SDL_bool
+SDL_Fcitx_Init()
+{
+ fcitx_client.dbus = SDL_DBus_GetContext();
+
+ fcitx_client.cursor_rect.x = -1;
+ fcitx_client.cursor_rect.y = -1;
+ 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());
+
+ FcitxClientCreateIC(&fcitx_client);
+
+ return SDL_TRUE;
+}
+
+void
+SDL_Fcitx_Quit()
+{
+ FcitxClientICCallMethod(&fcitx_client, "DestroyIC");
+}
+
+void
+SDL_Fcitx_SetFocus(SDL_bool focused)
+{
+ if (focused) {
+ FcitxClientICCallMethod(&fcitx_client, "FocusIn");
+ } else {
+ FcitxClientICCallMethod(&fcitx_client, "FocusOut");
+ }
+}
+
+void
+SDL_Fcitx_Reset(void)
+{
+ FcitxClientICCallMethod(&fcitx_client, "Reset");
+ FcitxClientICCallMethod(&fcitx_client, "CloseIC");
+}
+
+SDL_bool
+SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
+{
+ DBusMessage *msg = NULL;
+ DBusMessage *reply = NULL;
+ SDL_DBusContext *dbus = fcitx_client.dbus;
+
+ Uint32 state = 0;
+ SDL_bool handled = SDL_FALSE;
+ int type = FCITX_PRESS_KEY;
+ Uint32 event_time = 0;
+
+ msg = FcitxClientICNewMethod(&fcitx_client, "ProcessKeyEvent");
+ if (msg == NULL)
+ return SDL_FALSE;
+
+ state = Fcitx_ModState();
+ dbus->message_append_args(msg,
+ DBUS_TYPE_UINT32, &keysym,
+ DBUS_TYPE_UINT32, &keycode,
+ DBUS_TYPE_UINT32, &state,
+ DBUS_TYPE_INT32, &type,
+ DBUS_TYPE_UINT32, &event_time,
+ DBUS_TYPE_INVALID);
+
+ reply = dbus->connection_send_with_reply_and_block(dbus->session_conn,
+ msg,
+ -1,
+ NULL);
+
+ if (reply) {
+ dbus->message_get_args(reply,
+ NULL,
+ DBUS_TYPE_INT32, &handled,
+ DBUS_TYPE_INVALID);
+
+ dbus->message_unref(reply);
+ }
+
+ if (handled) {
+ SDL_Fcitx_UpdateTextRect(NULL);
+ }
+
+ return handled;
+}
+
+void
+SDL_Fcitx_UpdateTextRect(SDL_Rect *rect)
+{
+ SDL_Window *focused_win = NULL;
+ SDL_SysWMinfo info;
+ int x = 0, y = 0;
+ SDL_Rect *cursor = &fcitx_client.cursor_rect;
+
+ SDL_DBusContext *dbus = fcitx_client.dbus;
+ DBusMessage *msg = NULL;
+ DBusConnection *conn;
+
+ if (rect) {
+ SDL_memcpy(cursor, rect, sizeof(SDL_Rect));
+ }
+
+ focused_win = SDL_GetKeyboardFocus();
+ if (!focused_win) {
+ return ;
+ }
+
+ SDL_VERSION(&info.version);
+ if (!SDL_GetWindowWMInfo(focused_win, &info)) {
+ return;
+ }
+
+ SDL_GetWindowPosition(focused_win, &x, &y);
+
+#if SDL_VIDEO_DRIVER_X11
+ if (info.subsystem == SDL_SYSWM_X11) {
+ SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(focused_win)->driverdata;
+
+ Display *x_disp = info.info.x11.display;
+ Window x_win = info.info.x11.window;
+ int x_screen = displaydata->screen;
+ Window unused;
+ X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
+ }
+#endif
+
+ if (cursor->x == -1 && cursor->y == -1 && cursor->w == 0 && cursor->h == 0) {
+ // move to bottom left
+ int w = 0, h = 0;
+ SDL_GetWindowSize(focused_win, &w, &h);
+ cursor->x = 0;
+ cursor->y = h;
+ }
+
+ x += cursor->x;
+ y += cursor->y;
+
+ msg = FcitxClientICNewMethod(&fcitx_client, "SetCursorRect");
+ if (msg == NULL)
+ return ;
+
+ dbus->message_append_args(msg,
+ DBUS_TYPE_INT32, &x,
+ DBUS_TYPE_INT32, &y,
+ DBUS_TYPE_INT32, &cursor->w,
+ DBUS_TYPE_INT32, &cursor->h,
+ DBUS_TYPE_INVALID);
+
+ conn = dbus->session_conn;
+ if (dbus->connection_send(conn, msg, NULL))
+ dbus->connection_flush(conn);
+
+ dbus->message_unref(msg);
+}
+
+void
+SDL_Fcitx_PumpEvents()
+{
+ SDL_DBusContext *dbus = fcitx_client.dbus;
+ DBusConnection *conn = dbus->session_conn;
+
+ dbus->connection_read_write(conn, 0);
+
+ while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {
+ /* Do nothing, actual work happens in DBus_MessageFilter */
+ usleep(10);
+ }
+}
+
+#endif /* HAVE_FCITX_FRONTEND_H */
+
+/* vi: set ts=4 sw=4 expandtab: */