#define _POSIX_C_SOURCE 200809L #include #include #include #include typedef struct Log Log; struct Log { GMainLoop *loop; int want; int legacy; int modern; int invalid; int sawtext; int sawempty; }; static void legacy(IBusInputContext *ctx, IBusText *text, guint cursor, gboolean visible, void *arg) { Log *log; (void)ctx; (void)text; (void)cursor; (void)visible; log = arg; log->legacy++; } static void modern(IBusInputContext *ctx, IBusText *text, guint cursor, gboolean visible, guint mode, void *arg) { IBusAttrList *attrs; IBusAttribute *a; Log *log; const char *s; (void)ctx; log = arg; log->modern++; s = ibus_text_get_text(text); attrs = ibus_text_get_attributes(text); a = ibus_attr_list_get(attrs, 0); if(s[0] != '\0'){ if(strcmp(s, "k") != 0 || cursor != 1 || !visible || mode != IBUS_ENGINE_PREEDIT_CLEAR || 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->sawtext = 1; }else{ if(cursor != 0 || visible || mode != IBUS_ENGINE_PREEDIT_CLEAR || a != NULL) log->invalid = 1; else log->sawempty = 1; } if(log->loop != NULL && log->modern >= log->want) g_main_loop_quit(log->loop); } static gboolean timeout(void *arg) { g_main_loop_quit(arg); return G_SOURCE_REMOVE; } static int waitmodern(Log *log, int want) { guint timer; log->want = want; if(log->modern >= want) return 1; 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; return log->modern >= want; } int main(int argc, char **argv) { IBusBus *bus; IBusInputContext *ctx; 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("DBUS_SESSION_BUS_ADDRESS") < 0 || unsetenv("DISPLAY") < 0){ perror("ibus_client_smoke: environment"); return 1; } memset(&log, 0, sizeof log); 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; } ctx = ibus_bus_create_input_context(bus, "strans-libibus-smoke"); if(ctx == NULL){ fprintf(stderr, "ibus_client_smoke: cannot create input context\n"); g_object_unref(bus); return 1; } g_signal_connect(ctx, "update-preedit-text", G_CALLBACK(legacy), &log); g_signal_connect(ctx, "update-preedit-text-with-mode", G_CALLBACK(modern), &log); ibus_input_context_set_capabilities(ctx, IBUS_CAP_PREEDIT_TEXT); ibus_input_context_set_client_commit_preedit(ctx, TRUE); ibus_input_context_focus_in(ctx); ok = ibus_input_context_process_key_event(ctx, 'n', 0, IBUS_CONTROL_MASK) && waitmodern(&log, 1) && ibus_input_context_process_key_event(ctx, 'k', 0, 0) && waitmodern(&log, 2); ibus_input_context_reset(ctx); if(ok) ok = waitmodern(&log, 3); ibus_input_context_focus_out(ctx); if(ok) ok = waitmodern(&log, 4); if(log.legacy != 0 || log.modern != 4 || log.invalid || !log.sawtext || !log.sawempty) ok = 0; g_object_unref(ctx); g_object_unref(bus); if(!ok){ fprintf(stderr, "ibus_client_smoke: legacy=%d modern=%d invalid=%d text=%d empty=%d\n", log.legacy, log.modern, log.invalid, log.sawtext, log.sawempty); return 1; } printf("official libibus client preedit: ok\n"); return 0; }