src/core/linux/SDL_fcitx.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 18 Aug 2017 20:00:29 -0400
changeset 11323 46861f3fc187
parent 11284 3db78361e751
child 11811 5d94cb6b24d3
permissions -rw-r--r--
cmake: added a FIXME for later.

Have to figure out what cmake version fixed this and bump the minimum to that.
slouken@10496
     1
/*
slouken@10496
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@10496
     4
slouken@10496
     5
  This software is provided 'as-is', without any express or implied
slouken@10496
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@10496
     7
  arising from the use of this software.
slouken@10496
     8
slouken@10496
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@10496
    10
  including commercial applications, and to alter it and redistribute it
slouken@10496
    11
  freely, subject to the following restrictions:
slouken@10496
    12
slouken@10496
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@10496
    14
     claim that you wrote the original software. If you use this software
slouken@10496
    15
     in a product, an acknowledgment in the product documentation would be
slouken@10496
    16
     appreciated but is not required.
slouken@10496
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@10496
    18
     misrepresented as being the original software.
slouken@10496
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@10496
    20
*/
slouken@10537
    21
#include "../../SDL_internal.h"
slouken@10533
    22
slouken@10533
    23
#ifdef HAVE_FCITX_FRONTEND_H
slouken@10496
    24
slouken@10496
    25
#include <fcitx/frontend.h>
slouken@10496
    26
#include <unistd.h>
slouken@10496
    27
slouken@10496
    28
#include "SDL_fcitx.h"
slouken@10496
    29
#include "SDL_keycode.h"
slouken@10496
    30
#include "SDL_keyboard.h"
slouken@10496
    31
#include "../../events/SDL_keyboard_c.h"
slouken@10496
    32
#include "SDL_dbus.h"
slouken@10496
    33
#include "SDL_syswm.h"
slouken@10496
    34
#if SDL_VIDEO_DRIVER_X11
slouken@10496
    35
#  include "../../video/x11/SDL_x11video.h"
slouken@10496
    36
#endif
slouken@10496
    37
#include "SDL_hints.h"
slouken@10496
    38
slouken@10496
    39
#define FCITX_DBUS_SERVICE "org.fcitx.Fcitx"
slouken@10496
    40
slouken@10496
    41
#define FCITX_IM_DBUS_PATH "/inputmethod"
slouken@10496
    42
#define FCITX_IC_DBUS_PATH "/inputcontext_%d"
slouken@10496
    43
slouken@10496
    44
#define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod"
slouken@10496
    45
#define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext"
slouken@10496
    46
slouken@10496
    47
#define IC_NAME_MAX 64
slouken@10496
    48
#define DBUS_TIMEOUT 500
slouken@10496
    49
slouken@10496
    50
typedef struct _FcitxClient
slouken@10496
    51
{
slouken@10496
    52
    SDL_DBusContext *dbus;
slouken@10496
    53
slouken@10496
    54
    char servicename[IC_NAME_MAX];
slouken@10496
    55
    char icname[IC_NAME_MAX];
slouken@10496
    56
slouken@10496
    57
    int id;
slouken@10496
    58
slouken@10496
    59
    SDL_Rect cursor_rect;
slouken@10496
    60
} FcitxClient;
slouken@10496
    61
slouken@10496
    62
static FcitxClient fcitx_client;
slouken@10496
    63
slouken@10496
    64
static int
slouken@10496
    65
GetDisplayNumber()
slouken@10496
    66
{
slouken@10496
    67
    const char *display = SDL_getenv("DISPLAY");
slouken@10500
    68
    const char *p = NULL;
slouken@10496
    69
    int number = 0;
slouken@10496
    70
slouken@10496
    71
    if (display == NULL)
slouken@10496
    72
        return 0;
slouken@10496
    73
slouken@10496
    74
    display = SDL_strchr(display, ':');
slouken@10496
    75
    if (display == NULL)
slouken@10496
    76
        return 0;
slouken@10496
    77
slouken@10496
    78
    display++;
slouken@10496
    79
    p = SDL_strchr(display, '.');
slouken@10496
    80
    if (p == NULL && display != NULL) {
slouken@10496
    81
        number = SDL_strtod(display, NULL);
slouken@10496
    82
    } else {
slouken@10496
    83
        char *buffer = SDL_strdup(display);
slouken@10496
    84
        buffer[p - display] = '\0';
slouken@10496
    85
        number = SDL_strtod(buffer, NULL);
slouken@10496
    86
        SDL_free(buffer);
slouken@10496
    87
    }
slouken@10496
    88
slouken@10496
    89
    return number;
slouken@10496
    90
}
slouken@10496
    91
