한글 입력기가 안 먹는 리눅스 어플들의 특징이 있는데, 실은 이게 해당 어플의 버그는 아닙니다. 주로 영어권 국가에서 만든 어플들, 그 중에서도 문자열 입력 위젯이 커스텀으로 설계된 것들(주로 문서 에디터)이 거의 대부분 해당되는데 이는 당연하게도 영어권 국가에서는 별도의 다국어 입력기가 필요하지 않기 때문에 CJK 등 동양권 사용자들을 위한 추가적인 다국어 처리 설계를 코드에 포함시키지 않았을 뿐이죠.


그러나 다국어 처리를 하지 않았는데도 기본적으로 다국어 입력이 가능한 어플도 다수 있는데, 이는 프레임워크에서 제공한 기본 입력 위젯만을 사용한 경우가 그렇습니다. GTK2로 개발된 어플에서 문자일 입력 부분이 GtkEntry라는 기본 문자열입력 위젯을 사용했거나 이를 상속받아 커스텀한 위젯이라면 기본적으로 IM 입력기가 동작합니다. 그 이유는 gtk_entry_... 계열 함수들의 소스코드를 보시면 알 수 있는데, IMContext 위젯을 사용하여 다국어 입력을 고려한 설계가 적용되었기 때문입니다. 따라서 이 부분을 그대로 IM 입력기가 작동하지 않는 리눅스 어플의 (커스텀 제작된) 소스코드에 이식하여 적당히 수정한다면 당연히 한글 입력기 등의 IM 입력이 동작할 것이라고 추정해 볼 수 있습니다.


다음은 gtk+ 3.24.38의 소스 파일 들 중 /gtk/gtkentry.c의 일부입니다. (IM 입력기 처리 부분만)


//...중략... /* IM Context Callbacks */ static void gtk_entry_commit_cb (GtkIMContext *context, const gchar *str, GtkEntry *entry); static void gtk_entry_preedit_changed_cb (GtkIMContext *context, GtkEntry *entry); static gboolean gtk_entry_retrieve_surrounding_cb (GtkIMContext *context, GtkEntry *entry); static gboolean gtk_entry_delete_surrounding_cb (GtkIMContext *context, gint offset, gint n_chars, GtkEntry *entry); // ...중략... /** * GtkEntry:im-module: * * Which IM (input method) module should be used for this entry. * See #GtkIMContext. * * Setting this to a non-%NULL value overrides the * system-wide IM module setting. See the GtkSettings * #GtkSettings:gtk-im-module property. * * Since: 2.16 */ entry_props[PROP_IM_MODULE] = g_param_spec_string ("im-module", P_("IM module"), P_("Which IM module should be used"), NULL, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); //...중략... static void gtk_entry_init (GtkEntry *entry) { GtkEntryPrivate *priv; GtkCssNode *widget_node; gint i; entry->priv = gtk_entry_get_instance_private (entry); priv = entry->priv; //...중략... /* This object is completely private. No external entity can gain a reference * to it; so we create it here and destroy it in finalize(). */ priv->im_context = gtk_im_multicontext_new (); g_signal_connect (priv->im_context, "commit", G_CALLBACK (gtk_entry_commit_cb), entry); g_signal_connect (priv->im_context, "preedit-changed", G_CALLBACK (gtk_entry_preedit_changed_cb), entry); g_signal_connect (priv->im_context, "retrieve-surrounding", G_CALLBACK (gtk_entry_retrieve_surrounding_cb), entry); g_signal_connect (priv->im_context, "delete-surrounding", G_CALLBACK (gtk_entry_delete_surrounding_cb), entry); //...중략... } //...중략... /* IM Context Callbacks */ static void gtk_entry_commit_cb (GtkIMContext *context, const gchar *str, GtkEntry *entry) { GtkEntryPrivate *priv = entry->priv; if (priv->editable) gtk_entry_enter_text (entry, str); } static void gtk_entry_preedit_changed_cb (GtkIMContext *context, GtkEntry *entry) { GtkEntryPrivate *priv = entry->priv; if (priv->editable) { gchar *preedit_string; gint cursor_pos; gtk_im_context_get_preedit_string (priv->im_context, &preedit_string, NULL, &cursor_pos); g_signal_emit (entry, signals[PREEDIT_CHANGED], 0, preedit_string); priv->preedit_length = strlen (preedit_string); cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1)); priv->preedit_cursor = cursor_pos; g_free (preedit_string); gtk_entry_recompute (entry); } } static gboolean gtk_entry_retrieve_surrounding_cb (GtkIMContext *context, GtkEntry *entry) { GtkEntryPrivate *priv = entry->priv; gchar *text; /* XXXX ??? does this even make sense when text is not visible? Should we return FALSE? */ text = _gtk_entry_get_display_text (entry, 0, -1); gtk_im_context_set_surrounding (context, text, strlen (text), /* Length in bytes */ g_utf8_offset_to_pointer (text, priv->current_pos) - text); g_free (text); return TRUE; } static gboolean gtk_entry_delete_surrounding_cb (GtkIMContext *slave, gint offset, gint n_chars, GtkEntry *entry) { GtkEntryPrivate *priv = entry->priv; if (priv->editable) gtk_editable_delete_text (GTK_EDITABLE (entry), priv->current_pos + offset, priv->current_pos + offset + n_chars); return TRUE; } //...중략...