끝글자 버그를 고칩시다 4 - GtkEntry

Hodong Kim@Google의 이미지

https://kldp.org/node/161763 에 이어서 계속되는 글입니다.

GtkEntry 끝글자 버그

GtkTextView 에는 끝글자 버그가 없는데 GtkEntry 에 끝글자 버그가 있습니다.
gtk 버전은 3.24.5-1 입니다. 데비안 Buster 환경입니다.
최신 버전은 확인해보지 않아서 모르겠습니다.

재현 방법

아래 소스코드를 컴파일해서 실행합니다.
한글을 입력하다가 조합 중인 글자가 있는 상태에서 마우스로 오른쪽으로부터 왼쪽으로 드래그 하면 재현할 수 있습니다.
참고로 GtkTextView 에는 끝글자 버그가 없습니다.

컴파일 방법

gcc -o bug bug.c `pkg-config --cflags --libs gtk+-3.0`

#include <gtk/gtk.h>
 
static void
activate (GtkApplication* app,
          gpointer        user_data)
{
  GtkWidget *window;
  GtkWidget *entry;
 
  window = gtk_application_window_new (app);
  gtk_window_set_title (GTK_WINDOW (window), "Last character bug");
  entry = gtk_entry_new ();
  gtk_container_add   (GTK_CONTAINER (window), entry);
  gtk_widget_show_all (window);
}
 
int
main (int    argc,
      char **argv)
{
  GtkApplication *app;
  int status;
 
  app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);
 
  return status;
}

hodong@debian:~$ ./bug 
 
(bug:9689): Pango-CRITICAL **: 03:53:19.580: pango_layout_get_cursor_pos: assertion 'index >= 0 && index <= layout->length' failed

해결 방법

간단히 말씀드리자면, 마우스 클릭할 때, 입력 컨텍스트를 리셋해주면 됩니다.
GtkEntry 에는 아래와 같은 코드가 있습니다.
우리가 마우스로 클릭하면 커서가 이동합니다.
커서가 이동하면 gtk_entry_reset_im_context() 함수가 im context 를 reset 합니다.
조합 중인 문자의 오른쪽에서 클릭하면 커서가 이동하지 않아서 im context 가 리셋되지 않습니다.
그러니까 gtk entry 소스코드에서 마우스를 클릭하는 부분을 찾아서 gtk_entry_reset_im_context()를 수행하면 되겠죠. 참 쉽죠?
이렇게 고치기 쉬운게 끝글자 버그입니다.
그런데 주의할 점이 하나 있습니다.
마우스를 클릭할 때 reset 하는 코드를 추가하면,
마우스를 클릭할 때 reset 되고 커서가 이동하면서 또 reset 되겠죠.
중복적으로 reset 이 됩니다. 그런데 커서 이동은 키보드로도 이동되니까...
gtk_entry_move_cursor() 내에 gtk_entry_reset_im_context()을 제거하면
문제가 발생할 수 있겠군요.
그냥 중복적으로 reset 을 실행시켜도 문제가 되지는 않습니다.
GtkEntry 는 priv->need_im_reset == TRUE 일 때만 reset 하기 때문입니다.