slouken@10496
    92
static char*
slouken@10496
    93
GetAppName()
slouken@10496
    94
{
slouken@10496
    95
#if defined(__LINUX__) || defined(__FREEBSD__)
slouken@10496
    96
    char *spot;
slouken@10496
    97
    char procfile[1024];
slouken@10496
    98
    char linkfile[1024];
slouken@10496
    99
    int linksize;
slouken@10496
   100
slouken@10496
   101
#if defined(__LINUX__)
slouken@10496
   102
    SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/exe", getpid());
slouken@10496
   103
#elif defined(__FREEBSD__)
slouken@10496
   104
    SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/file", getpid());
slouken@10496
   105
#endif
slouken@10496
   106
    linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
slouken@10496
   107
    if (linksize > 0) {
slouken@10496
   108
        linkfile[linksize] = '\0';
slouken@10496
   109
        spot = SDL_strrchr(linkfile, '/');
slouken@10496
   110
        if (spot) {
slouken@10496
   111
            return SDL_strdup(spot + 1);
slouken@10496
   112
        } else {
slouken@10496
   113
            return SDL_strdup(linkfile);
slouken@10496
   114
        }
slouken@10496
   115
    }
slouken@10496
   116
#endif /* __LINUX__ || __FREEBSD__ */
slouken@10496
   117
slouken@10496
   118
    return SDL_strdup("SDL_App");
slouken@10496
   119
}
slouken@10496
   120
slouken@10496
   121
static DBusHandlerResult
slouken@10496
   122
DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
slouken@10496
   123
{
slouken@10496
   124
    SDL_DBusContext *dbus = (SDL_DBusContext *)data;
slouken@10496
   125
slouken@10496
   126
    if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "CommitString")) {
slouken@10496
   127
        DBusMessageIter iter;
slouken@10496
   128
        const char *text = NULL;
slouken@10496
   129
slouken@10496
   130
        dbus->message_iter_init(msg, &iter);
slouken@10496
   131
        dbus->message_iter_get_basic(&iter, &text);
slouken@10496
   132
slouken@10496
   133
        if (text)
slouken@10496
   134
            SDL_SendKeyboardText(text);
slouken@10496
   135
slouken@10496
   136
        return DBUS_HANDLER_RESULT_HANDLED;
slouken@10496
   137
    }
slouken@10496
   138
slouken@10496
   139
    if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdatePreedit")) {
slouken@10496
   140
        DBusMessageIter iter;
slouken@10496
   141
        const char *text;
slouken@10496
   142
slouken@10496
   143
        dbus->message_iter_init(msg, &iter);
slouken@10496
   144
        dbus->message_iter_get_basic(&iter, &text);
slouken@10496
   145
slouken@10496
   146
        if (text && *text) {
slouken@10496
   147
            char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
slouken@10496
   148
            size_t text_bytes = SDL_strlen(text), i = 0;
slouken@10496
   149
            size_t cursor = 0;
slouken@10496
   150
slouken@10496
   151
            while (i < text_bytes) {
icculus@11050
   152
                const size_t sz = SDL_utf8strlcpy(buf, text + i, sizeof(buf));
icculus@11050
   153
                const size_t chars = SDL_utf8strlen(buf);
slouken@10496
   154
slouken@10496
   155
                SDL_SendEditingText(buf, cursor, chars);
slouken@10496
   156
slouken@10496
   157
                i += sz;
slouken@10496
   158
                cursor += chars;
slouken@10496
   159
            }
slouken@10496
   160
        }
slouken@10496
   161
slouken@10496
   162
        SDL_Fcitx_UpdateTextRect(NULL);
slouken@10496
   163
        return DBUS_HANDLER_RESULT_HANDLED;
