src/core/linux/SDL_ibus.c
author Alex Baines <alex@abaines.me.uk>
Sun, 01 Feb 2015 21:08:54 +0000
changeset 9645 b1e7169d1dcb
parent 9619 b94b6d0bff0f
child 9646 9d4917e2d909
permissions -rw-r--r--
[ibus] Send an empty TextEditing event when the text is cleared by pressing backspace.
alex@8889
     1
/*
alex@8889
     2
  Simple DirectMedia Layer
slouken@9619
     3
  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
alex@8889
     4
alex@8889
     5
  This software is provided 'as-is', without any express or implied
alex@8889
     6
  warranty.  In no event will the authors be held liable for any damages
alex@8889
     7
  arising from the use of this software.
alex@8889
     8
alex@8889
     9
  Permission is granted to anyone to use this software for any purpose,
alex@8889
    10
  including commercial applications, and to alter it and redistribute it
alex@8889
    11
  freely, subject to the following restrictions:
alex@8889
    12
alex@8889
    13
  1. The origin of this software must not be misrepresented; you must not
alex@8889
    14
     claim that you wrote the original software. If you use this software
alex@8889
    15
     in a product, an acknowledgment in the product documentation would be
alex@8889
    16
     appreciated but is not required.
alex@8889
    17
  2. Altered source versions must be plainly marked as such, and must not be
alex@8889
    18
     misrepresented as being the original software.
alex@8889
    19
  3. This notice may not be removed or altered from any source distribution.
alex@8889
    20
*/
alex@8889
    21
#include "../../SDL_internal.h"
alex@8889
    22
alex@8889
    23
#ifdef HAVE_IBUS_IBUS_H
alex@8889
    24
#include "SDL.h"
alex@9095
    25
#include "SDL_syswm.h"
alex@8889
    26
#include "SDL_ibus.h"
alex@8889
    27
#include "SDL_dbus.h"
alex@8889
    28
#include "../../video/SDL_sysvideo.h"
alex@8889
    29
#include "../../events/SDL_keyboard_c.h"
alex@9095
    30
alex@9095
    31
#if SDL_VIDEO_DRIVER_X11
alex@9095
    32
    #include "../../video/x11/SDL_x11video.h"
alex@9095
    33
#endif
alex@9095
    34
alex@8889
    35
#include <sys/inotify.h>
alex@8889
    36
#include <unistd.h>
alex@8889
    37
#include <fcntl.h>
alex@8889
    38
alex@8889
    39
static const char IBUS_SERVICE[]         = "org.freedesktop.IBus";
alex@8889
    40
static const char IBUS_PATH[]            = "/org/freedesktop/IBus";
alex@8889
    41
static const char IBUS_INTERFACE[]       = "org.freedesktop.IBus";
alex@8889
    42
static const char IBUS_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
alex@8889
    43
alex@8889
    44
static char *input_ctx_path = NULL;
alex@8889
    45
static SDL_Rect ibus_cursor_rect = {0};
alex@8889
    46
static DBusConnection *ibus_conn = NULL;
alex@8889
    47
static char *ibus_addr_file = NULL;
alex@9096
    48
int inotify_fd = -1, inotify_wd = -1;
alex@8889
    49
alex@8889
    50
static Uint32
alex@8889
    51
IBus_ModState(void)
alex@8889
    52
{
alex@8889
    53
    Uint32 ibus_mods = 0;
alex@8889
    54
    SDL_Keymod sdl_mods = SDL_GetModState();
alex@8889
    55
    
alex@8889
    56
    /* Not sure about MOD3, MOD4 and HYPER mappings */
icculus@9107
    57
    if (sdl_mods & KMOD_LSHIFT) ibus_mods |= IBUS_SHIFT_MASK;
icculus@9107
    58
    if (sdl_mods & KMOD_CAPS)   ibus_mods |= IBUS_LOCK_MASK;
icculus@9107
    59
    if (sdl_mods & KMOD_LCTRL)  ibus_mods |= IBUS_CONTROL_MASK;
icculus@9107
    60
    if (sdl_mods & KMOD_LALT)   ibus_mods |= IBUS_MOD1_MASK;
icculus@9107
    61
    if (sdl_mods & KMOD_NUM)    ibus_mods |= IBUS_MOD2_MASK;
icculus@9107
    62
    if (sdl_mods & KMOD_MODE)   ibus_mods |= IBUS_MOD5_MASK;
icculus@9107
    63
    if (sdl_mods & KMOD_LGUI)   ibus_mods |= IBUS_SUPER_MASK;
icculus@9107
    64
    if (sdl_mods & KMOD_RGUI)   ibus_mods |= IBUS_META_MASK;
alex@8889
    65
alex@8889
    66
    return ibus_mods;
alex@8889
    67
}
alex@8889
    68