static void
gtk_entry_move_cursor (GtkEntry       *entry,
		       GtkMovementStep step,
		       gint            count,
		       gboolean        extend_selection)
{
  GtkEntryPrivate *priv = entry->priv;
  gint new_pos = priv->current_pos;
 
  gtk_entry_reset_im_context (entry);
  ...

void
gtk_entry_reset_im_context (GtkEntry *entry)
{
  GtkEntryPrivate *priv = entry->priv;
 
  g_return_if_fail (GTK_IS_ENTRY (entry));
 
  if (priv->need_im_reset)
    {
      priv->need_im_reset = FALSE;
      gtk_im_context_reset (priv->im_context);
    }
}

static void
gtk_entry_class_init (GtkEntryClass *class)
{
...
  widget_class->key_press_event = gtk_entry_key_press;
...

static gint
gtk_entry_key_press (GtkWidget   *widget,
		     GdkEventKey *event)
{
...
  if (priv->editable)
    {
      if (gtk_im_context_filter_keypress (priv->im_context, event))
	{
	  priv->need_im_reset = TRUE;
	  retval = TRUE;
          goto out;
	}

오늘은 일단 피곤하니까... 졸음 코딩 방지 차원에서 다음에 컨디션 좋을 때 글을 이어가도록 하겠습니다.
끝글자 버그라는게 이렇게 개쉬운 버그니까..
몇 년 동안 방치하지 말고 앞으로 이렇게 고쳐나가자고요. ㅇㅋ?

Forums: 
세벌의 이미지

앞서가시는군요.
저는 Debian stretch 쓰는데.

개쉬운? 요즘엔 개를 앞에 붙이는 유행어가 많네요. :)

늘 수고가 많으십니다.

Hodong Kim@Google의 이미지

어떻게 하다보니까 Buster 사용하네요.
Buster 가 다음달 안정판으로 나올 듯합니다.
https://lists.debian.org/debian-devel-announce/2019/06/msg00003.html
저는 미리 사용하면서 nimf 가 Buster 에서 잘 작동하는지 테스트.
끝글자 버그... 옛날 생각 나는군요.
벌써 4년이 지났습니다. 나름 성과가 있습니다.

2015년에 다솜(현 nimf) 만들기 전에 우분투 포럼에 써놓은 글이 있습니다.
https://forum.ubuntu-kr.org/viewtopic.php?f=4&t=18556&sid=2e2bf6b8444b4a3f2e60370ccfded88f&start=10#p123792

이때 다솜(현재 nimf)을 만들기로 결심을 했고
다솜(현재 nimf) 를 만들어서 ibus 끝글자 버그를 해결했습니다(?)
그래서 ibus의 끝글자 버그에 대한 해결책은 nimf 를 사용하는 것입니다.

hodong » 2015/01/21 Wed 12:50 pm 하는김에 ibus 설정창에서 해당 기능을 껐다 켰다할 수 있도록 그리고 ibus qt 부분도 수정하여 ibus측과 Debian측에 패치 적용해달라고 요청해야겠습니다. 만약 패치 적용을 거부한다면 새로운 입력기 프레임워크를 진행할 생각입니다.

그 지긋지긋한 ibus 끝글자 버그가 이제서야 막을 내립니다.
다들 고생하셨습니다.

ibus 끝글자 버그 - nimf 를 만들어서 해결 완료

ibus 입력기 자체에 있는 끝글자 버그는 ibus 설계 결함으로 발생하는 것이라, 패치로 버그 회피는 가능하지만 설계 변경 없이 근본적으로 고치는 것은 입력기를 새로 만드는 것 이상으로 매우 어렵습니다. 하지만 과거 어떻게든 ibus 를 사용하기 위해 ibus 프로젝트에도 패치를 보낸 바 있고 수용되지 않았고, 그래서 저는 nimf 를 만들어 사용 중입니다. nimf 를 ibus2 란 이름으로 ibus 프로젝트에 패치를 보낼 수도 없고 ㅎㅎ
2015년에 ibus 를 고치는 게 너무 어려워서 GG치고 nimf 를 만들게 된 것입니다.
https://github.com/ibus/ibus/issues/1282#issuecomment-104839603
그래서 ibus 의 끝글자 버그를 해결하는 방법은 nimf 를 사용하는 것입니다.

입력 방법의 미래

해외 각종 프로젝트에 한글 입력 끝글자 버그를 보고하고
패치 코드를 제공하다보면 저절로 nimf 가 홍보되고,
한글 입력 과정이 자연스레 알려집니다.
그 과정을 통해 개발자들이 입력 루틴을 다루는 스킬이 향상되어
리눅스 응용 어플의 입력 품질이 전반적으로 향상될 것이라 기대하고 있습니다.
nimf 로 리눅스 입력 시장을 장악하여 중구난방한 입력 api 를 평정하여 표준화하고
필요하다면 특허도 등록하고 회사도 설립하여 첨단 입력 방법을 선보이겠습니다. ㅋㅋㅋ
농담인거 다들 아시죠?
10년 후에 성지 순례하러 오겠습니다. ㅋㅋㅋ

---

요새 개더운데 건강 유의하세요.

세벌의 이미지

개더우면 개가 고생하죠.
개는 땀샘이 없어서 피부로 땀을 내보내지 못하고 혀를 헐떡인다고 하더군요. :p
Hodong 님도 늘 건강하시길 :)

데비안 버스터 일정 공유 고맙습니다.

Hodong Kim@Google의 이미지

scintilla 에 끝글자 버그 패치는 수정되어 적용이 되었습니다.
https://sourceforge.net/p/scintilla/code/ci/512ec9ab2e7c504e0be2947529e3e104a71bdfc1/
그런데, 패치 적용하고 fcitx 에서 테스트하면 문제가 있다는 의견이 있어서
패치가 삭제될 가능성이 있습니다. 제가 패치 적용하고 fcitx 에서 테스트할 때는 문제가 없는데,
그러한 작동 방식이 GTK+ 에도 사용되는데 scintilla 에서는 그런 동작을 원치 않는 것 같습니다.
아마도 적용된 커밋이 삭제될 듯 합니다.

그리고 wxWidgets 는 GTK 의 wrapper 입니다.
패치 만들어서 적용해보니 부작용이 자꾸 발생하더군요.
https://github.com/wxWidgets/wxWidgets/pull/1357
https://github.com/wxWidgets/wxWidgets/pull/1367
언어별 부작용이 아니라, 이벤트 처리에 대한 부작용을 일컫는 겁니다.
아무튼 여러 삽질 끝에 최종적으로 코드를 작성했는데,
수용할 수 없다는 것으로 이해되는 답변을 받았습니다.

wxWidgets 에 대한 끝글자 버그에 대해서는 왜 이러한 버그가 발생되는지에 대해서는 아래에 상세하게 코드로 설명해놓았습니다.
이 버그는 nimf 에 있는 옵션으로 회피할 수 없습니다.
https://github.com/wxWidgets/wxWidgets/pull/1357

wxWidgets 는 GTK+ 에 대한 wrapper 입니다. 제가 제안한 방식을 수용할 수 없다면,
wxWidgets 에 GTK+ 이벤트 핸들링을 재설계해야 해결할 수 있습니다.
제 입장에서는, 제가 wxWidgets 프로젝트에 대한 권한이 없고 그걸 재설계한다고 해도 수용 여부를 알 수 없는데,
그걸 재설계할 바에는 만약 필요하다면 가칭 Nimdra GUI Toolkit 을 만드는게 좋겠죠.
https://gitlab.gnome.org/GNOME/gtk/issues/1934 버그가 해결되지 않으면 그게 정말 필요할 수도 있습니다.

해당 프로젝트에 자꾸 얘기해봤자, 싸움 나거나 서로 피곤해질 꺼 뻔한 거고,
그래서 저는 GG 쳤습니다.

사용자분들 입장에서는 nimf 만 멀쩡하면 뭐하냐, scintilla, wxWidgets, libreoffice, wine 등의 끝글자 버그는 그대로인데, 땜빵 코드(workaround code)로 해결하는 것은 좋은 해결법이 아니니, 한글 끝글자 버그를 근본적으로 해결해야 하지 않겠느냐 하시는 분들이 당연히 계실텐데, 이러한 사정이 있습니다. 타 응용 어플에 무한정 시간을 소비할 수는 없는 법,

nimf 를 사용하시는 분들은 nimf 에 끝글자 버그를 회피하기 위한 옵션이 있으니 그걸 사용하시기 바랍니다. 회피 옵션 괜히 있는게 아닙니다.

타 응용 어플 끝글자 버그를 고치기 위한 노력은 여기서 끝을 맺겠습니다.

감사합니다.

댓글 첨부 파일: 
첨부파일 크기
Image icon options-1.png34.16 KB
Image icon options-2.png30.03 KB
익명 사용자의 이미지

오늘 geany 쓰다가 하두 짜증나서.. GtkEntry 버그를 고쳤습니다. 패치 올립니다.

root@nimfsoft:/usr/ports/x11-toolkits/gtk30/files # more patch-gtk_gtkentry.c 
--- gtk/gtkentry.c.orig 2020-11-25 12:48:42 UTC
+++ gtk/gtkentry.c
@@ -4308,6 +4308,9 @@ gtk_entry_event (GtkWidget *widget,
   gdouble x, y;
   gint i;
 
+  if (event->type == GDK_BUTTON_PRESS)
+    gtk_entry_reset_im_context (GTK_ENTRY (widget));
+
   if (event->type == GDK_MOTION_NOTIFY &&
       priv->mouse_cursor_obscured &&
       event->any.window == priv->text_area)

gtk_entry_multipress_gesture_pressed(), gtk_entry_real_set_position() 여기에서 reset 했더니... 커서 위치가 안 맞더라구요.
그래서 gtk_entry_event() 에서 reset 했습니다.

https://github.com/gooroom/gtk3/blob/master/debian/patches/gooroom-gtk-entry-reset-im-context-when-mouse-button-press.patch
이 파일도 봤는데...

diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index b8e957e..3950398 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -4308,6 +4308,13 @@ gtk_entry_event (GtkWidget *widget,
   gdouble x, y;
   gint i;
 
+  if (event->type == GDK_BUTTON_PRESS ||
+      event->type == GDK_2BUTTON_PRESS ||
+      event->type == GDK_3BUTTON_PRESS)
+    {
+      gtk_entry_reset_im_context (GTK_ENTRY (widget));
+    }
+
   if (event->type == GDK_MOTION_NOTIFY &&
       priv->mouse_cursor_obscured &&
       event->any.window == priv->text_area)

GDK_2BUTTON_PRESS, GDK_3BUTTON_PRESS 이 있는데, 이거는 두번째 버튼, 세번째 버튼을 의미하는게 아니라, 더블클릭, 트리플 클릭을 의미하는데, gtk_entry_event() 이거 이후에 GtkGesture 에서 GDK_2BUTTON_PRESS, GDK_3BUTTON_PRESS 을 논리적으로 만들어줍니다. 그래서 gtk_entry_multipress_gesture_pressed() 에서나 처리할 수 있는 겁니다.
그래서 요렇게

root@nimfsoft:/usr/ports/x11-toolkits/gtk30/files # more patch-gtk_gtkentry.c 
--- gtk/gtkentry.c.orig 2020-11-25 12:48:42 UTC
+++ gtk/gtkentry.c
@@ -4308,6 +4308,9 @@ gtk_entry_event (GtkWidget *widget,
   gdouble x, y;
   gint i;
 
+  if (event->type == GDK_BUTTON_PRESS)
+    gtk_entry_reset_im_context (GTK_ENTRY (widget));
+
   if (event->type == GDK_MOTION_NOTIFY &&
       priv->mouse_cursor_obscured &&
       event->any.window == priv->text_area)

하시는게 좋겠습니다.
한글 버그 고치는 내용이 널리 널리 알려져서 이런 일 하는 사람들이 많이 늘어나기를 기대해봅니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.