slouken@10496
   164
    }
slouken@10496
   165
slouken@10496
   166
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
slouken@10496
   167
}
slouken@10496
   168
icculus@11042
   169
static void
icculus@11042
   170
FcitxClientICCallMethod(FcitxClient *client, const char *method)
slouken@10496
   171
{
icculus@11042
   172
    SDL_DBus_CallVoidMethod(client->servicename, client->icname, FCITX_IC_DBUS_INTERFACE, method, DBUS_TYPE_INVALID);
slouken@10496
   173
}
slouken@10496
   174
slouken@11284
   175
static void SDLCALL
slouken@10496
   176
Fcitx_SetCapabilities(void *data,
slouken@10496
   177
        const char *name,
slouken@10496
   178
        const char *old_val,
slouken@10496
   179
        const char *internal_editing)
slouken@10496
   180
{
slouken@10496
   181
    FcitxClient *client = (FcitxClient *)data;
slouken@10496
   182
    Uint32 caps = CAPACITY_NONE;
slouken@10496
   183
slouken@10496
   184
    if (!(internal_editing && *internal_editing == '1')) {
slouken@10496
   185
        caps |= CAPACITY_PREEDIT;
slouken@10496
   186
    }
slouken@10496
   187
icculus@11042
   188
    SDL_DBus_CallVoidMethod(client->servicename, client->icname, FCITX_IC_DBUS_INTERFACE, "SetCapacity", DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID);
slouken@10496
   189
}
slouken@10496
   190
icculus@11053
   191
static SDL_bool
slouken@10496
   192
FcitxClientCreateIC(FcitxClient *client)
slouken@10496
   193
{
icculus@11042
   194
    char *appname = GetAppName();
icculus@11042
   195
    pid_t pid = getpid();
icculus@11042
   196
    int id = -1;
icculus@11042
   197
    Uint32 enable, arg1, arg2, arg3, arg4;
slouken@10496
   198
icculus@11053
   199
    if (!SDL_DBus_CallMethod(client->servicename, FCITX_IM_DBUS_PATH, FCITX_IM_DBUS_INTERFACE, "CreateICv3",
icculus@11053
   200
            DBUS_TYPE_STRING, &appname, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID,
icculus@11053
   201
            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)) {
icculus@11053
   202
        id = -1;  /* just in case. */
icculus@11053
   203
    }
slouken@10496
   204
icculus@11042
   205
    SDL_free(appname);
slouken@10496
   206
icculus@11042
   207
    if (id >= 0) {
icculus@11042
   208
        SDL_DBusContext *dbus = client->dbus;
icculus@11042
   209
slouken@10496
   210
        client->id = id;
slouken@10496
   211
icculus@11042
   212
        SDL_snprintf(client->icname, IC_NAME_MAX, FCITX_IC_DBUS_PATH, client->id);
slouken@10496
   213
slouken@10496
   214
        dbus->bus_add_match(dbus->session_conn,
slouken@10496
   215
                "type='signal', interface='org.fcitx.Fcitx.InputContext'",
slouken@10496
   216
                NULL);
slouken@10496
   217
        dbus->connection_add_filter(dbus->session_conn,
slouken@10496
   218
                &DBus_MessageFilter, dbus,
slouken@10496
   219
                NULL);
slouken@10496
   220
        dbus->connection_flush(dbus->session_conn);
slouken@10496
   221
slouken@11284
   222
        SDL_AddHintCallback(SDL_HINT_IME_INTERNAL_EDITING, Fcitx_SetCapabilities, client);
icculus@11053
   223
        return SDL_TRUE;
slouken@10496
   224
    }
icculus@11053
   225
icculus@11053
   226
    return SDL_FALSE;
slouken@10496
   227
}
slouken@10496
   228
slouken@10496
   229
static Uint32
slouken@10496
   230
