Add IBus IME Support, move DBus code to its own file. (v3.3 squashed)

This commit is contained in:
Alex Baines 2014-06-18 20:11:39 +01:00
parent 0d673844ac
commit 41a39837ca
11 changed files with 1104 additions and 232 deletions

View file

@ -2130,6 +2130,43 @@ AC_HELP_STRING([--enable-dbus], [enable D-Bus support [[default=yes]]]),
if test x$have_dbus_dbus_h_hdr = xyes; then
AC_DEFINE(HAVE_DBUS_DBUS_H, 1, [ ])
EXTRA_CFLAGS="$EXTRA_CFLAGS $DBUS_CFLAGS"
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_dbus.c"
fi
fi
fi
}
dnl See if the platform has libibus IME support.
CheckIBus()
{
AC_ARG_ENABLE(ibus,
AC_HELP_STRING([--enable-ibus], [enable IBus support [[default=yes]]]),
, enable_ibus=yes)
if test x$enable_ibus = xyes; then
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
if test x$PKG_CONFIG != xno; then
IBUS_CFLAGS=`$PKG_CONFIG --cflags ibus-1.0`
save_CFLAGS="$CFLAGS"
CFLAGS="$save_CFLAGS $IBUS_CFLAGS"
AC_CHECK_HEADER(ibus-1.0/ibus.h,
have_ibus_ibus_h_hdr=yes,
have_ibus_ibus_h_hdr=no)
AC_CHECK_HEADER(sys/inotify.h,
have_inotify_inotify_h_hdr=yes,
have_inotify_inotify_h_hdr=no)
CFLAGS="$save_CFLAGS"
if test x$have_ibus_ibus_h_hdr = xyes; then
if test x$enable_dbus != xyes; then
AC_MSG_WARN([DBus support is required for IBus.])
have_ibus_ibus_h_hdr=no
elif test x$have_inotify_inotify_h_hdr != xyes; then
AC_MSG_WARN([INotify support is required for IBus.])
have_ibus_ibus_h_hdr=no
else
AC_DEFINE(HAVE_IBUS_IBUS_H, 1, [ ])
EXTRA_CFLAGS="$EXTRA_CFLAGS $IBUS_CFLAGS"
SOURCES="$SOURCES $srcdir/src/core/linux/SDL_ibus.c"
fi
fi
fi
fi
@ -2732,6 +2769,7 @@ case "$host" in
CheckWayland
CheckLibUDev
CheckDBus
CheckIBus
CheckInputEvents
CheckInputKD
CheckTslib
@ -3347,6 +3385,11 @@ if test x$have_dbus_dbus_h_hdr = xyes; then
else
SUMMARY="${SUMMARY}Using dbus : NO\n"
fi
if test x$have_ibus_ibus_h_hdr = xyes; then
SUMMARY="${SUMMARY}Using ibus : YES\n"
else
SUMMARY="${SUMMARY}Using ibus : NO\n"
fi
AC_CONFIG_COMMANDS([summary], [echo -en "$SUMMARY"], [SUMMARY="$SUMMARY"])
AC_OUTPUT

View file

@ -78,6 +78,7 @@
#undef HAVE_PTHREAD_NP_H
#undef HAVE_LIBUDEV_H
#undef HAVE_DBUS_DBUS_H
#undef HAVE_IBUS_IBUS_H
/* C library functions */
#undef HAVE_MALLOC

236
src/core/linux/SDL_dbus.c Normal file
View file

@ -0,0 +1,236 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 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"
#include "SDL_dbus.h"
#if SDL_USE_LIBDBUS
/* we never link directly to libdbus. */
#include "SDL_loadso.h"
static const char *dbus_library = "libdbus-1.so.3";
static void *dbus_handle = NULL;
static unsigned int screensaver_cookie = 0;
static SDL_DBusContext dbus = {0};
static int
load_dbus_syms(void)
{
#define SDL_DBUS_SYM2(x, y) \
if (!(dbus.x = SDL_LoadFunction(dbus_handle, #y))) return -1
#define SDL_DBUS_SYM(x) \
SDL_DBUS_SYM2(x, dbus_##x)
SDL_DBUS_SYM(bus_get_private);
SDL_DBUS_SYM(bus_register);
SDL_DBUS_SYM(bus_add_match);
SDL_DBUS_SYM(connection_open_private);
SDL_DBUS_SYM(connection_set_exit_on_disconnect);
SDL_DBUS_SYM(connection_get_is_connected);
SDL_DBUS_SYM(connection_add_filter);
SDL_DBUS_SYM(connection_send);
SDL_DBUS_SYM(connection_send_with_reply_and_block);
SDL_DBUS_SYM(connection_close);
SDL_DBUS_SYM(connection_unref);
SDL_DBUS_SYM(connection_flush);
SDL_DBUS_SYM(connection_read_write);
SDL_DBUS_SYM(connection_dispatch);
SDL_DBUS_SYM(message_is_signal);
SDL_DBUS_SYM(message_new_method_call);
SDL_DBUS_SYM(message_append_args);
SDL_DBUS_SYM(message_get_args);
SDL_DBUS_SYM(message_iter_init);
SDL_DBUS_SYM(message_iter_next);
SDL_DBUS_SYM(message_iter_get_basic);
SDL_DBUS_SYM(message_iter_get_arg_type);
SDL_DBUS_SYM(message_iter_recurse);
SDL_DBUS_SYM(message_unref);
SDL_DBUS_SYM(error_init);
SDL_DBUS_SYM(error_is_set);
SDL_DBUS_SYM(error_free);
SDL_DBUS_SYM(get_local_machine_id);
SDL_DBUS_SYM(free);
#undef SDL_DBUS_SYM
#undef SDL_DBUS_SYM2
return 0;
}
static void
UnloadDBUSLibrary(void)
{
if (dbus_handle != NULL) {
SDL_UnloadObject(dbus_handle);
dbus_handle = NULL;
}
}
static int
LoadDBUSLibrary(void)
{
int retval = 0;
if (dbus_handle == NULL) {
dbus_handle = SDL_LoadObject(dbus_library);
if (dbus_handle == NULL) {
retval = -1;
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
} else {
retval = load_dbus_syms();
if (retval < 0) {
UnloadDBUSLibrary();
}
}
}
return retval;
}
void
SDL_DBus_Init(void)
{
if (LoadDBUSLibrary() != -1) {
DBusError err;
dbus.error_init(&err);
dbus.session_conn = dbus.bus_get_private(DBUS_BUS_SESSION, &err);
if (dbus.error_is_set(&err)) {
dbus.error_free(&err);
if (dbus.session_conn) {
dbus.connection_unref(dbus.session_conn);
dbus.session_conn = NULL;
}
return; /* oh well */
}
dbus.connection_set_exit_on_disconnect(dbus.session_conn, 0);
}
}
void
SDL_DBus_Quit(void)
{
if (dbus.session_conn) {
dbus.connection_close(dbus.session_conn);
dbus.connection_unref(dbus.session_conn);
SDL_memset(&dbus, 0, sizeof(dbus));
}
UnloadDBUSLibrary();
}
SDL_DBusContext *
SDL_DBus_GetContext(void)
{
if(!dbus_handle || !dbus.session_conn){
SDL_DBus_Init();
}
if(dbus_handle && dbus.session_conn){
return &dbus;
} else {
return NULL;
}
}
void
SDL_DBus_ScreensaverTickle(void)
{
DBusConnection *conn = dbus.session_conn;
if (conn != NULL) {
DBusMessage *msg = dbus.message_new_method_call("org.gnome.ScreenSaver",
"/org/gnome/ScreenSaver",
"org.gnome.ScreenSaver",
"SimulateUserActivity");
if (msg != NULL) {
if (dbus.connection_send(conn, msg, NULL)) {
dbus.connection_flush(conn);
}
dbus.message_unref(msg);
}
}
}
SDL_bool
SDL_DBus_ScreensaverInhibit(SDL_bool inhibit)
{
DBusConnection *conn = dbus.session_conn;
if (conn == NULL)
return SDL_FALSE;
if (inhibit &&
screensaver_cookie != 0)
return SDL_TRUE;
if (!inhibit &&
screensaver_cookie == 0)
return SDL_TRUE;
if (inhibit) {
const char *app = "My SDL application";
const char *reason = "Playing a game";
DBusMessage *msg = dbus.message_new_method_call("org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"Inhibit");
if (msg != NULL) {
dbus.message_append_args (msg,
DBUS_TYPE_STRING, &app,
DBUS_TYPE_STRING, &reason,
DBUS_TYPE_INVALID);
}
if (msg != NULL) {
DBusMessage *reply;
reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
if (reply) {
if (!dbus.message_get_args(reply, NULL,
DBUS_TYPE_UINT32, &screensaver_cookie,
DBUS_TYPE_INVALID))
screensaver_cookie = 0;
dbus.message_unref(reply);
}
dbus.message_unref(msg);
}
if (screensaver_cookie == 0) {
return SDL_FALSE;
}
return SDL_TRUE;
} else {
DBusMessage *msg = dbus.message_new_method_call("org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"UnInhibit");
dbus.message_append_args (msg,
DBUS_TYPE_UINT32, &screensaver_cookie,
DBUS_TYPE_INVALID);
if (msg != NULL) {
if (dbus.connection_send(conn, msg, NULL)) {
dbus.connection_flush(conn);
}
dbus.message_unref(msg);
}
screensaver_cookie = 0;
return SDL_TRUE;
}
}
#endif

79
src/core/linux/SDL_dbus.h Normal file
View file

@ -0,0 +1,79 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 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"
#ifndef _SDL_dbus_h
#define _SDL_dbus_h
#ifdef HAVE_DBUS_DBUS_H
#define SDL_USE_LIBDBUS 1
#include "SDL_stdinc.h"
#include <dbus/dbus.h>
typedef struct SDL_DBusContext {
DBusConnection *session_conn;
DBusConnection *(*bus_get_private)(DBusBusType, DBusError *);
dbus_bool_t (*bus_register)(DBusConnection *, DBusError *);
void (*bus_add_match)(DBusConnection *, const char *, DBusError *);
DBusConnection * (*connection_open_private)(const char *, DBusError *);
void (*connection_set_exit_on_disconnect)(DBusConnection *, dbus_bool_t);
dbus_bool_t (*connection_get_is_connected)(DBusConnection *);
dbus_bool_t (*connection_add_filter)(DBusConnection *, DBusHandleMessageFunction,
void *, DBusFreeFunction);
dbus_bool_t (*connection_send)(DBusConnection *, DBusMessage *, dbus_uint32_t *);
DBusMessage *(*connection_send_with_reply_and_block)(DBusConnection *, DBusMessage *, int, DBusError *);
void (*connection_close)(DBusConnection *);
void (*connection_unref)(DBusConnection *);
void (*connection_flush)(DBusConnection *);
dbus_bool_t (*connection_read_write)(DBusConnection *, int);
DBusDispatchStatus (*connection_dispatch)(DBusConnection *);
dbus_bool_t (*message_is_signal)(DBusMessage *, const char *, const char *);
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_get_args)(DBusMessage *, DBusError *, int, ...);
dbus_bool_t (*message_iter_init)(DBusMessage *, DBusMessageIter *);
dbus_bool_t (*message_iter_next)(DBusMessageIter *);
void (*message_iter_get_basic)(DBusMessageIter *, void *);
int (*message_iter_get_arg_type)(DBusMessageIter *);
void (*message_iter_recurse)(DBusMessageIter *, DBusMessageIter *);
void (*message_unref)(DBusMessage *);
void (*error_init)(DBusError *);
dbus_bool_t (*error_is_set)(const DBusError *);
void (*error_free)(DBusError *);
char *(*get_local_machine_id)(void);
void (*free)(void *);
} SDL_DBusContext;
extern void SDL_DBus_Init(void);
extern void SDL_DBus_Quit(void);
extern SDL_DBusContext * SDL_DBus_GetContext(void);
extern void SDL_DBus_ScreensaverTickle(void);
extern SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit);
#endif /* HAVE_DBUS_DBUS_H */
#endif /* _SDL_dbus_h */
/* vi: set ts=4 sw=4 expandtab: */

593
src/core/linux/SDL_ibus.c Normal file
View file

@ -0,0 +1,593 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 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_IBUS_IBUS_H
#include "SDL.h"
#include "SDL_ibus.h"
#include "SDL_dbus.h"
#include "../../video/SDL_sysvideo.h"
#include "../../events/SDL_keyboard_c.h"
#include <sys/inotify.h>
#include <unistd.h>
#include <fcntl.h>
static const char IBUS_SERVICE[] = "org.freedesktop.IBus";
static const char IBUS_PATH[] = "/org/freedesktop/IBus";
static const char IBUS_INTERFACE[] = "org.freedesktop.IBus";
static const char IBUS_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
static char *input_ctx_path = NULL;
static SDL_Rect ibus_cursor_rect = {0};
static DBusConnection *ibus_conn = NULL;
static char *ibus_addr_file = NULL;
int inotify_fd = -1;
static Uint32
IBus_ModState(void)
{
Uint32 ibus_mods = 0;
SDL_Keymod sdl_mods = SDL_GetModState();
/* Not sure about MOD3, MOD4 and HYPER mappings */
if(sdl_mods & KMOD_LSHIFT) ibus_mods |= IBUS_SHIFT_MASK;
if(sdl_mods & KMOD_CAPS) ibus_mods |= IBUS_LOCK_MASK;
if(sdl_mods & KMOD_LCTRL) ibus_mods |= IBUS_CONTROL_MASK;
if(sdl_mods & KMOD_LALT) ibus_mods |= IBUS_MOD1_MASK;
if(sdl_mods & KMOD_NUM) ibus_mods |= IBUS_MOD2_MASK;
if(sdl_mods & KMOD_MODE) ibus_mods |= IBUS_MOD5_MASK;
if(sdl_mods & KMOD_LGUI) ibus_mods |= IBUS_SUPER_MASK;
if(sdl_mods & KMOD_RGUI) ibus_mods |= IBUS_META_MASK;
return ibus_mods;
}
static const char *
IBus_GetVariantText(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus)
{
/* The text we need is nested weirdly, use dbus-monitor to see the structure better */
const char *text = NULL;
DBusMessageIter sub1, sub2;
if(dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT){
return NULL;
}
dbus->message_iter_recurse(iter, &sub1);
if(dbus->message_iter_get_arg_type(&sub1) != DBUS_TYPE_STRUCT){
return NULL;
}
dbus->message_iter_recurse(&sub1, &sub2);
if(dbus->message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING){
return NULL;
}
const char *struct_id = NULL;
dbus->message_iter_get_basic(&sub2, &struct_id);
if(!struct_id || SDL_strncmp(struct_id, "IBusText", sizeof("IBusText")) != 0){
return NULL;
}
dbus->message_iter_next(&sub2);
dbus->message_iter_next(&sub2);
if(dbus->message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING){
return NULL;
}
dbus->message_iter_get_basic(&sub2, &text);
return text;
}
static size_t
IBus_utf8_strlen(const char *str)
{
size_t utf8_len = 0;
const char *p;
for(p = str; *p; ++p){
if(!((*p & 0x80) && !(*p & 0x40))){
++utf8_len;
}
}
return utf8_len;
}
static DBusHandlerResult
IBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *user_data)
{
SDL_DBusContext *dbus = (SDL_DBusContext *)user_data;
if(dbus->message_is_signal(msg, IBUS_INPUT_INTERFACE, "CommitText")){
DBusMessageIter iter;
dbus->message_iter_init(msg, &iter);
const char *text = IBus_GetVariantText(conn, &iter, dbus);
if(text && *text){
char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
size_t text_bytes = SDL_strlen(text), i = 0;
while(i < text_bytes){
size_t sz = SDL_utf8strlcpy(buf, text+i, sizeof(buf));
SDL_SendKeyboardText(buf);
i += sz;
}
}
return DBUS_HANDLER_RESULT_HANDLED;
}
if(dbus->message_is_signal(msg, IBUS_INPUT_INTERFACE, "UpdatePreeditText")){
DBusMessageIter iter;
dbus->message_iter_init(msg, &iter);
const char *text = IBus_GetVariantText(conn, &iter, dbus);
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 = IBus_utf8_strlen(buf);
SDL_SendEditingText(buf, cursor, chars);
i += sz;
cursor += chars;
}
} else {
SDL_SendEditingText("", 0, 0);
}
SDL_IBus_UpdateTextRect(NULL);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static char *
IBus_ReadAddressFromFile(const char *file_path)
{
FILE *addr_file = fopen(file_path, "r");
if(!addr_file){
return NULL;
}
char addr_buf[1024];
SDL_bool success = SDL_FALSE;
while(fgets(addr_buf, sizeof(addr_buf), addr_file)){
if(SDL_strncmp(addr_buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=")-1) == 0){
size_t sz = SDL_strlen(addr_buf);
if(addr_buf[sz-1] == '\n') addr_buf[sz-1] = 0;
if(addr_buf[sz-2] == '\r') addr_buf[sz-2] = 0;
success = SDL_TRUE;
break;
}
}
fclose(addr_file);
if(success){
return SDL_strdup(addr_buf + (sizeof("IBUS_ADDRESS=") - 1));
} else {
return NULL;
}
}
static char *
IBus_GetDBusAddressFilename(void)
{
if(ibus_addr_file){
return SDL_strdup(ibus_addr_file);
}
SDL_DBusContext *dbus = SDL_DBus_GetContext();
if(!dbus){
return NULL;
}
/* Use this environment variable if it exists. */
const char *addr = SDL_getenv("IBUS_ADDRESS");
if(addr && *addr){
return SDL_strdup(addr);
}
/* Otherwise, we have to get the hostname, display, machine id, config dir
and look up the address from a filepath using all those bits, eek. */
const char *disp_env = SDL_getenv("DISPLAY");
char *display = NULL;
if(!disp_env || !*disp_env){
display = SDL_strdup(":0.0");
} else {
display = SDL_strdup(disp_env);
}
const char *host = display;
char *disp_num = SDL_strrchr(display, ':'),
*screen_num = SDL_strrchr(display, '.');
if(!disp_num){
SDL_free(display);
return NULL;
}
*disp_num = 0;
disp_num++;
if(screen_num){
*screen_num = 0;
}
if(!*host){
host = "unix";
}
char config_dir[PATH_MAX];
SDL_memset(config_dir, 0, sizeof(config_dir));
const char *conf_env = SDL_getenv("XDG_CONFIG_HOME");
if(conf_env && *conf_env){
SDL_strlcpy(config_dir, conf_env, sizeof(config_dir));
} else {
const char *home_env = SDL_getenv("HOME");
if(!home_env || !*home_env){
SDL_free(display);
return NULL;
}
SDL_snprintf(config_dir, sizeof(config_dir), "%s/.config", home_env);
}
char *key = dbus->get_local_machine_id();
char file_path[PATH_MAX];
SDL_memset(file_path, 0, sizeof(file_path));
SDL_snprintf(file_path, sizeof(file_path), "%s/ibus/bus/%s-%s-%s",
config_dir, key, host, disp_num);
dbus->free(key);
SDL_free(display);
return SDL_strdup(file_path);
}
static SDL_bool
IBus_SetupConnection(SDL_DBusContext *dbus, const char* addr)
{
const char *path = NULL;
SDL_bool result = SDL_FALSE;
ibus_conn = dbus->connection_open_private(addr, NULL);
if(!ibus_conn){
return SDL_FALSE;
}
dbus->connection_flush(ibus_conn);
if(!dbus->bus_register(ibus_conn, NULL)){
ibus_conn = NULL;
return SDL_FALSE;
}
dbus->connection_flush(ibus_conn);
DBusMessage *msg = dbus->message_new_method_call(IBUS_SERVICE,
IBUS_PATH,
IBUS_INTERFACE,
"CreateInputContext");
if(msg){
const char *client_name = "SDL2_Application";
dbus->message_append_args(msg,
DBUS_TYPE_STRING, &client_name,
DBUS_TYPE_INVALID);
}
if(msg){
DBusMessage *reply;
reply = dbus->connection_send_with_reply_and_block(ibus_conn, msg, 1000, NULL);
if(reply){
if(dbus->message_get_args(reply, NULL,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID)){
if(input_ctx_path){
SDL_free(input_ctx_path);
}
input_ctx_path = SDL_strdup(path);
result = SDL_TRUE;
}
dbus->message_unref(reply);
}
dbus->message_unref(msg);
}
if(result){
DBusMessage *msg = dbus->message_new_method_call(IBUS_SERVICE,
input_ctx_path,
IBUS_INPUT_INTERFACE,
"SetCapabilities");
if(msg){
Uint32 caps = IBUS_CAP_FOCUS | IBUS_CAP_PREEDIT_TEXT;
dbus->message_append_args(msg,
DBUS_TYPE_UINT32, &caps,
DBUS_TYPE_INVALID);
}
if(msg){
if(dbus->connection_send(ibus_conn, msg, NULL)){
dbus->connection_flush(ibus_conn);
}
dbus->message_unref(msg);
}
dbus->bus_add_match(ibus_conn, "type='signal',interface='org.freedesktop.IBus.InputContext'", NULL);
dbus->connection_add_filter(ibus_conn, &IBus_MessageFilter, dbus, NULL);
dbus->connection_flush(ibus_conn);
}
SDL_IBus_SetFocus(SDL_GetFocusWindow() != NULL);
SDL_IBus_UpdateTextRect(NULL);
return result;
}
static SDL_bool
IBus_CheckConnection(SDL_DBusContext *dbus)
{
if(!dbus) return SDL_FALSE;
if(ibus_conn && dbus->connection_get_is_connected(ibus_conn)){
return SDL_TRUE;
}
if(inotify_fd != -1){
char buf[1024];
ssize_t readsize = read(inotify_fd, buf, sizeof(buf));
if(readsize > 0){
char *p;
SDL_bool file_updated = SDL_FALSE;
for(p = buf; p < buf + readsize; /**/){
struct inotify_event *event = (struct inotify_event*) p;
if(event->len > 0){
char *addr_file_no_path = SDL_strrchr(ibus_addr_file, '/');
if(!addr_file_no_path) return SDL_FALSE;
if(SDL_strcmp(addr_file_no_path + 1, event->name) == 0){
file_updated = SDL_TRUE;
break;
}
}
p += sizeof(struct inotify_event) + event->len;
}
if(file_updated){
char *addr = IBus_ReadAddressFromFile(ibus_addr_file);
if(addr){
SDL_bool result = IBus_SetupConnection(dbus, addr);
SDL_free(addr);
return result;
}
}
}
}
return SDL_FALSE;
}
SDL_bool
SDL_IBus_Init(void)
{
SDL_bool result = SDL_FALSE;
SDL_DBusContext *dbus = SDL_DBus_GetContext();
if(dbus){
char *addr_file = IBus_GetDBusAddressFilename();
if(!addr_file){
return SDL_FALSE;
}
ibus_addr_file = SDL_strdup(addr_file);
char *addr = IBus_ReadAddressFromFile(addr_file);
inotify_fd = inotify_init();
fcntl(inotify_fd, F_SETFL, O_NONBLOCK);
char *addr_file_dir = SDL_strrchr(addr_file, '/');
if(addr_file_dir){
*addr_file_dir = 0;
}
inotify_add_watch(inotify_fd, addr_file, IN_CREATE | IN_MODIFY);
SDL_free(addr_file);
result = IBus_SetupConnection(dbus, addr);
SDL_free(addr);
}
return result;
}
void
SDL_IBus_Quit(void)
{
if(input_ctx_path){
SDL_free(input_ctx_path);
input_ctx_path = NULL;
}
if(ibus_addr_file){
SDL_free(ibus_addr_file);
ibus_addr_file = NULL;
}
SDL_DBusContext *dbus = SDL_DBus_GetContext();
if(dbus && ibus_conn){
dbus->connection_close(ibus_conn);
dbus->connection_unref(ibus_conn);
}
SDL_memset(&ibus_cursor_rect, 0, sizeof(ibus_cursor_rect));
}
static void
IBus_SimpleMessage(const char *method)
{
SDL_DBusContext *dbus = SDL_DBus_GetContext();
if(IBus_CheckConnection(dbus)){
DBusMessage *msg = dbus->message_new_method_call(IBUS_SERVICE,
input_ctx_path,
IBUS_INPUT_INTERFACE,
method);
if(msg){
if(dbus->connection_send(ibus_conn, msg, NULL)){
dbus->connection_flush(ibus_conn);
}
dbus->message_unref(msg);
}
}
}
void
SDL_IBus_SetFocus(SDL_bool focused)
{
const char *method = focused ? "FocusIn" : "FocusOut";
IBus_SimpleMessage(method);
}
void
SDL_IBus_Reset(void)
{
IBus_SimpleMessage("Reset");
}
SDL_bool
SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
{
SDL_bool result = SDL_FALSE;
SDL_DBusContext *dbus = SDL_DBus_GetContext();
if(IBus_CheckConnection(dbus)){
DBusMessage *msg = dbus->message_new_method_call(IBUS_SERVICE,
input_ctx_path,
IBUS_INPUT_INTERFACE,
"ProcessKeyEvent");
if(msg){
Uint32 mods = IBus_ModState();
dbus->message_append_args(msg,
DBUS_TYPE_UINT32, &keysym,
DBUS_TYPE_UINT32, &keycode,
DBUS_TYPE_UINT32, &mods,
DBUS_TYPE_INVALID);
}
if(msg){
DBusMessage *reply;
reply = dbus->connection_send_with_reply_and_block(ibus_conn, msg, 300, NULL);
if(reply){
if(!dbus->message_get_args(reply, NULL,
DBUS_TYPE_BOOLEAN, &result,
DBUS_TYPE_INVALID)){
result = SDL_FALSE;
}
dbus->message_unref(reply);
}
dbus->message_unref(msg);
}
}
return result;
}
void
SDL_IBus_UpdateTextRect(SDL_Rect *rect)
{
if(rect){
SDL_memcpy(&ibus_cursor_rect, rect, sizeof(ibus_cursor_rect));
}
SDL_Window *focused_win = SDL_GetFocusWindow();
if(!focused_win) return;
int x = 0, y = 0;
SDL_GetWindowPosition(focused_win, &x, &y);
x += ibus_cursor_rect.x;
y += ibus_cursor_rect.y;
SDL_DBusContext *dbus = SDL_DBus_GetContext();
if(IBus_CheckConnection(dbus)){
DBusMessage *msg = dbus->message_new_method_call(IBUS_SERVICE,
input_ctx_path,
IBUS_INPUT_INTERFACE,
"SetCursorLocation");
if(msg){
dbus->message_append_args(msg,
DBUS_TYPE_INT32, &x,
DBUS_TYPE_INT32, &y,
DBUS_TYPE_INT32, &ibus_cursor_rect.w,
DBUS_TYPE_INT32, &ibus_cursor_rect.h,
DBUS_TYPE_INVALID);
}
if(msg){
if(dbus->connection_send(ibus_conn, msg, NULL)){
dbus->connection_flush(ibus_conn);
}
dbus->message_unref(msg);
}
}
}
void
SDL_IBus_PumpEvents(void)
{
SDL_DBusContext *dbus = SDL_DBus_GetContext();
if(IBus_CheckConnection(dbus)){
dbus->connection_read_write(ibus_conn, 0);
while(dbus->connection_dispatch(ibus_conn) == DBUS_DISPATCH_DATA_REMAINS){
/* Do nothing, actual work happens in IBus_MessageFilter */
}
}
}
#endif

58
src/core/linux/SDL_ibus.h Normal file
View file

@ -0,0 +1,58 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 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"
#ifndef _SDL_ibus_h
#define _SDL_ibus_h
#ifdef HAVE_IBUS_IBUS_H
#define SDL_USE_IBUS 1
#include "SDL_stdinc.h"
#include <ibus-1.0/ibus.h>
extern SDL_bool SDL_IBus_Init(void);
extern void SDL_IBus_Quit(void);
/* Lets the IBus server know about changes in window focus */
extern void SDL_IBus_SetFocus(SDL_bool focused);
/* Closes the candidate list and resets any text currently being edited */
extern void SDL_IBus_Reset(void);
/* Sends a keypress event to IBus, returns SDL_TRUE if IBus used this event to
update its candidate list or change input methods. PumpEvents should be
called some time after this, to recieve the TextInput / TextEditing event back. */
extern SDL_bool SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode);
/* Update the position of IBus' candidate list. If rect is NULL then this will
just reposition it relative to the focused window's new position. */
extern void SDL_IBus_UpdateTextRect(SDL_Rect *window_relative_rect);
/* Checks DBus for new IBus events, and calls SDL_SendKeyboardText /
SDL_SendEditingText for each event it finds */
extern void SDL_IBus_PumpEvents();
#endif /* HAVE_IBUS_IBUS_H */
#endif /* _SDL_ibus_h */
/* vi: set ts=4 sw=4 expandtab: */

View file

@ -492,6 +492,11 @@ X11_DispatchEvent(_THIS)
}
#ifdef DEBUG_XEVENTS
printf("window %p: FocusIn!\n", data);
#endif
#ifdef SDL_USE_IBUS
if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
SDL_IBus_SetFocus(SDL_TRUE);
}
#endif
if (data->pending_focus == PENDING_FOCUS_OUT &&
data->window == SDL_GetKeyboardFocus()) {
@ -529,6 +534,11 @@ X11_DispatchEvent(_THIS)
}
#ifdef DEBUG_XEVENTS
printf("window %p: FocusOut!\n", data);
#endif
#ifdef SDL_USE_IBUS
if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
SDL_IBus_SetFocus(SDL_FALSE);
}
#endif
data->pending_focus = PENDING_FOCUS_OUT;
data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_OUT_TIME;
@ -561,11 +571,14 @@ X11_DispatchEvent(_THIS)
KeySym keysym = NoSymbol;
char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
Status status = 0;
Bool handled = False;
#ifdef DEBUG_XEVENTS
printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
#endif
#ifndef SDL_USE_IBUS
SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]);
#endif
#if 1
if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
int min_keycode, max_keycode;
@ -591,9 +604,21 @@ X11_DispatchEvent(_THIS)
#else
XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
#endif
if (*text) {
SDL_SendKeyboardText(text);
#ifdef SDL_USE_IBUS
if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
if(!(handled = SDL_IBus_ProcessKeyEvent(keysym, keycode))){
#endif
if(*text){
SDL_SendKeyboardText(text);
}
#ifdef SDL_USE_IBUS
}
}
if (!handled) {
SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]);
}
#endif
}
break;
@ -663,6 +688,12 @@ X11_DispatchEvent(_THIS)
SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED,
xevent.xconfigure.x - border_left,
xevent.xconfigure.y - border_top);
#ifdef SDL_USE_IBUS
if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
/* Update IBus candidate list position */
SDL_IBus_UpdateTextRect(NULL);
}
#endif
}
if (xevent.xconfigure.width != data->last_xconfigure.width ||
xevent.xconfigure.height != data->last_xconfigure.height) {
@ -1079,14 +1110,20 @@ X11_PumpEvents(_THIS)
SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
X11_XResetScreenSaver(data->display);
#if SDL_USE_LIBDBUS
SDL_dbus_screensaver_tickle(_this);
#endif
#if SDL_USE_LIBDBUS
SDL_DBus_ScreensaverTickle();
#endif
data->screensaver_activity = now;
}
}
#ifdef SDL_USE_IBUS
if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
SDL_IBus_PumpEvents();
}
#endif
/* Keep processing pending events */
while (X11_Pending(data->display)) {
X11_DispatchEvent(_this);
@ -1107,12 +1144,12 @@ X11_SuspendScreenSaver(_THIS)
#endif /* SDL_VIDEO_DRIVER_X11_XSCRNSAVER */
#if SDL_USE_LIBDBUS
if (SDL_dbus_screensaver_inhibit(_this)) {
if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) {
return;
}
if (_this->suspend_screensaver) {
SDL_dbus_screensaver_tickle(_this);
SDL_DBus_ScreensaverTickle();
}
#endif

View file

@ -287,6 +287,10 @@ X11_InitKeyboard(_THIS)
SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
#ifdef SDL_USE_IBUS
SDL_IBus_Init();
#endif
return 0;
}
@ -320,6 +324,38 @@ X11_UpdateKeymap(_THIS)
void
X11_QuitKeyboard(_THIS)
{
#ifdef SDL_USE_IBUS
SDL_IBus_Quit();
#endif
}
void
X11_StartTextInput(_THIS)
{
#ifdef SDL_USE_IBUS
SDL_IBus_SetFocus(SDL_GetFocusWindow() != NULL);
#endif
}
void
X11_StopTextInput(_THIS)
{
#ifdef SDL_USE_IBUS
SDL_IBus_Reset();
#endif
}
void
X11_SetTextInputRect(_THIS, SDL_Rect *rect)
{
if (!rect) {
SDL_InvalidParamError("rect");
return;
}
#ifdef SDL_USE_IBUS
SDL_IBus_UpdateTextRect(rect);
#endif
}
#endif /* SDL_VIDEO_DRIVER_X11 */

