#define _POSIX_C_SOURCE 200809L #include #include #include #include typedef struct Log Log; struct Log { GMainLoop *loop; int *waiting; IBusInputContext *a; IBusInputContext *clearctx; int legacy; int modern; int commit; int invalid; int sawpreedit; int sawcommit; int done; int transfer; int atext; int aclear; }; static void legacy(IBusInputContext *ctx, IBusText *text, guint cursor, gboolean visible, void *arg) { IBusAttrList *attrs; IBusAttribute *a; Log *log; const char *s; (void)ctx; log = arg; log->legacy++; s = ibus_text_get_text(text); if(strcmp(s, "k") == 0){ attrs = ibus_text_get_attributes(text); a = attrs == NULL ? NULL : ibus_attr_list_get(attrs, 0); if(cursor != 1 || !visible || attrs == NULL || a == NULL || a->type != IBUS_ATTR_TYPE_UNDERLINE || a->value != IBUS_ATTR_UNDERLINE_SINGLE || a->start_index != 0 || a->end_index != 1 || ibus_attr_list_get(attrs, 1) != NULL) log->invalid = 1; else{ log->sawpreedit = 1; if(log->transfer && ctx == log->a) log->atext = 1; } } if(log->transfer && log->atext && !log->aclear && s[0] == '\0'){ if(cursor != 0 || visible) log->invalid = 1; else{ log->clearctx = ctx; if(ctx == log->a) log->aclear = 1; else log->invalid = 1; } } log->done = log->sawpreedit && log->sawcommit; if(log->loop != NULL && log->waiting != NULL && *log->waiting) g_main_loop_quit(log->loop); } static void modern(IBusInputContext *ctx, IBusText *text, guint cursor, gboolean visible, guint mode, void *arg) { Log *log; (void)ctx; (void)text; (void)cursor; (void)visible; (void)mode; log = arg; log->modern++; log->invalid = 1; if(log->loop != NULL) g_main_loop_quit(log->loop); } static void commit(IBusInputContext *ctx, IBusText *text, void *arg) { IBusAttrList *attrs; Log *log; const char *s; (void)ctx; log = arg; log->commit++; s = ibus_text_get_text(text); attrs = ibus_text_get_attributes(text); if(strcmp(s, "か") != 0 || attrs == NULL || ibus_attr_list_get(attrs, 0) != NULL) log->invalid = 1; else log->sawcommit = 1; log->done = log->sawpreedit && log->sawcommit; if(log->loop != NULL && log->waiting != NULL && *log->waiting) g_main_loop_quit(log->loop); } static gboolean timeout(void *arg) { g_main_loop_quit(arg); return G_SOURCE_REMOVE; } static int waitflag(Log *log, int *flag) { guint timer; if(*flag) return 1; log->waiting = flag; log->loop = g_main_loop_new(NULL, FALSE); timer = g_timeout_add_seconds(4, timeout, log->loop); g_main_loop_run(log->loop); if(g_main_context_find_source_by_id(NULL, timer) != NULL) g_source_remove(timer); g_main_loop_unref(log->loop); log->loop = NULL; log->waiting = NULL; return *flag; } int main(int argc, char **argv) { IBusBus *bus; IBusInputContext *a, *b; Log log; int ok; if(argc != 2){ fprintf(stderr, "usage: ibus_client_smoke address\n"); return 2; } if(setenv("IBUS_ADDRESS", argv[1], 1) < 0 || unsetenv("IBUS_ADDRESS_FILE") < 0 || unsetenv("DBUS_SESSION_BUS_ADDRESS") < 0 || unsetenv("DISPLAY") < 0){ perror("ibus_client_smoke: environment"); return 1; } memset(&log, 0, sizeof log); b = NULL; ibus_init(); bus = ibus_bus_new(); if(bus == NULL || !ibus_bus_is_connected(bus)){ fprintf(stderr, "ibus_client_smoke: cannot connect to private bus\n"); if(bus != NULL) g_object_unref(bus); return 1; } a = ibus_bus_create_input_context(bus, "strans-libibus-smoke-a"); if(a == NULL){ fprintf(stderr, "ibus_client_smoke: cannot create input context\n"); g_object_unref(bus); return 1; } log.a = a; g_signal_connect(a, "update-preedit-text", G_CALLBACK(legacy), &log); g_signal_connect(a, "update-preedit-text-with-mode", G_CALLBACK(modern), &log); g_signal_connect(a, "commit-text", G_CALLBACK(commit), &log); ibus_input_context_set_capabilities(a, IBUS_CAP_PREEDIT_TEXT); ibus_input_context_focus_in(a); ok = ibus_input_context_process_key_event(a, 'n', 0, IBUS_CONTROL_MASK) && ibus_input_context_process_key_event(a, 'k', 0, 0) && ibus_input_context_process_key_event(a, 'a', 0, 0) && ibus_input_context_process_key_event(a, '0', 0, 0) && waitflag(&log, &log.done); b = ibus_bus_create_input_context(bus, "strans-libibus-smoke-b"); if(b == NULL) ok = 0; else{ g_signal_connect(b, "update-preedit-text", G_CALLBACK(legacy), &log); g_signal_connect(b, "update-preedit-text-with-mode", G_CALLBACK(modern), &log); ibus_input_context_set_capabilities(b, IBUS_CAP_PREEDIT_TEXT); log.transfer = 1; ok = ok && ibus_input_context_process_key_event(a, 'k', 0, 0) && waitflag(&log, &log.atext); ibus_input_context_focus_in(b); ok = ok && ibus_input_context_process_key_event(b, 'n', 0, 0) && waitflag(&log, &log.aclear); ibus_input_context_focus_out(b); } ibus_input_context_focus_out(a); if(log.legacy == 0 || log.modern != 0 || log.commit != 1 || log.invalid || !log.done || !log.atext || !log.aclear || log.clearctx != a) ok = 0; if(b != NULL) g_object_unref(b); g_object_unref(a); g_object_unref(bus); if(!ok){ fprintf(stderr, "ibus_client_smoke: legacy=%d modern=%d commit=%d invalid=%d preedit=%d committed=%d transfer-text=%d a-clear=%d\n", log.legacy, log.modern, log.commit, log.invalid, log.sawpreedit, log.sawcommit, log.atext, log.aclear); return 1; } printf("official libibus client preedit, commit, and owner clear: ok\n"); return 0; }