Fcitx_ModState(void)
slouken@10496
   231
{
slouken@10496
   232
    Uint32 fcitx_mods = 0;
slouken@10496
   233
    SDL_Keymod sdl_mods = SDL_GetModState();
slouken@10496
   234
slouken@10496
   235
    if (sdl_mods & KMOD_SHIFT) fcitx_mods |= FcitxKeyState_Shift;
slouken@10496
   236
    if (sdl_mods & KMOD_CAPS)   fcitx_mods |= FcitxKeyState_CapsLock;
slouken@10496
   237
    if (sdl_mods & KMOD_CTRL)  fcitx_mods |= FcitxKeyState_Ctrl;
slouken@10496
   238
    if (sdl_mods & KMOD_ALT)   fcitx_mods |= FcitxKeyState_Alt;
slouken@10496
   239
    if (sdl_mods & KMOD_NUM)    fcitx_mods |= FcitxKeyState_NumLock;
slouken@10496
   240
    if (sdl_mods & KMOD_LGUI)   fcitx_mods |= FcitxKeyState_Super;
slouken@10496
   241
    if (sdl_mods & KMOD_RGUI)   fcitx_mods |= FcitxKeyState_Meta;
slouken@10496
   242
slouken@10496
   243
    return fcitx_mods;
slouken@10496
   244
}
slouken@10496
   245
slouken@10496
   246
SDL_bool
slouken@10496
   247
SDL_Fcitx_Init()
slouken@10496
   248
{
slouken@10496
   249
    fcitx_client.dbus = SDL_DBus_GetContext();
slouken@10496
   250
slouken@10496
   251
    fcitx_client.cursor_rect.x = -1;
slouken@10496
   252
    fcitx_client.cursor_rect.y = -1;
slouken@10496
   253
    fcitx_client.cursor_rect.w = 0;
slouken@10496
   254
    fcitx_client.cursor_rect.h = 0;
slouken@10496
   255
slouken@10496
   256
    SDL_snprintf(fcitx_client.servicename, IC_NAME_MAX,
slouken@10496
   257
            "%s-%d",
slouken@10496
   258
            FCITX_DBUS_SERVICE, GetDisplayNumber());
slouken@10496
   259
icculus@11053
   260
    return FcitxClientCreateIC(&fcitx_client);
slouken@10496
   261
}
slouken@10496
   262
slouken@10496
   263
void
slouken@10496
   264
SDL_Fcitx_Quit()
slouken@10496
   265
{
slouken@10496
   266
    FcitxClientICCallMethod(&fcitx_client, "DestroyIC");
slouken@10496
   267
}
slouken@10496
   268
slouken@10496
   269
void
slouken@10496
   270
SDL_Fcitx_SetFocus(SDL_bool focused)
slouken@10496
   271
{
slouken@10496
   272
    if (focused) {
slouken@10496
   273
        FcitxClientICCallMethod(&fcitx_client, "FocusIn");
slouken@10496
   274
    } else {
slouken@10496
   275
        FcitxClientICCallMethod(&fcitx_client, "FocusOut");
slouken@10496
   276
    }
slouken@10496
   277
}
slouken@10496
   278
slouken@10496
   279
void
slouken@10496
   280
SDL_Fcitx_Reset(void)
slouken@10496
   281
{
slouken@10496
   282
    FcitxClientICCallMethod(&fcitx_client, "Reset");
slouken@10496
   283
    FcitxClientICCallMethod(&fcitx_client, "CloseIC");
slouken@10496
   284
}
slouken@10496
   285
slouken@10496
   286
SDL_bool
slouken@10496
   287
SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
slouken@10496
   288
{
icculus@11042
   289
    Uint32 state = Fcitx_ModState();
icculus@11042
   290
    Uint32 handled = SDL_FALSE;
slouken@10496
   291
    int type = FCITX_PRESS_KEY;
slouken@10496
   292
    Uint32 event_time = 0;
slouken@10496
   293
icculus@11042
   294
    if (SDL_DBus_CallMethod(fcitx_client.servicename, fcitx_client.icname, FCITX_IC_DBUS_INTERFACE, "ProcessKeyEvent",
icculus@11042
   295
            DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &keycode, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INT32, &type, DBUS_TYPE_UINT32, &event_time, DBUS_TYPE_INVALID,
icculus@11042
   296
            DBUS_TYPE_INT32, &handled, DBUS_TYPE_INVALID)) {
icculus@11042
   297
        if (handled) {
icculus@11042
   298
            SDL_Fcitx_UpdateTextRect(NULL);
icculus@11042
   299
            return SDL_TRUE;
icculus@11042
   300
        }
slouken@10496
   301
    }
slouken@10496
   302
icculus@11042
   303
    return SDL_FALSE;
slouken@10496
   304
}
slouken@10496
   305