View file

@ -26,6 +26,9 @@
extern int X11_InitKeyboard(_THIS);
extern void X11_UpdateKeymap(_THIS);
extern void X11_QuitKeyboard(_THIS);
extern void X11_StartTextInput(_THIS);
extern void X11_StopTextInput(_THIS);
extern void X11_SetTextInputRect(_THIS, SDL_Rect *rect);
#endif /* _SDL_x11keyboard_h */

View file

@ -39,220 +39,6 @@
#include "SDL_x11opengles.h"
#endif
/* !!! FIXME: move dbus stuff to somewhere under src/core/linux ... */
#if SDL_USE_LIBDBUS
/* we never link directly to libdbus. */
#include "SDL_loadso.h"
static const char *dbus_library = "libdbus-1.so.3";
static void *dbus_handle = NULL;
static unsigned int screensaver_cookie = 0;
/* !!! FIXME: this is kinda ugly. */
static SDL_bool
load_dbus_sym(const char *fn, void **addr)
{
*addr = SDL_LoadFunction(dbus_handle, fn);
if (*addr == NULL) {
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */
return SDL_FALSE;
}
return SDL_TRUE;
}
/* libdbus entry points... */
static DBusConnection *(*DBUS_dbus_bus_get_private)(DBusBusType, DBusError *) = NULL;
static void (*DBUS_dbus_connection_set_exit_on_disconnect)(DBusConnection *, dbus_bool_t) = NULL;
static dbus_bool_t (*DBUS_dbus_connection_send)(DBusConnection *, DBusMessage *, dbus_uint32_t *) = NULL;
static DBusMessage *(*DBUS_dbus_connection_send_with_reply_and_block)(DBusConnection *, DBusMessage *, int, DBusError *) = NULL;
static void (*DBUS_dbus_connection_close)(DBusConnection *) = NULL;
static void (*DBUS_dbus_connection_unref)(DBusConnection *) = NULL;
static void (*DBUS_dbus_connection_flush)(DBusConnection *) = NULL;
static DBusMessage *(*DBUS_dbus_message_new_method_call)(const char *, const char *, const char *, const char *) = NULL;
static dbus_bool_t (*DBUS_dbus_message_append_args)(DBusMessage *, int, ...) = NULL;
static dbus_bool_t (*DBUS_dbus_message_get_args)(DBusMessage *, DBusError *, int, ...) = NULL;
static void (*DBUS_dbus_message_unref)(DBusMessage *) = NULL;
static void (*DBUS_dbus_error_init)(DBusError *) = NULL;
static dbus_bool_t (*DBUS_dbus_error_is_set)(const DBusError *) = NULL;
static void (*DBUS_dbus_error_free)(DBusError *) = NULL;
static int
load_dbus_syms(void)
{
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
#define SDL_DBUS_SYM(x) \
if (!load_dbus_sym(#x, (void **) (char *) &DBUS_##x)) return -1
SDL_DBUS_SYM(dbus_bus_get_private);
SDL_DBUS_SYM(dbus_connection_set_exit_on_disconnect);
SDL_DBUS_SYM(dbus_connection_send);
SDL_DBUS_SYM(dbus_connection_send_with_reply_and_block);
SDL_DBUS_SYM(dbus_connection_close);
SDL_DBUS_SYM(dbus_connection_unref);
SDL_DBUS_SYM(dbus_connection_flush);
SDL_DBUS_SYM(dbus_message_append_args);
SDL_DBUS_SYM(dbus_message_get_args);
SDL_DBUS_SYM(dbus_message_new_method_call);
SDL_DBUS_SYM(dbus_message_unref);
SDL_DBUS_SYM(dbus_error_init);
SDL_DBUS_SYM(dbus_error_is_set);
SDL_DBUS_SYM(dbus_error_free);
#undef SDL_DBUS_SYM
return 0;
}
static void
UnloadDBUSLibrary(void)
{
if (dbus_handle != NULL) {
SDL_UnloadObject(dbus_handle);
dbus_handle = NULL;
}
}
static int
LoadDBUSLibrary(void)
{
int retval = 0;
if (dbus_handle == NULL) {
dbus_handle = SDL_LoadObject(dbus_library);
if (dbus_handle == NULL) {
retval = -1;
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
} else {
retval = load_dbus_syms();
if (retval < 0) {
UnloadDBUSLibrary();
}
}
}
return retval;
}
static void
X11_InitDBus(_THIS)
{
if (LoadDBUSLibrary() != -1) {
SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
DBusError err;
DBUS_dbus_error_init(&err);
data->dbus = DBUS_dbus_bus_get_private(DBUS_BUS_SESSION, &err);
if (DBUS_dbus_error_is_set(&err)) {
DBUS_dbus_error_free(&err);
if (data->dbus) {
DBUS_dbus_connection_unref(data->dbus);
data->dbus = NULL;
}
return; /* oh well */
}
DBUS_dbus_connection_set_exit_on_disconnect(data->dbus, 0);
}
}
static void
X11_QuitDBus(_THIS)
{
SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
if (data->dbus) {
DBUS_dbus_connection_close(data->dbus);
DBUS_dbus_connection_unref(data->dbus);
data->dbus = NULL;
}
}
void
SDL_dbus_screensaver_tickle(_THIS)
{
const SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
DBusConnection *conn = data->dbus;
if (conn != NULL) {
DBusMessage *msg = DBUS_dbus_message_new_method_call("org.gnome.ScreenSaver",
"/org/gnome/ScreenSaver",
"org.gnome.ScreenSaver",
"SimulateUserActivity");
if (msg != NULL) {
if (DBUS_dbus_connection_send(conn, msg, NULL)) {
DBUS_dbus_connection_flush(conn);
}
DBUS_dbus_message_unref(msg);
}
}
}
SDL_bool
SDL_dbus_screensaver_inhibit(_THIS)
{
const SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
DBusConnection *conn = data->dbus;
if (conn == NULL)
return SDL_FALSE;
if (_this->suspend_screensaver &&
screensaver_cookie != 0)
return SDL_TRUE;
if (!_this->suspend_screensaver &&
screensaver_cookie == 0)
return SDL_TRUE;
if (_this->suspend_screensaver) {
const char *app = "My SDL application";
const char *reason = "Playing a game";
DBusMessage *msg = DBUS_dbus_message_new_method_call("org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"Inhibit");
if (msg != NULL) {
DBUS_dbus_message_append_args (msg,
DBUS_TYPE_STRING, &app,
DBUS_TYPE_STRING, &reason,
DBUS_TYPE_INVALID);
}
if (msg != NULL) {
DBusMessage *reply;
reply = DBUS_dbus_connection_send_with_reply_and_block(conn, msg, 300, NULL);
if (reply) {
if (!DBUS_dbus_message_get_args(reply, NULL,
DBUS_TYPE_UINT32, &screensaver_cookie,
DBUS_TYPE_INVALID))
screensaver_cookie = 0;
DBUS_dbus_message_unref(reply);
}
DBUS_dbus_message_unref(msg);
}
if (screensaver_cookie == 0) {
return SDL_FALSE;
}
return SDL_TRUE;
} else {
DBusMessage *msg = DBUS_dbus_message_new_method_call("org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"UnInhibit");
DBUS_dbus_message_append_args (msg,
DBUS_TYPE_UINT32, &screensaver_cookie,
DBUS_TYPE_INVALID);
if (msg != NULL) {
if (DBUS_dbus_connection_send(conn, msg, NULL)) {
DBUS_dbus_connection_flush(conn);
}
DBUS_dbus_message_unref(msg);
}
screensaver_cookie = 0;
return SDL_TRUE;
}
}
#endif
/* Initialization/Query functions */
static int X11_VideoInit(_THIS);
static void X11_VideoQuit(_THIS);
@ -487,7 +273,10 @@ X11_CreateDevice(int devindex)
device->SetClipboardText = X11_SetClipboardText;
device->GetClipboardText = X11_GetClipboardText;
device->HasClipboardText = X11_HasClipboardText;
device->StartTextInput = X11_StartTextInput;
device->StopTextInput = X11_StopTextInput;
device->SetTextInputRect = X11_SetTextInputRect;
device->free = X11_DeleteDevice;
return device;
@ -635,7 +424,7 @@ X11_VideoInit(_THIS)
X11_InitTouch(_this);
#if SDL_USE_LIBDBUS
X11_InitDBus(_this);
SDL_DBus_Init();
#endif
return 0;
@ -659,7 +448,7 @@ X11_VideoQuit(_THIS)
X11_QuitTouch(_this);
#if SDL_USE_LIBDBUS
X11_QuitDBus(_this);
SDL_DBus_Quit();
#endif
}

View file

@ -54,8 +54,11 @@
#endif
#ifdef HAVE_DBUS_DBUS_H
#define SDL_USE_LIBDBUS 1
#include <dbus/dbus.h>
#include "../../core/linux/SDL_dbus.h"
#endif
#ifdef HAVE_IBUS_IBUS_H
#include "../../core/linux/SDL_ibus.h"
#endif
#include "SDL_x11dyn.h"
@ -114,16 +117,10 @@ typedef struct SDL_VideoData
SDL_Scancode key_layout[256];
SDL_bool selection_waiting;
#if SDL_USE_LIBDBUS
DBusConnection *dbus;
#endif
} SDL_VideoData;
extern SDL_bool X11_UseDirectColorVisuals(void);
SDL_bool SDL_dbus_screensaver_inhibit(_THIS);
void SDL_dbus_screensaver_tickle(_THIS);
#endif /* _SDL_x11video_h */
/* vi: set ts=4 sw=4 expandtab: */