diff --git a/glfw/dbus_glfw.c b/glfw/dbus_glfw.c index b2b0712c1..36647eac9 100644 --- a/glfw/dbus_glfw.c +++ b/glfw/dbus_glfw.c @@ -29,13 +29,17 @@ #include "dbus_glfw.h" static inline void -report_error(DBusError *err, const char *msg) { - const char *prefix = msg ? msg : "DBUS error occurred"; - _glfwInputError(GLFW_PLATFORM_ERROR, "%s: %s", prefix, err->message); +report_error(DBusError *err, const char *fmt, ...) { + static char buf[1024]; + va_list args; + va_start(args, fmt); + int n = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + snprintf(buf + n, sizeof(buf), ". DBUS error: %s", err->message); + _glfwInputError(GLFW_PLATFORM_ERROR, "%s", buf); dbus_error_free(err); } - GLFWbool glfw_dbus_init(_GLFWDBUSData *dbus) { DBusError err; @@ -43,7 +47,7 @@ glfw_dbus_init(_GLFWDBUSData *dbus) { dbus_error_init(&err); dbus->session_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err); if (dbus_error_is_set(&err)) { - report_error(&err, "Failed to connect to DBUS system bus"); + report_error(&err, "Failed to connect to DBUS session bus"); return GLFW_FALSE; } } @@ -57,7 +61,15 @@ glfw_dbus_connect_to(const char *path, const char* err_msg) { DBusConnection *ans = dbus_connection_open_private(path, &err); if (!ans) { report_error(&err, err_msg); + return NULL; } + dbus_error_free(&err); + dbus_connection_flush(ans); + if (!dbus_bus_register(ans, &err)) { + report_error(&err, err_msg); + return NULL; + } + dbus_connection_flush(ans); return ans; } @@ -75,3 +87,78 @@ glfw_dbus_close_connection(DBusConnection *conn) { dbus_connection_close(conn); dbus_connection_unref(conn); } + +static GLFWbool +call_void_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap) { + GLFWbool retval = GLFW_FALSE; + + if (conn) { + DBusMessage *msg = dbus_message_new_method_call(node, path, interface, method); + if (msg) { + int firstarg = va_arg(ap, int); + if ((firstarg == DBUS_TYPE_INVALID) || dbus_message_append_args_valist(msg, firstarg, ap)) { + if (dbus_connection_send(conn, msg, NULL)) { + dbus_connection_flush(conn); + retval = GLFW_TRUE; + } + } + + dbus_message_unref(msg); + } + } + + return retval; +} + +GLFWbool +glfw_dbus_call_void_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...) { + GLFWbool retval; + va_list ap; + va_start(ap, method); + retval = call_void_method(conn, node, path, interface, method, ap); + va_end(ap); + return retval; +} + +static GLFWbool +call_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap) { + GLFWbool retval = GLFW_FALSE; + + if (conn) { + DBusMessage *msg = dbus_message_new_method_call(node, path, interface, method); + if (msg) { + int firstarg = va_arg(ap, int); + if ((firstarg == DBUS_TYPE_INVALID) || dbus_message_append_args_valist(msg, firstarg, ap)) { + DBusError err; + dbus_error_init(&err); + DBusMessage *reply = dbus_connection_send_with_reply_and_block(conn, msg, 300, &err); + if (reply) { + firstarg = va_arg(ap, int); + dbus_error_free(&err); + if ((firstarg == DBUS_TYPE_INVALID) || dbus_message_get_args_valist(reply, &err, firstarg, ap)) { + retval = GLFW_TRUE; + } else { + report_error(&err, "Failed to get reply args from DBUS method: %s on node: %s and interface: %s", method, node, interface); + } + dbus_message_unref(reply); + } else { + report_error(&err, "Failed to call DBUS method: %s on node: %s and interface: %s", method, node, interface); + } + } + dbus_message_unref(msg); + } + } + + return retval; +} + +GLFWbool +glfw_dbus_call_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...) { + GLFWbool retval; + va_list ap; + va_start(ap, method); + retval = call_method(conn, node, path, interface, method, ap); + va_end(ap); + return retval; + +} diff --git a/glfw/dbus_glfw.h b/glfw/dbus_glfw.h index 8f95320a2..926ab0ea5 100644 --- a/glfw/dbus_glfw.h +++ b/glfw/dbus_glfw.h @@ -38,3 +38,6 @@ GLFWbool glfw_dbus_init(_GLFWDBUSData *dbus); void glfw_dbus_terminate(_GLFWDBUSData *dbus); DBusConnection* glfw_dbus_connect_to(const char *path, const char* err_msg); void glfw_dbus_close_connection(DBusConnection *conn); +GLFWbool glfw_dbus_call_void_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...); +GLFWbool +glfw_dbus_call_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...); diff --git a/glfw/ibus_glfw.c b/glfw/ibus_glfw.c index 3e9f2b233..a0e305202 100644 --- a/glfw/ibus_glfw.c +++ b/glfw/ibus_glfw.c @@ -27,11 +27,25 @@ #include #include #include +#include #include "internal.h" #include "ibus_glfw.h" #define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__); +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 +}; + static inline GLFWbool has_env_var(const char *name, const char *val) { @@ -44,6 +58,13 @@ MIN(size_t a, size_t b) { return a < b ? a : b; } +// Connection handling {{{ +static DBusHandlerResult +message_handler(DBusConnection *conn, DBusMessage *msg, void *user_data) { + _GLFWIBUSData *ibus = (_GLFWIBUSData*)user_data; + (void)ibus; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} static inline const char* get_ibus_address_file_name(void) { @@ -95,13 +116,15 @@ get_ibus_address_file_name(void) { static inline const char* -read_ibus_address(const char *address_file) { - FILE *addr_file = fopen(address_file, "r"); +read_ibus_address(_GLFWIBUSData *ibus) { static char buf[1024]; + struct stat s; + FILE *addr_file = fopen(ibus->address_file_name, "r"); if (!addr_file) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to open IBUS address file: %s with error: %s", address_file, strerror(errno)); + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to open IBUS address file: %s with error: %s", ibus->address_file_name, strerror(errno)); return NULL; } + int stat_result = fstat(fileno(addr_file), &s); GLFWbool found = GLFW_FALSE; while (fgets(buf, sizeof(buf), addr_file)) { if (strncmp(buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=")-1) == 0) { @@ -113,27 +136,61 @@ read_ibus_address(const char *address_file) { } } fclose(addr_file); addr_file = NULL; + if (stat_result != 0) { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to stat IBUS address file: %s with error: %s", ibus->address_file_name, strerror(errno)); + return NULL; + } + ibus->address_file_mtime = s.st_mtime; if (found) return buf + sizeof("IBUS_ADDRESS=") - 1; - _glfwInputError(GLFW_PLATFORM_ERROR, "Could not find IBUS_ADDRESS in %s", address_file); + _glfwInputError(GLFW_PLATFORM_ERROR, "Could not find IBUS_ADDRESS in %s", ibus->address_file_name); return NULL; } +GLFWbool +setup_connection(_GLFWIBUSData *ibus) { + const char *path = NULL; + const char *client_name = "GLFW_Application"; + const char *address_file_name = get_ibus_address_file_name(); + if (!address_file_name) return GLFW_FALSE; + free((void*)ibus->address_file_name); + ibus->address_file_name = strdup(address_file_name); + const char *address = read_ibus_address(ibus); + if (!address) return GLFW_FALSE; + free((void*)ibus->address); + ibus->address = strdup(address); + debug("Connecting to IBUS daemon for IME input management\n"); + ibus->conn = glfw_dbus_connect_to(ibus->address, "Failed to connect to the IBUS daemon, with error"); + if (!ibus->conn) return GLFW_FALSE; + if (!glfw_dbus_call_method(ibus->conn, IBUS_SERVICE, IBUS_PATH, IBUS_INTERFACE, "CreateInputContext", + DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID, + DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return GLFW_FALSE; + free((void*)ibus->input_ctx_path); + ibus->input_ctx_path = strdup(path); + enum Capabilities caps = IBUS_CAP_FOCUS | IBUS_CAP_PREEDIT_TEXT; + if (!glfw_dbus_call_void_method(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "SetCapabilities", DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID)) return GLFW_FALSE; + + dbus_connection_flush(ibus->conn); + 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); + dbus_connection_flush(ibus->conn); + debug("Connected to IBUS daemon for IME input management\n"); + ibus->ok = GLFW_TRUE; + + return GLFW_TRUE; +} + void glfw_connect_to_ibus(_GLFWIBUSData *ibus, _GLFWDBUSData *dbus) { - if (ibus->ok) return; + if (ibus->inited) return; + ibus->inited = GLFW_TRUE; if (!has_env_var("XMODIFIERS", "@im=ibus") && !has_env_var("GTK_IM_MODULE", "ibus") && !has_env_var("QT_IM_MODULE", "ibus")) return; if (!glfw_dbus_init(dbus)) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cannot connect to IBUS as connection to DBUS session bus failed"); } - const char* address_file_name = get_ibus_address_file_name(); - if (!address_file_name) return; - const char *address = read_ibus_address(address_file_name); - if (!address) return; - ibus->conn = glfw_dbus_connect_to(address, "Failed to connect to the IBUS daemon, with error"); - if (!ibus->conn) return; - ibus->ok = GLFW_TRUE; - debug("Connected to IBUS daemon for IME input management\n"); + setup_connection(ibus); } void @@ -142,5 +199,10 @@ glfw_ibus_terminate(_GLFWIBUSData *ibus) { glfw_dbus_close_connection(ibus->conn); ibus->conn = NULL; } +#define F(x) if (ibus->x) { free((void*)ibus->x); ibus->x = NULL; } + F(input_ctx_path); F(address); F(address_file_name); +#undef F + ibus->ok = GLFW_FALSE; } +// }}} diff --git a/glfw/ibus_glfw.h b/glfw/ibus_glfw.h index 1b7d778f6..6b9bb922e 100644 --- a/glfw/ibus_glfw.h +++ b/glfw/ibus_glfw.h @@ -30,8 +30,10 @@ #include "dbus_glfw.h" typedef struct { - GLFWbool ok; + GLFWbool ok, inited; + time_t address_file_mtime; DBusConnection *conn; + const char *input_ctx_path, *address_file_name, *address; } _GLFWIBUSData; void glfw_connect_to_ibus(_GLFWIBUSData *ibus, _GLFWDBUSData *dbus);