slouken@10496
   306
void
slouken@10496
   307
SDL_Fcitx_UpdateTextRect(SDL_Rect *rect)
slouken@10496
   308
{
slouken@10496
   309
    SDL_Window *focused_win = NULL;
slouken@10496
   310
    SDL_SysWMinfo info;
slouken@10496
   311
    int x = 0, y = 0;
slouken@10496
   312
    SDL_Rect *cursor = &fcitx_client.cursor_rect;
slouken@10496
   313
slouken@10496
   314
    if (rect) {
slouken@10496
   315
        SDL_memcpy(cursor, rect, sizeof(SDL_Rect));
slouken@10496
   316
    }
slouken@10496
   317
slouken@10496
   318
    focused_win = SDL_GetKeyboardFocus();
slouken@10496
   319
    if (!focused_win) {
slouken@10496
   320
        return ;
slouken@10496
   321
    }
slouken@10496
   322
slouken@10496
   323
    SDL_VERSION(&info.version);
slouken@10496
   324
    if (!SDL_GetWindowWMInfo(focused_win, &info)) {
slouken@10496
   325
        return;
slouken@10496
   326
    }
slouken@10496
   327
slouken@10496
   328
    SDL_GetWindowPosition(focused_win, &x, &y);
slouken@10496
   329
slouken@10496
   330
#if SDL_VIDEO_DRIVER_X11
slouken@10496
   331
    if (info.subsystem == SDL_SYSWM_X11) {
slouken@10496
   332
        SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(focused_win)->driverdata;
slouken@10496
   333
slouken@10496
   334
        Display *x_disp = info.info.x11.display;
slouken@10496
   335
        Window x_win = info.info.x11.window;
slouken@10496
   336
        int x_screen = displaydata->screen;
slouken@10496
   337
        Window unused;
slouken@10496
   338
        X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
slouken@10496
   339
    }
slouken@10496
   340
#endif
slouken@10496
   341
slouken@10496
   342
    if (cursor->x == -1 && cursor->y == -1 && cursor->w == 0 && cursor->h == 0) {
icculus@10780
   343
        /* move to bottom left */
slouken@10496
   344
        int w = 0, h = 0;
slouken@10496
   345
        SDL_GetWindowSize(focused_win, &w, &h);
slouken@10496
   346
        cursor->x = 0;
slouken@10496
   347
        cursor->y = h;
slouken@10496
   348
    }
slouken@10496
   349
slouken@10496
   350
    x += cursor->x;
slouken@10496
   351
    y += cursor->y;
slouken@10496
   352
icculus@11042
   353
    SDL_DBus_CallVoidMethod(fcitx_client.servicename, fcitx_client.icname, FCITX_IC_DBUS_INTERFACE, "SetCursorRect",
icculus@11042
   354
        DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &cursor->w, DBUS_TYPE_INT32, &cursor->h, DBUS_TYPE_INVALID);
slouken@10496
   355
}
slouken@10496
   356
slouken@10496
   357
void
slouken@10721
   358
SDL_Fcitx_PumpEvents(void)
slouken@10496
   359
{
slouken@10496
   360
    SDL_DBusContext *dbus = fcitx_client.dbus;
slouken@10496
   361
    DBusConnection *conn = dbus->session_conn;
slouken@10496
   362
slouken@10496
   363
    dbus->connection_read_write(conn, 0);
slouken@10496
   364
slouken@10496
   365
    while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {
slouken@10496
   366
        /* Do nothing, actual work happens in DBus_MessageFilter */
slouken@10496
   367
        usleep(10);
slouken@10496
   368
    }
slouken@10496
   369
}
slouken@10496
   370
slouken@10533
   371
#endif /* HAVE_FCITX_FRONTEND_H */
slouken@10533
   372
slouken@10496
   373
/* vi: set ts=4 sw=4 expandtab: */