alex@8889
    69
static const char *
alex@8889
    70
IBus_GetVariantText(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus)
alex@8889
    71
{
alex@8889
    72
    /* The text we need is nested weirdly, use dbus-monitor to see the structure better */
alex@8889
    73
    const char *text = NULL;
icculus@9108
    74
    const char *struct_id = NULL;
alex@8889
    75
    DBusMessageIter sub1, sub2;
alex@8889
    76
icculus@9107
    77
    if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) {
alex@8889
    78
        return NULL;
alex@8889
    79
    }
alex@8889
    80
    
alex@8889
    81
    dbus->message_iter_recurse(iter, &sub1);
alex@8889
    82
    
icculus@9107
    83
    if (dbus->message_iter_get_arg_type(&sub1) != DBUS_TYPE_STRUCT) {
alex@8889
    84
        return NULL;
alex@8889
    85
    }
alex@8889
    86
    
alex@8889
    87
    dbus->message_iter_recurse(&sub1, &sub2);
alex@8889
    88
    
icculus@9107
    89
    if (dbus->message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
alex@8889
    90
        return NULL;
alex@8889
    91
    }
alex@8889
    92
    
alex@8889
    93
    dbus->message_iter_get_basic(&sub2, &struct_id);
icculus@9107
    94
    if (!struct_id || SDL_strncmp(struct_id, "IBusText", sizeof("IBusText")) != 0) {
alex@8889
    95
        return NULL;
alex@8889
    96
    }
alex@8889
    97
    
alex@8889
    98
    dbus->message_iter_next(&sub2);
alex@8889
    99
    dbus->message_iter_next(&sub2);
alex@8889
   100
    
icculus@9107
   101
    if (dbus->message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
alex@8889
   102
        return NULL;
alex@8889
   103
    }
alex@8889
   104
    
alex@8889
   105
    dbus->message_iter_get_basic(&sub2, &text);
alex@8889
   106
    
alex@8889
   107
    return text;
alex@8889
   108
}
alex@8889
   109
alex@8889
   110
static size_t 
alex@8889
   111
IBus_utf8_strlen(const char *str)
alex@8889
   112
{
alex@8889
   113
    size_t utf8_len = 0;
alex@8889
   114
    const char *p;
alex@8889
   115
    
icculus@9107
   116
    for (p = str; *p; ++p) {
icculus@9107
   117
        if (!((*p & 0x80) && !(*p & 0x40))) {
alex@8889
   118
            ++utf8_len;
alex@8889
   119
        }
alex@8889
   120
    }
alex@8889
   121
    
alex@8889
   122
    return utf8_len;
alex@8889
   123
}
alex@8889
   124
alex@8889
   125
static DBusHandlerResult
alex@8889
   126
IBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *user_data)
alex@8889
   127
{
alex@8889
   128
    SDL_DBusContext *dbus = (SDL_DBusContext *)user_data;
alex@8889
   129
        
icculus@9107
   130
    if (dbus->message_is_signal(msg, IBUS_INPUT_INTERFACE, "CommitText")) {
alex@8889
   131
        DBusMessageIter iter;
icculus@9108
   132
        const char *text;
icculus@9107
   133
alex@8889
   134
        dbus->message_iter_init(msg, &iter);
alex@8889
   135
        
icculus@9108
   136
        text = IBus_GetVariantText(conn, &iter, dbus);
icculus@9107
   137
        if (text && *text) {
alex@8889
   138
            char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
alex@8889
   139
            size_t text_bytes = SDL_strlen(text), i = 0;
alex@8889
   140
            
icculus@9107
   141
            while (i < text_bytes) {
alex@8889
   142
                size_t sz = SDL_utf8strlcpy(buf, text+i, sizeof(buf));
alex@8889
   143
                SDL_SendKeyboardText(buf);
alex@8889
   144
                
alex@8889
   145
                i += sz;
alex@8889
   146
            }
alex@8889
   147
        }
alex@8889
   148
        
alex@8889
   149
        return DBUS_HANDLER_RESULT_HANDLED;
alex@8889
   150
    }
alex@8889
   151
    
icculus@9107
   152
    if (dbus->message_is_signal(msg, IBUS_INPUT_INTERFACE, "UpdatePreeditText")) {
alex@8889
   153
        DBusMessageIter iter;
icculus@9108
   154
        const char *text;
icculus@9108
   155
alex@8889
   156
        dbus->message_iter_init(msg, &iter);
icculus@9108
   157
        text = IBus_GetVariantText(conn, &iter, dbus);
alex@8889
   158
        
alex@9645
   159
        if (text) {
alex@8889
   160
            char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
alex@8889
   161
            size_t text_bytes = SDL_strlen(text), i = 0;
alex@8889
   162
            size_t cursor = 0;
alex@8889
   163
            
alex@9645
   164
            do {
alex@8889
   165
                size_t sz = SDL_utf8strlcpy(buf, text+i, sizeof(buf));
alex@8889
   166
                size_t chars = IBus_utf8_strlen(buf);
alex@8889
   167
                
alex@8889
   168
                SDL_SendEditingText(buf, cursor, chars);
alex@8889
   169
alex@8889
   170
                i += sz;
alex@8889
   171
                cursor += chars;
alex@9645
   172
            } while (i < text_bytes);
alex@8889
   173
        }
alex@8889
   174
        
alex@8889
   175
        SDL_IBus_UpdateTextRect(NULL);
alex@8889
   176
        
alex@8889
   177
        return DBUS_HANDLER_RESULT_HANDLED;
alex@8889
   178
    }
alex@8889
   179
    
icculus@9107
   180
    if (dbus->message_is_signal(msg, IBUS_INPUT_INTERFACE, "HidePreeditText")) {
icculus@9107
   181
        SDL_SendEditingText("", 0, 0);
icculus@9107
   182
        return DBUS_HANDLER_RESULT_HANDLED;
alex@9096
   183
    }
alex@9096
   184
    
alex@8889
   185
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
alex@8889
   186
}
alex@8889
   187
