2018-07-08 18:17:48 +03:00
|
|
|
|
//========================================================================
|
|
|
|
|
// GLFW 3.3 XKB - www.glfw.org
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>
|
|
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
//========================================================================
|
|
|
|
|
|
2018-07-29 19:13:02 +03:00
|
|
|
|
#define _GNU_SOURCE
|
2018-07-28 20:38:15 +03:00
|
|
|
|
#include <stdio.h>
|
2018-07-08 19:28:49 +03:00
|
|
|
|
#include <errno.h>
|
2018-07-08 18:17:48 +03:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2018-07-09 06:52:36 +03:00
|
|
|
|
#include <sys/stat.h>
|
2018-07-26 17:20:38 +03:00
|
|
|
|
#include <limits.h>
|
2018-07-08 18:17:48 +03:00
|
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
#include "ibus_glfw.h"
|
|
|
|
|
|
2018-07-09 03:40:22 +03:00
|
|
|
|
#define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__);
|
2018-07-09 06:52:36 +03:00
|
|
|
|
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";
|
|
|
|
|
enum Capabilities {
|
|
|
|
|
IBUS_CAP_PREEDIT_TEXT = 1 << 0,
|
|
|
|
|
IBUS_CAP_AUXILIARY_TEXT = 1 << 1,
|
|
|
|
|
IBUS_CAP_LOOKUP_TABLE = 1 << 2,
|
|
|
|
|
IBUS_CAP_FOCUS = 1 << 3,
|
|
|
|
|
IBUS_CAP_PROPERTY = 1 << 4,
|
|
|
|
|
IBUS_CAP_SURROUNDING_TEXT = 1 << 5
|
|
|
|
|
};
|
|
|
|
|
|
2018-07-09 03:40:22 +03:00
|
|
|
|
|
2018-07-08 18:17:48 +03:00
|
|
|
|
static inline GLFWbool
|
2018-07-10 12:58:14 +03:00
|
|
|
|
test_env_var(const char *name, const char *val) {
|
2018-07-08 18:17:48 +03:00
|
|
|
|
const char *q = getenv(name);
|
2018-07-08 19:28:49 +03:00
|
|
|
|
return (q && strcmp(q, val) == 0) ? GLFW_TRUE : GLFW_FALSE;
|
2018-07-08 18:17:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-08 19:28:49 +03:00
|
|
|
|
static inline GLFWbool
|
|
|
|
|
MIN(size_t a, size_t b) {
|
|
|
|
|
return a < b ? a : b;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-10 12:34:32 +03:00
|
|
|
|
static const char*
|
|
|
|
|
get_ibus_text_from_message(DBusMessage *msg) {
|
|
|
|
|
/* The message structure is (from dbus-monitor)
|
|
|
|
|
variant struct {
|
|
|
|
|
string "IBusText"
|
|
|
|
|
array [
|
|
|
|
|
]
|
|
|
|
|
string "ash "
|
|
|
|
|
variant struct {
|
|
|
|
|
string "IBusAttrList"
|
|
|
|
|
array [
|
|
|
|
|
]
|
|
|
|
|
array [
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
const char *text = NULL;
|
|
|
|
|
const char *struct_id = NULL;
|
|
|
|
|
DBusMessageIter iter, sub1, sub2;
|
|
|
|
|
dbus_message_iter_init(msg, &iter);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_get_basic(&sub2, &struct_id);
|
|
|
|
|
if (!struct_id || 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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 09:51:31 +03:00
|
|
|
|
static inline void
|
|
|
|
|
send_text(const char *text, int state) {
|
|
|
|
|
_GLFWwindow *w = _glfwFocusedWindow();
|
|
|
|
|
if (w && w->callbacks.keyboard) {
|
|
|
|
|
w->callbacks.keyboard((GLFWwindow*) w, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0, text, state);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-09 07:37:11 +03:00
|
|
|
|
|
2018-07-09 06:52:36 +03:00
|
|
|
|
// Connection handling {{{
|
2018-07-09 07:37:11 +03:00
|
|
|
|
|
2018-07-09 06:52:36 +03:00
|
|
|
|
static DBusHandlerResult
|
|
|
|
|
message_handler(DBusConnection *conn, DBusMessage *msg, void *user_data) {
|
2018-07-10 12:34:32 +03:00
|
|
|
|
// To monitor signals from IBUS, use
|
|
|
|
|
// dbus-monitor --address `ibus address` "type='signal',interface='org.freedesktop.IBus.InputContext'"
|
2018-07-09 06:52:36 +03:00
|
|
|
|
_GLFWIBUSData *ibus = (_GLFWIBUSData*)user_data;
|
|
|
|
|
(void)ibus;
|
2018-07-10 12:34:32 +03:00
|
|
|
|
const char *text;
|
2018-08-06 08:05:46 +03:00
|
|
|
|
switch(glfw_dbus_match_signal(msg, IBUS_INPUT_INTERFACE, "CommitText", "UpdatePreeditText", "HidePreeditText", "ShowPreeditText", NULL)) {
|
2018-07-10 12:34:32 +03:00
|
|
|
|
case 0:
|
|
|
|
|
text = get_ibus_text_from_message(msg);
|
|
|
|
|
debug("IBUS: CommitText: '%s'\n", text ? text : "(nil)");
|
2018-07-11 09:51:31 +03:00
|
|
|
|
send_text(text, 2);
|
2018-07-10 12:34:32 +03:00
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
text = get_ibus_text_from_message(msg);
|
2018-07-11 09:51:31 +03:00
|
|
|
|
send_text(text, 1);
|
2018-07-10 12:34:32 +03:00
|
|
|
|
debug("IBUS: UpdatePreeditText: '%s'\n", text ? text : "(nil)");
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
debug("IBUS: HidePreeditText\n");
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
debug("IBUS: ShowPreeditText\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-07-09 06:52:36 +03:00
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
2018-07-08 19:28:49 +03:00
|
|
|
|
|
|
|
|
|
static inline const char*
|
|
|
|
|
get_ibus_address_file_name(void) {
|
|
|
|
|
const char *addr;
|
|
|
|
|
static char ans[PATH_MAX];
|
|
|
|
|
addr = getenv("IBUS_ADDRESS");
|
|
|
|
|
int offset = 0;
|
|
|
|
|
if (addr && addr[0]) {
|
|
|
|
|
memcpy(ans, addr, MIN(strlen(addr), sizeof(ans)));
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *de = getenv("DISPLAY");
|
|
|
|
|
if (!de || !de[0]) de = ":0.0";
|
2018-07-29 09:45:21 +03:00
|
|
|
|
char *display = _glfw_strdup(de);
|
2018-07-08 19:28:49 +03:00
|
|
|
|
const char *host = display;
|
|
|
|
|
char *disp_num = strrchr(display, ':');
|
|
|
|
|
char *screen_num = strrchr(display, '.');
|
|
|
|
|
|
|
|
|
|
if (!disp_num) {
|
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as DISPLAY env var has no colon");
|
|
|
|
|
free(display);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
*disp_num = 0;
|
|
|
|
|
disp_num++;
|
|
|
|
|
if (screen_num) *screen_num = 0;
|
|
|
|
|
if (!*host) host = "unix";
|
|
|
|
|
|
|
|
|
|
memset(ans, 0, sizeof(ans));
|
|
|
|
|
const char *conf_env = getenv("XDG_CONFIG_HOME");
|
|
|
|
|
if (conf_env && conf_env[0]) {
|
|
|
|
|
offset = snprintf(ans, sizeof(ans), "%s", conf_env);
|
|
|
|
|
} else {
|
|
|
|
|
conf_env = getenv("HOME");
|
|
|
|
|
if (!conf_env || !conf_env[0]) {
|
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as no HOME env var is set");
|
|
|
|
|
free(display);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
offset = snprintf(ans, sizeof(ans), "%s/.config", conf_env);
|
|
|
|
|
}
|
|
|
|
|
char *key = dbus_get_local_machine_id();
|
|
|
|
|
snprintf(ans + offset, sizeof(ans) - offset, "/ibus/bus/%s-%s-%s", key, host, disp_num);
|
|
|
|
|
dbus_free(key);
|
|
|
|
|
free(display);
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-07-09 07:37:11 +03:00
|
|
|
|
static inline GLFWbool
|
2018-07-09 06:52:36 +03:00
|
|
|
|
read_ibus_address(_GLFWIBUSData *ibus) {
|
2018-07-08 19:28:49 +03:00
|
|
|
|
static char buf[1024];
|
2018-07-09 06:52:36 +03:00
|
|
|
|
struct stat s;
|
|
|
|
|
FILE *addr_file = fopen(ibus->address_file_name, "r");
|
2018-07-08 19:28:49 +03:00
|
|
|
|
if (!addr_file) {
|
2018-07-09 06:52:36 +03:00
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to open IBUS address file: %s with error: %s", ibus->address_file_name, strerror(errno));
|
2018-07-09 07:37:11 +03:00
|
|
|
|
return GLFW_FALSE;
|
2018-07-08 19:28:49 +03:00
|
|
|
|
}
|
2018-07-09 06:52:36 +03:00
|
|
|
|
int stat_result = fstat(fileno(addr_file), &s);
|
2018-07-08 19:28:49 +03:00
|
|
|
|
GLFWbool found = GLFW_FALSE;
|
|
|
|
|
while (fgets(buf, sizeof(buf), addr_file)) {
|
|
|
|
|
if (strncmp(buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=")-1) == 0) {
|
|
|
|
|
size_t sz = strlen(buf);
|
|
|
|
|
if (buf[sz-1] == '\n') buf[sz-1] = 0;
|
|
|
|
|
if (buf[sz-2] == '\r') buf[sz-2] = 0;
|
|
|
|
|
found = GLFW_TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fclose(addr_file); addr_file = NULL;
|
2018-07-09 06:52:36 +03:00
|
|
|
|
if (stat_result != 0) {
|
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to stat IBUS address file: %s with error: %s", ibus->address_file_name, strerror(errno));
|
2018-07-09 07:37:11 +03:00
|
|
|
|
return GLFW_FALSE;
|
2018-07-09 06:52:36 +03:00
|
|
|
|
}
|
|
|
|
|
ibus->address_file_mtime = s.st_mtime;
|
2018-07-09 07:37:11 +03:00
|
|
|
|
if (found) {
|
|
|
|
|
free((void*)ibus->address);
|
2018-07-29 09:45:21 +03:00
|
|
|
|
ibus->address = _glfw_strdup(buf + sizeof("IBUS_ADDRESS=") - 1);
|
2018-07-09 07:37:11 +03:00
|
|
|
|
return GLFW_TRUE;
|
|
|
|
|
}
|
2018-07-09 06:52:36 +03:00
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not find IBUS_ADDRESS in %s", ibus->address_file_name);
|
2018-07-09 07:37:11 +03:00
|
|
|
|
return GLFW_FALSE;
|
2018-07-08 19:28:49 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-10 06:11:47 +03:00
|
|
|
|
void
|
|
|
|
|
input_context_created(DBusMessage *msg, const char* errmsg, void *data) {
|
|
|
|
|
if (errmsg) {
|
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "IBUS: Failed to create input context with error: %s", errmsg);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const char *path = NULL;
|
|
|
|
|
if (!glfw_dbus_get_args(msg, "Failed to get IBUS context path from reply", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return;
|
|
|
|
|
_GLFWIBUSData *ibus = (_GLFWIBUSData*)data;
|
|
|
|
|
free((void*)ibus->input_ctx_path);
|
2018-07-29 09:45:21 +03:00
|
|
|
|
ibus->input_ctx_path = _glfw_strdup(path);
|
2018-07-10 11:00:36 +03:00
|
|
|
|
if (!ibus->input_ctx_path) return;
|
|
|
|
|
dbus_bus_add_match(ibus->conn, "type='signal',interface='org.freedesktop.IBus.InputContext'", NULL);
|
|
|
|
|
DBusObjectPathVTable ibus_vtable = {.message_function = message_handler};
|
|
|
|
|
dbus_connection_try_register_object_path(ibus->conn, ibus->input_ctx_path, &ibus_vtable, ibus, NULL);
|
2018-07-10 06:11:47 +03:00
|
|
|
|
enum Capabilities caps = IBUS_CAP_FOCUS | IBUS_CAP_PREEDIT_TEXT;
|
2018-07-10 11:00:36 +03:00
|
|
|
|
if (!glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "SetCapabilities", DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID)) return;
|
2018-07-10 06:55:10 +03:00
|
|
|
|
ibus->ok = GLFW_TRUE;
|
2018-07-10 06:11:47 +03:00
|
|
|
|
glfw_ibus_set_focused(ibus, GLFW_FALSE);
|
2018-07-11 07:43:38 +03:00
|
|
|
|
glfw_ibus_set_cursor_geometry(ibus, 0, 0, 0, 0);
|
2018-07-10 06:11:47 +03:00
|
|
|
|
debug("Connected to IBUS daemon for IME input management\n");
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 06:52:36 +03:00
|
|
|
|
GLFWbool
|
|
|
|
|
setup_connection(_GLFWIBUSData *ibus) {
|
|
|
|
|
const char *client_name = "GLFW_Application";
|
|
|
|
|
const char *address_file_name = get_ibus_address_file_name();
|
2018-07-10 06:11:47 +03:00
|
|
|
|
ibus->ok = GLFW_FALSE;
|
2018-07-09 06:52:36 +03:00
|
|
|
|
if (!address_file_name) return GLFW_FALSE;
|
|
|
|
|
free((void*)ibus->address_file_name);
|
2018-07-29 09:45:21 +03:00
|
|
|
|
ibus->address_file_name = _glfw_strdup(address_file_name);
|
2018-07-09 07:37:11 +03:00
|
|
|
|
if (!read_ibus_address(ibus)) return GLFW_FALSE;
|
|
|
|
|
if (ibus->conn) {
|
|
|
|
|
glfw_dbus_close_connection(ibus->conn);
|
|
|
|
|
ibus->conn = NULL;
|
|
|
|
|
}
|
|
|
|
|
debug("Connecting to IBUS daemon @ %s for IME input management\n", ibus->address);
|
2018-07-12 08:34:29 +03:00
|
|
|
|
ibus->conn = glfw_dbus_connect_to(ibus->address, "Failed to connect to the IBUS daemon, with error", "ibus", GLFW_FALSE);
|
2018-07-09 06:52:36 +03:00
|
|
|
|
if (!ibus->conn) return GLFW_FALSE;
|
2018-07-10 06:11:47 +03:00
|
|
|
|
free((void*)ibus->input_ctx_path); ibus->input_ctx_path = NULL;
|
|
|
|
|
if (!glfw_dbus_call_method_with_reply(
|
2018-07-12 09:41:17 +03:00
|
|
|
|
ibus->conn, IBUS_SERVICE, IBUS_PATH, IBUS_INTERFACE, "CreateInputContext", DBUS_TIMEOUT_USE_DEFAULT, input_context_created, ibus,
|
2018-07-10 06:11:47 +03:00
|
|
|
|
DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID)) {
|
|
|
|
|
return GLFW_FALSE;
|
|
|
|
|
}
|
2018-07-09 06:52:36 +03:00
|
|
|
|
return GLFW_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-08 19:28:49 +03:00
|
|
|
|
|
2018-07-08 18:17:48 +03:00
|
|
|
|
void
|
2018-07-09 08:18:00 +03:00
|
|
|
|
glfw_connect_to_ibus(_GLFWIBUSData *ibus) {
|
2018-07-09 06:52:36 +03:00
|
|
|
|
if (ibus->inited) return;
|
2018-09-07 13:37:01 +03:00
|
|
|
|
if (!test_env_var("GLFW_IM_MODULE", "ibus")) return;
|
2018-07-09 07:37:11 +03:00
|
|
|
|
ibus->inited = GLFW_TRUE;
|
2018-07-09 06:52:36 +03:00
|
|
|
|
setup_connection(ibus);
|
2018-07-09 03:40:22 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
glfw_ibus_terminate(_GLFWIBUSData *ibus) {
|
|
|
|
|
if (ibus->conn) {
|
|
|
|
|
glfw_dbus_close_connection(ibus->conn);
|
|
|
|
|
ibus->conn = NULL;
|
|
|
|
|
}
|
2018-07-09 06:52:36 +03:00
|
|
|
|
#define F(x) if (ibus->x) { free((void*)ibus->x); ibus->x = NULL; }
|
2018-07-09 07:37:11 +03:00
|
|
|
|
F(input_ctx_path);
|
|
|
|
|
F(address);
|
|
|
|
|
F(address_file_name);
|
2018-07-09 06:52:36 +03:00
|
|
|
|
#undef F
|
|
|
|
|
|
2018-07-09 03:40:22 +03:00
|
|
|
|
ibus->ok = GLFW_FALSE;
|
2018-07-08 18:17:48 +03:00
|
|
|
|
}
|
2018-07-09 07:37:11 +03:00
|
|
|
|
|
|
|
|
|
static GLFWbool
|
|
|
|
|
check_connection(_GLFWIBUSData *ibus) {
|
|
|
|
|
if (!ibus->inited) return GLFW_FALSE;
|
|
|
|
|
if (ibus->conn && dbus_connection_get_is_connected(ibus->conn)) {
|
2018-07-10 06:55:10 +03:00
|
|
|
|
return ibus->ok;
|
2018-07-09 07:37:11 +03:00
|
|
|
|
}
|
|
|
|
|
struct stat s;
|
|
|
|
|
if (stat(ibus->address_file_name, &s) != 0 || s.st_mtime != ibus->address_file_mtime) {
|
|
|
|
|
if (!read_ibus_address(ibus)) return GLFW_FALSE;
|
2018-07-10 06:55:10 +03:00
|
|
|
|
setup_connection(ibus);
|
2018-07-09 07:37:11 +03:00
|
|
|
|
}
|
|
|
|
|
return GLFW_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-07-09 19:04:06 +03:00
|
|
|
|
void
|
|
|
|
|
glfw_ibus_dispatch(_GLFWIBUSData *ibus) {
|
|
|
|
|
if (ibus->conn) glfw_dbus_dispatch(ibus->conn);
|
|
|
|
|
}
|
2018-07-09 06:52:36 +03:00
|
|
|
|
// }}}
|
2018-07-09 07:37:11 +03:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
simple_message(_GLFWIBUSData *ibus, const char *method) {
|
|
|
|
|
if (check_connection(ibus)) {
|
2018-07-10 11:00:36 +03:00
|
|
|
|
glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, method, DBUS_TYPE_INVALID);
|
2018-07-09 07:37:11 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
glfw_ibus_set_focused(_GLFWIBUSData *ibus, GLFWbool focused) {
|
|
|
|
|
simple_message(ibus, focused ? "FocusIn" : "FocusOut");
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:43:38 +03:00
|
|
|
|
void
|
|
|
|
|
glfw_ibus_set_cursor_geometry(_GLFWIBUSData *ibus, int x, int y, int w, int h) {
|
2018-07-09 07:37:11 +03:00
|
|
|
|
if (check_connection(ibus)) {
|
2018-07-10 06:11:47 +03:00
|
|
|
|
glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "SetCursorLocation",
|
2018-07-09 07:37:11 +03:00
|
|
|
|
DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &w, DBUS_TYPE_INT32, &h, DBUS_TYPE_INVALID);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-10 11:00:36 +03:00
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
IBUS_SHIFT_MASK = 1 << 0,
|
|
|
|
|
IBUS_LOCK_MASK = 1 << 1,
|
|
|
|
|
IBUS_CONTROL_MASK = 1 << 2,
|
|
|
|
|
IBUS_MOD1_MASK = 1 << 3,
|
|
|
|
|
IBUS_MOD2_MASK = 1 << 4,
|
|
|
|
|
IBUS_MOD3_MASK = 1 << 5,
|
|
|
|
|
IBUS_MOD4_MASK = 1 << 6,
|
|
|
|
|
IBUS_MOD5_MASK = 1 << 7,
|
|
|
|
|
IBUS_BUTTON1_MASK = 1 << 8,
|
|
|
|
|
IBUS_BUTTON2_MASK = 1 << 9,
|
|
|
|
|
IBUS_BUTTON3_MASK = 1 << 10,
|
|
|
|
|
IBUS_BUTTON4_MASK = 1 << 11,
|
|
|
|
|
IBUS_BUTTON5_MASK = 1 << 12,
|
|
|
|
|
|
|
|
|
|
/* The next few modifiers are used by XKB, so we skip to the end.
|
|
|
|
|
* Bits 15 - 23 are currently unused. Bit 29 is used internally.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* ibus mask */
|
|
|
|
|
IBUS_HANDLED_MASK = 1 << 24,
|
|
|
|
|
IBUS_FORWARD_MASK = 1 << 25,
|
|
|
|
|
IBUS_IGNORED_MASK = IBUS_FORWARD_MASK,
|
|
|
|
|
|
|
|
|
|
IBUS_SUPER_MASK = 1 << 26,
|
|
|
|
|
IBUS_HYPER_MASK = 1 << 27,
|
|
|
|
|
IBUS_META_MASK = 1 << 28,
|
|
|
|
|
|
|
|
|
|
IBUS_RELEASE_MASK = 1 << 30,
|
|
|
|
|
|
|
|
|
|
IBUS_MODIFIER_MASK = 0x5f001fff
|
|
|
|
|
} IBusModifierType;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline uint32_t
|
|
|
|
|
ibus_key_state(unsigned int glfw_modifiers, int action) {
|
|
|
|
|
uint32_t ans = action == GLFW_RELEASE ? IBUS_RELEASE_MASK : 0;
|
|
|
|
|
#define M(g, i) if(glfw_modifiers & GLFW_MOD_##g) ans |= i
|
|
|
|
|
M(SHIFT, IBUS_SHIFT_MASK);
|
|
|
|
|
M(CAPS_LOCK, IBUS_LOCK_MASK);
|
|
|
|
|
M(CONTROL, IBUS_CONTROL_MASK);
|
|
|
|
|
M(ALT, IBUS_MOD1_MASK);
|
|
|
|
|
M(NUM_LOCK, IBUS_MOD2_MASK);
|
|
|
|
|
M(SUPER, IBUS_MOD4_MASK);
|
|
|
|
|
#undef M
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
key_event_processed(DBusMessage *msg, const char* errmsg, void *data) {
|
2018-07-11 13:49:03 +03:00
|
|
|
|
uint32_t handled = 0;
|
|
|
|
|
KeyEvent *ev = (KeyEvent*)data;
|
|
|
|
|
GLFWbool is_release = ev->action == GLFW_RELEASE;
|
2018-07-12 09:41:17 +03:00
|
|
|
|
GLFWbool failed = GLFW_FALSE;
|
2018-07-10 11:00:36 +03:00
|
|
|
|
if (errmsg) {
|
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "IBUS: Failed to process key with error: %s", errmsg);
|
2018-07-12 09:41:17 +03:00
|
|
|
|
failed = GLFW_TRUE;
|
2018-07-11 13:49:03 +03:00
|
|
|
|
} else {
|
|
|
|
|
glfw_dbus_get_args(msg, "Failed to get IBUS handled key from reply", DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID);
|
|
|
|
|
debug("IBUS processed scancode: 0x%x release: %d handled: %u\n", ev->keycode, is_release, handled);
|
2018-07-10 11:00:36 +03:00
|
|
|
|
}
|
2018-07-12 09:41:17 +03:00
|
|
|
|
glfw_xkb_key_from_ime(ev, handled ? GLFW_TRUE : GLFW_FALSE, failed);
|
2018-07-10 11:00:36 +03:00
|
|
|
|
free(ev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GLFWbool
|
2018-07-11 13:49:03 +03:00
|
|
|
|
ibus_process_key(const KeyEvent *ev_, _GLFWIBUSData *ibus) {
|
|
|
|
|
if (!check_connection(ibus)) return GLFW_FALSE;
|
2018-07-10 11:00:36 +03:00
|
|
|
|
KeyEvent *ev = malloc(sizeof(KeyEvent));
|
|
|
|
|
if (!ev) return GLFW_FALSE;
|
|
|
|
|
memcpy(ev, ev_, sizeof(KeyEvent));
|
|
|
|
|
uint32_t state = ibus_key_state(ev->glfw_modifiers, ev->action);
|
|
|
|
|
if (!glfw_dbus_call_method_with_reply(
|
2018-07-11 13:49:03 +03:00
|
|
|
|
ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "ProcessKeyEvent",
|
2018-07-12 09:41:17 +03:00
|
|
|
|
3000, key_event_processed, ev,
|
2018-07-12 06:55:51 +03:00
|
|
|
|
DBUS_TYPE_UINT32, &ev->ibus_sym, DBUS_TYPE_UINT32, &ev->ibus_keycode, DBUS_TYPE_UINT32,
|
2018-07-10 11:00:36 +03:00
|
|
|
|
&state, DBUS_TYPE_INVALID)) {
|
|
|
|
|
free(ev);
|
|
|
|
|
return GLFW_FALSE;
|
|
|
|
|
}
|
|
|
|
|
return GLFW_TRUE;
|
|
|
|
|
}
|