alex@8889
   188
static char *
alex@8889
   189
IBus_ReadAddressFromFile(const char *file_path)
alex@8889
   190
{
icculus@9108
   191
    char addr_buf[1024];
icculus@9108
   192
    SDL_bool success = SDL_FALSE;
icculus@9108
   193
    FILE *addr_file;
icculus@9108
   194
icculus@9108
   195
    addr_file = fopen(file_path, "r");
icculus@9107
   196
    if (!addr_file) {
alex@8889
   197
        return NULL;
alex@8889
   198
    }
alex@8889
   199
icculus@9107
   200
    while (fgets(addr_buf, sizeof(addr_buf), addr_file)) {
icculus@9107
   201
        if (SDL_strncmp(addr_buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=")-1) == 0) {
alex@8889
   202
            size_t sz = SDL_strlen(addr_buf);
icculus@9107
   203
            if (addr_buf[sz-1] == '\n') addr_buf[sz-1] = 0;
icculus@9107
   204
            if (addr_buf[sz-2] == '\r') addr_buf[sz-2] = 0;
alex@8889
   205
            success = SDL_TRUE;
alex@8889
   206
            break;
alex@8889
   207
        }
alex@8889
   208
    }
alex@8889
   209
alex@8889
   210
    fclose(addr_file);
alex@8889
   211
icculus@9107
   212
    if (success) {
alex@8889
   213
        return SDL_strdup(addr_buf + (sizeof("IBUS_ADDRESS=") - 1));
alex@8889
   214
    } else {
alex@8889
   215
        return NULL;
alex@8889
   216
    }
alex@8889
   217
}
alex@8889
   218
alex@8889
   219
static char *
alex@8889
   220
IBus_GetDBusAddressFilename(void)
alex@8889
   221
{
icculus@9108
   222
    SDL_DBusContext *dbus;
icculus@9108
   223
    const char *disp_env;
icculus@9108
   224
    char config_dir[PATH_MAX];
icculus@9108
   225
    char *display = NULL;
icculus@9108
   226
    const char *addr;
icculus@9108
   227
    const char *conf_env;
icculus@9108
   228
    char *key;
icculus@9108
   229
    char file_path[PATH_MAX];
slouken@9114
   230
    const char *host;
slouken@9114
   231
    char *disp_num, *screen_num;
icculus@9108
   232
icculus@9107
   233
    if (ibus_addr_file) {
alex@8889
   234
        return SDL_strdup(ibus_addr_file);
alex@8889
   235
    }
alex@8889
   236
    
icculus@9108
   237
    dbus = SDL_DBus_GetContext();
icculus@9107
   238
    if (!dbus) {
alex@8889
   239
        return NULL;
alex@8889
   240
    }
alex@8889
   241
    
alex@8889
   242
    /* Use this environment variable if it exists. */
icculus@9108
   243
    addr = SDL_getenv("IBUS_ADDRESS");
icculus@9107
   244
    if (addr && *addr) {
alex@8889
   245
        return SDL_strdup(addr);
alex@8889
   246
    }
alex@8889
   247
    
alex@8889
   248
    /* Otherwise, we have to get the hostname, display, machine id, config dir
alex@8889
   249
       and look up the address from a filepath using all those bits, eek. */
icculus@9108
   250
    disp_env = SDL_getenv("DISPLAY");
icculus@9108
   251
icculus@9107
   252
    if (!disp_env || !*disp_env) {
alex@8889
   253
        display = SDL_strdup(":0.0");
alex@8889
   254
    } else {
alex@8889
   255
        display = SDL_strdup(disp_env);
alex@8889
   256
    }
alex@8889
   257
    
slouken@9114
   258
    host = display;
slouken@9114
   259
    disp_num   = SDL_strrchr(display, ':');
slouken@9114
   260
    screen_num = SDL_strrchr(display, '.');
alex@8889
   261
    
icculus@9107
   262
    if (!disp_num) {
alex@8889
   263
        SDL_free(display);
alex@8889
   264
        return NULL;
alex@8889
   265
    }
alex@8889
   266
    
alex@8889
   267
    *disp_num = 0;
alex@8889
   268
    disp_num++;
alex@8889
   269
    
icculus@9107
   270
    if (screen_num) {
alex@8889
   271
        *screen_num = 0;
alex@8889
   272
    }
alex@8889
   273
    
icculus@9107
   274
    if (!*host) {
alex@8889
   275
        host = "unix";
alex@8889
   276
    }
alex@8889
   277
        
alex@8889
   278
    SDL_memset(config_dir, 0, sizeof(config_dir));
alex@8889
   279
    
icculus@9108
   280
    conf_env = SDL_getenv("XDG_CONFIG_HOME");
icculus@9107
   281
    if (conf_env && *conf_env) {
alex@8889
   282
        SDL_strlcpy(config_dir, conf_env, sizeof(config_dir));
alex@8889
   283
    } else {
alex@8889
   284
        const char *home_env = SDL_getenv("HOME");
icculus@9107
   285
        if (!home_env || !*home_env) {
alex@8889
   286
            SDL_free(display);
alex@8889
   287
            return NULL;
alex@8889
   288
        }
alex@8889
   289
        SDL_snprintf(config_dir, sizeof(config_dir), "%s/.config", home_env);
alex@8889
   290
    }
alex@8889
   291
    
icculus@9108
   292
    key = dbus->get_local_machine_id();
icculus@9108
   293
alex@8889
   294
    SDL_memset(file_path, 0, sizeof(file_path));
alex@8889
   295
    SDL_snprintf(file_path, sizeof(file_path), "%s/ibus/bus/%s-%s-%s", 
alex@8889
   296
                                               config_dir, key, host, disp_num);
alex@8889
   297
    dbus->free(key);
alex@8889
   298
    SDL_free(display);
alex@8889
   299
    
alex@8889
   300
    return SDL_strdup(file_path);
alex@8889
   301
}
alex@8889
   302
alex@9097
   303
static SDL_bool IBus_CheckConnection(SDL_DBusContext *dbus);
alex@9097
   304
alex@9097
   305
static void
alex@9097
   306
IBus_SetCapabilities(void *data, const char *name, const char *old_val,
alex@9097
   307
                                                   const char *internal_editing)
alex@9097
   308
{
alex@9097
   309
    SDL_DBusContext *dbus = SDL_DBus_GetContext();
alex@9097
   310
    
icculus@9107
   311
    if (IBus_CheckConnection(dbus)) {
alex@9097
   312
alex@9097
   313
        DBusMessage *msg = dbus->message_new_method_call(IBUS_SERVICE,
alex@9097
   314
                                                         input_ctx_path,
alex@9097
   315
                                                         IBUS_INPUT_INTERFACE,
alex@9097
   316
                                                         "SetCapabilities");
icculus@9107
   317
        if (msg) {
alex@9097
   318
            Uint32 caps = IBUS_CAP_FOCUS;
icculus@9107
   319
            if (!(internal_editing && *internal_editing == '1')) {
alex@9097
   320
                caps |= IBUS_CAP_PREEDIT_TEXT;
alex@9097
   321
            }
alex@9097
   322
            
alex@9097
   323
            dbus->message_append_args(msg,
alex@9097
   324
                                      DBUS_TYPE_UINT32, &caps,
alex@9097
   325
                                      DBUS_TYPE_INVALID);
alex@9097
   326
        }
alex@9097
   327
        
icculus@9107
   328
        if (msg) {
icculus@9107
   329
            if (dbus->connection_send(ibus_conn, msg, NULL)) {
alex@9097
   330
                dbus->connection_flush(ibus_conn);
alex@9097
   331
            }
alex@9097
   332
            dbus->message_unref(msg);
alex@9097
   333
        }
alex@9097
   334
    }
alex@9097
   335
}
alex@9097
   336
alex@9097
   337
alex@8889
   338
static SDL_bool
alex@8889
   339
IBus_SetupConnection(SDL_DBusContext *dbus, const char* addr)
alex@8889
   340
{
alex@8889
   341
    const char *path = NULL;
alex@8889
   342
    SDL_bool result = SDL_FALSE;
icculus@9108
   343
    DBusMessage *msg;
icculus@9108
   344
alex@8889
   345
    ibus_conn = dbus->connection_open_private(addr, NULL);
alex@8889
   346
icculus@9107
   347
    if (!ibus_conn) {
alex@8889
   348
        return SDL_FALSE;
alex@8889
   349
    }
alex@8889
   350
alex@8889
   351
    dbus->connection_flush(ibus_conn);
alex@8889
   352
    
icculus@9107
   353
    if (!dbus->bus_register(ibus_conn, NULL)) {
alex@8889
   354
        ibus_conn = NULL;
alex@8889
   355
        return SDL_FALSE;
alex@8889
   356
    }
alex@8889
   357
    
alex@8889
   358
    dbus->connection_flush(ibus_conn);
alex@8889
   359
icculus@9108
   360
    msg = dbus->message_new_method_call(IBUS_SERVICE, IBUS_PATH, IBUS_INTERFACE, "CreateInputContext");
icculus@9107
   361
    if (msg) {
alex@8889
   362
        const char *client_name = "SDL2_Application";
alex@8889
   363
        dbus->message_append_args(msg,
alex@8889
   364
                                  DBUS_TYPE_STRING, &client_name,
alex@8889
   365
                                  DBUS_TYPE_INVALID);
alex@8889
   366
    }
alex@8889
   367
    
icculus@9107
   368
    if (msg) {
alex@8889
   369
        DBusMessage *reply;
alex@8889
   370
        
alex@8889
   371
        reply = dbus->connection_send_with_reply_and_block(ibus_conn, msg, 1000, NULL);
icculus@9107
   372
        if (reply) {
icculus@9107
   373
            if (dbus->message_get_args(reply, NULL,
alex@8889
   374
                                       DBUS_TYPE_OBJECT_PATH, &path,
icculus@9107
   375
                                       DBUS_TYPE_INVALID)) {
icculus@9107
   376
                if (input_ctx_path) {
alex@8889
   377
                    SDL_free(input_ctx_path);
alex@8889
   378
                }
alex@8889
   379
                input_ctx_path = SDL_strdup(path);
alex@8889
   380
                result = SDL_TRUE;                          
alex@8889
   381
            }
alex@8889
   382
            dbus->message_unref(reply);
alex@8889
   383
        }
alex@8889
   384
        dbus->message_unref(msg);
alex@8889
   385
    }
alex@8889
   386
icculus@9107
   387
    if (result) {
slouken@9098
   388
        SDL_AddHintCallback(SDL_HINT_IME_INTERNAL_EDITING, &IBus_SetCapabilities, NULL);
alex@8889
   389
        
alex@8889
   390
        dbus->bus_add_match(ibus_conn, "type='signal',interface='org.freedesktop.IBus.InputContext'", NULL);
alex@8889
   391
        dbus->connection_add_filter(ibus_conn, &IBus_MessageFilter, dbus, NULL);
alex@8889
   392
        dbus->connection_flush(ibus_conn);
alex@8889
   393
    }
alex@8889
   394
alex@9096
   395
    SDL_IBus_SetFocus(SDL_GetKeyboardFocus() != NULL);
alex@8889
   396
    SDL_IBus_UpdateTextRect(NULL);
alex@8889
   397
    
alex@8889
   398
    return result;
alex@8889
   399
}
alex@8889
   400
alex@8889
   401
static SDL_bool
alex@8889
   402
IBus_CheckConnection(SDL_DBusContext *dbus)
alex@8889
   403
{
icculus@9107
   404
    if (!dbus) return SDL_FALSE;
alex@8889
   405
    
icculus@9107
   406
    if (ibus_conn && dbus->connection_get_is_connected(ibus_conn)) {
alex@8889
   407
        return SDL_TRUE;
alex@8889
   408
    }
alex@8889
   409
    
icculus@9107
   410
    if (inotify_fd > 0 && inotify_wd > 0) {
alex@8889
   411
        char buf[1024];
alex@8889
   412
        ssize_t readsize = read(inotify_fd, buf, sizeof(buf));
icculus@9107
   413
        if (readsize > 0) {
alex@8889
   414
        
alex@8889
   415
            char *p;
alex@8889
   416
            SDL_bool file_updated = SDL_FALSE;
alex@8889
   417
            
icculus@9107
   418
            for (p = buf; p < buf + readsize; /**/) {
alex@8889
   419
                struct inotify_event *event = (struct inotify_event*) p;
icculus@9107
   420
                if (event->len > 0) {
alex@8889
   421
                    char *addr_file_no_path = SDL_strrchr(ibus_addr_file, '/');
icculus@9107
   422
                    if (!addr_file_no_path) return SDL_FALSE;
alex@8889
   423
                 
icculus@9107
   424
                    if (SDL_strcmp(addr_file_no_path + 1, event->name) == 0) {
alex@8889
   425
                        file_updated = SDL_TRUE;
alex@8889
   426
                        break;
alex@8889
   427
                    }
alex@8889
   428
                }
alex@8889
   429
                
alex@8889
   430
                p += sizeof(struct inotify_event) + event->len;
alex@8889
   431
            }
alex@8889
   432
            
icculus@9107
   433
            if (file_updated) {
alex@8889
   434
                char *addr = IBus_ReadAddressFromFile(ibus_addr_file);
icculus@9107
   435
                if (addr) {
alex@8889
   436
                    SDL_bool result = IBus_SetupConnection(dbus, addr);
alex@8889
   437
                    SDL_free(addr);
alex@8889
   438
                    return result;
alex@8889
   439
                }
alex@8889
   440
            }
alex@8889
   441
        }
alex@8889
   442
    }
alex@8889
   443
    
alex@8889
   444
    return SDL_FALSE;
alex@8889
   445
}
alex@8889
   446
alex@8889
   447
SDL_bool
alex@8889
   448
SDL_IBus_Init(void)
alex@8889
   449
{
alex@8889
   450
    SDL_bool result = SDL_FALSE;
alex@8889
   451
    SDL_DBusContext *dbus = SDL_DBus_GetContext();
alex@8889
   452
    
icculus@9107
   453
    if (dbus) {
alex@8889
   454
        char *addr_file = IBus_GetDBusAddressFilename();
icculus@9108
   455
        char *addr;
icculus@9108
   456
        char *addr_file_dir;
icculus@9108
   457
icculus@9107
   458
        if (!addr_file) {
alex@8889
   459
            return SDL_FALSE;
alex@8889
   460
        }
alex@8889
   461
        
alex@8889
   462
        ibus_addr_file = SDL_strdup(addr_file);
alex@8889
   463
        
icculus@9108
   464
        addr = IBus_ReadAddressFromFile(addr_file);
urkle@9451
   465
        if (!addr) {
urkle@9451
   466
            return SDL_FALSE;
urkle@9451
   467
        }
alex@8889
   468
        
icculus@9107
   469
        if (inotify_fd < 0) {
alex@9096
   470
            inotify_fd = inotify_init();
alex@9096
   471
            fcntl(inotify_fd, F_SETFL, O_NONBLOCK);
alex@9096
   472
        }
alex@8889
   473
        
icculus@9108
   474
        addr_file_dir = SDL_strrchr(addr_file, '/');
icculus@9107
   475
        if (addr_file_dir) {
alex@8889
   476
            *addr_file_dir = 0;
alex@8889
   477
        }
alex@8889
   478
        
alex@9096
   479
        inotify_wd = inotify_add_watch(inotify_fd, addr_file, IN_CREATE | IN_MODIFY);
alex@8889
   480
        SDL_free(addr_file);
alex@8889
   481
        
alex@9462
   482
        if (addr) {
alex@9462
   483
            result = IBus_SetupConnection(dbus, addr);
alex@9462
   484
            SDL_free(addr);
alex@9462
   485
        }
alex@8889
   486
    }
alex@8889
   487
    
alex@8889
   488
    return result;
alex@8889
   489
}
alex@8889
   490
alex@8889
   491
void
alex@8889
   492
SDL_IBus_Quit(void)
alex@8889
   493
{   
icculus@9108
   494
    SDL_DBusContext *dbus;
icculus@9108
   495
icculus@9107
   496
    if (input_ctx_path) {
alex@8889
   497
        SDL_free(input_ctx_path);
alex@8889
   498
        input_ctx_path = NULL;
alex@8889
   499
    }
alex@8889
   500
    
icculus@9107
   501
    if (ibus_addr_file) {
alex@8889
   502
        SDL_free(ibus_addr_file);
alex@8889
   503
        ibus_addr_file = NULL;
alex@8889
   504
    }
alex@8889
   505
    
icculus@9108
   506
    dbus = SDL_DBus_GetContext();
alex@8889
   507
    
icculus@9107
   508
    if (dbus && ibus_conn) {
alex@8889
   509
        dbus->connection_close(ibus_conn);
alex@8889
   510
        dbus->connection_unref(ibus_conn);
alex@8889
   511
    }
alex@8889
   512
    
icculus@9107
   513
    if (inotify_fd > 0 && inotify_wd > 0) {
alex@9096
   514
        inotify_rm_watch(inotify_fd, inotify_wd);
alex@9096
   515
        inotify_wd = -1;
alex@9096
   516
    }
alex@9096
   517
    
slouken@9098
   518
    SDL_DelHintCallback(SDL_HINT_IME_INTERNAL_EDITING, &IBus_SetCapabilities, NULL);
alex@9097
   519
    
alex@8889
   520
    SDL_memset(&ibus_cursor_rect, 0, sizeof(ibus_cursor_rect));
alex@8889
   521
}
alex@8889
   522
alex@8889
   523
static void
alex@8889
   524
IBus_SimpleMessage(const char *method)
alex@8889
   525
{   
alex@8889
   526
    SDL_DBusContext *dbus = SDL_DBus_GetContext();
alex@8889
   527
    
icculus@9107
   528
    if (IBus_CheckConnection(dbus)) {
alex@8889
   529
        DBusMessage *msg = dbus->message_new_method_call(IBUS_SERVICE,
alex@8889
   530
                                                         input_ctx_path,
alex@8889
   531
                                                         IBUS_INPUT_INTERFACE,
alex@8889
   532
                                                         method);
icculus@9107
   533
        if (msg) {
icculus@9107
   534
            if (dbus->connection_send(ibus_conn, msg, NULL)) {
alex@8889
   535
                dbus->connection_flush(ibus_conn);
alex@8889
   536
            }
alex@8889
   537
            dbus->message_unref(msg);
alex@8889
   538
        }
alex@8889
   539
    }
alex@8889
   540
}
alex@8889
   541
alex@8889
   542
void
alex@8889
   543
SDL_IBus_SetFocus(SDL_bool focused)
alex@8889
   544
{ 
alex@8889
   545
    const char *method = focused ? "FocusIn" : "FocusOut";
alex@8889
   546
    IBus_SimpleMessage(method);
alex@8889
   547
}
alex@8889
   548
alex@8889
   549
void
alex@8889
   550
SDL_IBus_Reset(void)
alex@8889
   551
{
alex@8889
   552
    IBus_SimpleMessage("Reset");
alex@8889
   553
}
alex@8889
   554
alex@8889
   555
SDL_bool
alex@8889
   556
SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
alex@8889
   557
{ 
alex@8889
   558
    SDL_bool result = SDL_FALSE;   
alex@8889
   559
    SDL_DBusContext *dbus = SDL_DBus_GetContext();
alex@8889
   560
    
icculus@9107
   561
    if (IBus_CheckConnection(dbus)) {
alex@8889
   562
        DBusMessage *msg = dbus->message_new_method_call(IBUS_SERVICE,
alex@8889
   563
                                                         input_ctx_path,
alex@8889
   564
                                                         IBUS_INPUT_INTERFACE,
alex@8889
   565
                                                         "ProcessKeyEvent");
icculus@9107
   566
        if (msg) {
alex@8889
   567
            Uint32 mods = IBus_ModState();
alex@8889
   568
            dbus->message_append_args(msg,
alex@8889
   569
                                      DBUS_TYPE_UINT32, &keysym,
alex@8889
   570
                                      DBUS_TYPE_UINT32, &keycode,
alex@8889
   571
                                      DBUS_TYPE_UINT32, &mods,
alex@8889
   572
                                      DBUS_TYPE_INVALID);
alex@8889
   573
        }
alex@8889
   574
        
icculus@9107
   575
        if (msg) {
alex@8889
   576
            DBusMessage *reply;
alex@8889
   577
            
alex@8889
   578
            reply = dbus->connection_send_with_reply_and_block(ibus_conn, msg, 300, NULL);
icculus@9107
   579
            if (reply) {
icculus@9107
   580
                if (!dbus->message_get_args(reply, NULL,
alex@8889
   581
                                           DBUS_TYPE_BOOLEAN, &result,
icculus@9107
   582
                                           DBUS_TYPE_INVALID)) {
alex@8889
   583
                    result = SDL_FALSE;                         
alex@8889
   584
                }
alex@8889
   585
                dbus->message_unref(reply);
alex@8889
   586
            }
alex@8889
   587
            dbus->message_unref(msg);
alex@8889
   588
        }
alex@8889
   589
        
alex@8889
   590
    }
alex@8889
   591
    
alex@9097
   592
    SDL_IBus_UpdateTextRect(NULL);
alex@9097
   593
alex@8889
   594
    return result;
alex@8889
   595
}
alex@8889
   596
alex@8889
   597
void
alex@8889
   598
SDL_IBus_UpdateTextRect(SDL_Rect *rect)
alex@8889
   599
{
slouken@9113
   600
    SDL_Window *focused_win;
slouken@9113
   601
    SDL_SysWMinfo info;
icculus@9108
   602
    int x = 0, y = 0;
icculus@9108
   603
    SDL_DBusContext *dbus;
icculus@9108
   604
icculus@9107
   605
    if (rect) {
alex@8889
   606
        SDL_memcpy(&ibus_cursor_rect, rect, sizeof(ibus_cursor_rect));
alex@8889
   607
    }
alex@8889
   608
slouken@9113
   609
    focused_win = SDL_GetKeyboardFocus();
icculus@9107
   610
    if (!focused_win) {
icculus@9107
   611
        return;
icculus@9107
   612
    }
icculus@9107
   613
alex@9095
   614
    SDL_VERSION(&info.version);
icculus@9107
   615
    if (!SDL_GetWindowWMInfo(focused_win, &info)) {
icculus@9107
   616
        return;
icculus@9107
   617
    }
icculus@9108
   618
alex@9095
   619
    SDL_GetWindowPosition(focused_win, &x, &y);
alex@9095
   620
   
alex@9095
   621
#if SDL_VIDEO_DRIVER_X11    
icculus@9107
   622
    if (info.subsystem == SDL_SYSWM_X11) {
icculus@9107
   623
        SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(focused_win)->driverdata;
alex@9095
   624
            
alex@9095
   625
        Display *x_disp = info.info.x11.display;
alex@9095
   626
        Window x_win = info.info.x11.window;
alex@9095
   627
        int x_screen = displaydata->screen;
alex@9095
   628
        Window unused;
alex@9095
   629
            
icculus@9107
   630
        X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
alex@9095
   631
    }
alex@9095
   632
#endif
alex@8889
   633
alex@8889
   634
    x += ibus_cursor_rect.x;
alex@8889
   635
    y += ibus_cursor_rect.y;
alex@8889
   636
        
icculus@9108
   637
    dbus = SDL_DBus_GetContext();
alex@8889
   638
    
icculus@9107
   639
    if (IBus_CheckConnection(dbus)) {
alex@8889
   640
        DBusMessage *msg = dbus->message_new_method_call(IBUS_SERVICE,
alex@8889
   641
                                                         input_ctx_path,
alex@8889
   642
                                                         IBUS_INPUT_INTERFACE,
alex@8889
   643
                                                         "SetCursorLocation");
icculus@9107
   644
        if (msg) {
alex@8889
   645
            dbus->message_append_args(msg,
alex@8889
   646
                                      DBUS_TYPE_INT32, &x,
alex@8889
   647
                                      DBUS_TYPE_INT32, &y,
alex@8889
   648
                                      DBUS_TYPE_INT32, &ibus_cursor_rect.w,
alex@8889
   649
                                      DBUS_TYPE_INT32, &ibus_cursor_rect.h,
alex@8889
   650
                                      DBUS_TYPE_INVALID);
alex@8889
   651
        }
alex@8889
   652
        
icculus@9107
   653
        if (msg) {
icculus@9107
   654
            if (dbus->connection_send(ibus_conn, msg, NULL)) {
alex@8889
   655
                dbus->connection_flush(ibus_conn);
alex@8889
   656
            }
alex@8889
   657
            dbus->message_unref(msg);
alex@8889
   658
        }
alex@8889
   659
    }
alex@8889
   660
}
alex@8889
   661
alex@8889
   662
void
alex@8889
   663
SDL_IBus_PumpEvents(void)
alex@8889
   664
{
alex@8889
   665
    SDL_DBusContext *dbus = SDL_DBus_GetContext();
alex@8889
   666
    
icculus@9107
   667
    if (IBus_CheckConnection(dbus)) {
alex@8889
   668
        dbus->connection_read_write(ibus_conn, 0);
alex@8889
   669
    
icculus@9107
   670
        while (dbus->connection_dispatch(ibus_conn) == DBUS_DISPATCH_DATA_REMAINS) {
alex@8889
   671
            /* Do nothing, actual work happens in IBus_MessageFilter */
alex@8889
   672
        }
alex@8889
   673
    }
alex@8889
   674
}
alex@8889
   675
alex@8889
   676
#endif