[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 751789471c: Display pre-edit information from X input methods
From: |
Po Lu |
Subject: |
master 751789471c: Display pre-edit information from X input methods |
Date: |
Fri, 7 Jan 2022 01:42:59 -0500 (EST) |
branch: master
commit 751789471cf04916bcfad358472625f382e596d8
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Display pre-edit information from X input methods
This also repurposes the `pgtk-preedit-text' event to be
meaningful on X, renames it `preedit-text', and documents it.
* doc/lispref/commands.texi (Misc Events): Document
`preedit-text'.
* lisp/term/pgtk-win.el (pgtk-preedit-text): Bind to
`preedit-text' instead.
* lisp/term/x-win.el (x-preedit-overlay): New variable.
(x-preedit-text): New command, bound as a special event to
`preedit-text'.
* src/keyboard.c (kbd_buffer_get_event):
(make_lispy_event): Rename PGTK_PREEDIT_TEXT_EVENT
PREEDIT_TEXT_EVENT.
(syms_of_keyboard): New defsym `preedit-text'.
* src/pgtkterm.c (pgtk_enqueue_preedit): Use PREEDIT_TEXT_EVENT
instead.
* src/termhooks.h (enum event_kind): Rename
`PGTK_PREEDIT_TEXT_EVENT' `PREEDIT_TEXT_EVENT'.
* src/xfns.c (Xxic_preedit_draw_callback):
(Xxic_preedit_caret_callback):
(Xxic_preedit_done_callback):
(Xxic_preedit_start_callback): New callback variables.
(STYLE_OFFTHESPOT, STYLE_OVERTHESPOT):
(STYLE_ROOT, STYLE_CALLBACK, STYLE_NONE): New macros.
(supported_xim_styles): Use reasonable values. This also serves
as a better fix for bug#10867.
(best_xim_style): Restore code deleted as part of the original
fix for bug#10867.
(create_frame_xic): Add preedit callbacks.
(xic_set_preeditarea): Add preedit callbacks.
(x_xic_to_frame):
(xic_preedit_start_callback):
(xic_preedit_caret_callback):
(xic_preedit_done_callback):
(x_xim_text_to_utf8_unix):
(xic_preedit_draw_callback): New functions.
* src/xterm.c (x_detect_focus_change): Fix type of XI event.
(x_free_frame_resources): Free preedit text buffer if still
present.
* src/xterm.h (struct x_output): New fields `preedit_size',
`preedit_chars' and `preedit_active'.
---
doc/lispref/commands.texi | 13 ++
lisp/term/pgtk-win.el | 4 +-
lisp/term/x-win.el | 18 +++
src/keyboard.c | 14 +-
src/pgtkterm.c | 2 +-
src/termhooks.h | 4 +-
src/xfns.c | 335 +++++++++++++++++++++++++++++++++++++++++++++-
src/xterm.c | 5 +-
src/xterm.h | 6 +
9 files changed, 378 insertions(+), 23 deletions(-)
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 0f12fa7241..855b371cac 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2129,6 +2129,19 @@ which @code{1.0} is the width and height of the touchpad
respectively. They are usually interpreted as being relative to the
size of the object beneath the gesture: image, window, etc.
+@cindex @code{preedit-text} event
+@item (preedit-text @var{arg})
+This kind of event is sent when a system input method tells Emacs to
+display some text to indicate to the user what will be inserted. The
+contents of @var{arg} are dependent on the window system being used.
+
+On X, @var{arg} is a string describing some text to place behind the
+cursor. It can be @code{nil}, which means to remove any text
+previously displayed. @c FIXME: what is the value of ARG on PGTK?
+
+It is a special event (@xref{Special Events}), which should normally
+not be bound by the user.
+
@cindex @code{drag-n-drop} event
@item (drag-n-drop @var{position} @var{files})
This kind of event is generated when a group of files is
diff --git a/lisp/term/pgtk-win.el b/lisp/term/pgtk-win.el
index 0f5b9031db..9bcf3eac64 100644
--- a/lisp/term/pgtk-win.el
+++ b/lisp/term/pgtk-win.el
@@ -325,8 +325,7 @@ See the documentation of `create-fontset-from-fontset-spec'
for the format.")
(defun pgtk-preedit-text (event)
"An internal function to display preedit text from input method.
-EVENT is an event of PGTK_PREEDIT_TEXT_EVENT.
-It contains colors and texts."
+EVENT is a `preedit-text-event'."
(interactive "e")
(when pgtk-preedit-overlay
(delete-overlay pgtk-preedit-overlay))
@@ -356,6 +355,7 @@ It contains colors and texts."
(overlay-put ov 'before-string ovstr)
(setq pgtk-preedit-overlay ov)))
+(define-key special-event-map [preedit-text] 'pgtk-preedit-text)
(add-hook 'after-init-hook
(function
diff --git a/lisp/term/x-win.el b/lisp/term/x-win.el
index 62cd984866..6b5e396419 100644
--- a/lisp/term/x-win.el
+++ b/lisp/term/x-win.el
@@ -1517,6 +1517,24 @@ This uses `icon-map-list' to map icon file names to
stock icon names."
(global-set-key [XF86WakeUp] 'ignore)
+
+(defvar x-preedit-overlay nil
+ "The overlay currently used to display preedit text from a compose
sequence.")
+
+(defun x-preedit-text (event)
+ "Display preedit text from a compose sequence in EVENT.
+EVENT is a preedit-text event."
+ (interactive "e")
+ (when x-preedit-overlay
+ (delete-overlay x-preedit-overlay)
+ (setq x-preedit-overlay nil))
+ (when (nth 1 event)
+ (setq x-preedit-overlay (make-overlay (point) (point)))
+ (overlay-put x-preedit-overlay 'before-string
+ (propertize (nth 1 event) 'face '(:underline t)))))
+
+(define-key special-event-map [preedit-text] 'x-preedit-text)
+
(provide 'x-win)
(provide 'term/x-win)
diff --git a/src/keyboard.c b/src/keyboard.c
index ec1b7cd85d..a9f3257282 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -3973,9 +3973,7 @@ kbd_buffer_get_event (KBOARD **kbp,
*used_mouse_menu = true;
FALLTHROUGH;
#endif
-#ifdef HAVE_PGTK
- case PGTK_PREEDIT_TEXT_EVENT:
-#endif
+ case PREEDIT_TEXT_EVENT:
#ifdef HAVE_NTGUI
case END_SESSION_EVENT:
case LANGUAGE_CHANGE_EVENT:
@@ -6289,10 +6287,8 @@ make_lispy_event (struct input_event *event)
return list3 (Qconfig_changed_event,
event->arg, event->frame_or_window);
-#ifdef HAVE_PGTK
- case PGTK_PREEDIT_TEXT_EVENT:
- return list2 (intern ("pgtk-preedit-text"), event->arg);
-#endif
+ case PREEDIT_TEXT_EVENT:
+ return list2 (Qpreedit_text, event->arg);
/* The 'kind' field of the event is something we don't recognize. */
default:
@@ -12003,6 +11999,8 @@ syms_of_keyboard (void)
DEFSYM (Qno_record, "no-record");
DEFSYM (Qencoded, "encoded");
+ DEFSYM (Qpreedit_text, "preedit-text");
+
button_down_location = make_nil_vector (5);
staticpro (&button_down_location);
staticpro (&frame_relative_event_pos);
@@ -12771,8 +12769,6 @@ keys_of_keyboard (void)
"ns-put-working-text");
initial_define_lispy_key (Vspecial_event_map, "ns-unput-working-text",
"ns-unput-working-text");
- initial_define_lispy_key (Vspecial_event_map, "pgtk-preedit-text",
- "pgtk-preedit-text");
/* Here we used to use `ignore-event' which would simple set prefix-arg to
current-prefix-arg, as is done in `handle-switch-frame'.
But `handle-switch-frame is not run from the special-map.
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index 736fce09c4..1d301d11f6 100644
--- a/src/pgtkterm.c
+++ b/src/pgtkterm.c
@@ -5259,7 +5259,7 @@ pgtk_enqueue_preedit (struct frame *f, Lisp_Object
preedit)
{
union buffered_input_event inev;
EVENT_INIT (inev.ie);
- inev.ie.kind = PGTK_PREEDIT_TEXT_EVENT;
+ inev.ie.kind = PREEDIT_TEXT_EVENT;
inev.ie.arg = preedit;
inev.ie.code = 0;
XSETFRAME (inev.ie.frame_or_window, f);
diff --git a/src/termhooks.h b/src/termhooks.h
index 55f7aa5d1a..518e855eae 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -269,10 +269,8 @@ enum event_kind
, FILE_NOTIFY_EVENT
#endif
-#ifdef HAVE_PGTK
/* Pre-edit text was changed. */
- , PGTK_PREEDIT_TEXT_EVENT
-#endif
+ , PREEDIT_TEXT_EVENT
/* Either the mouse wheel has been released without it being
clicked, or the user has lifted his finger from a touchpad.
diff --git a/src/xfns.c b/src/xfns.c
index b94fe17922..d87e67f95b 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -24,6 +24,7 @@ along with GNU Emacs. If not, see
<https://www.gnu.org/licenses/>. */
#include <unistd.h>
#include "lisp.h"
+#include "character.h"
#include "xterm.h"
#include "frame.h"
#include "window.h"
@@ -2330,8 +2331,19 @@ hack_wm_protocols (struct frame *f, Widget widget)
#ifdef HAVE_X_I18N
-static XFontSet xic_create_xfontset (struct frame *);
-static XIMStyle best_xim_style (XIMStyles *);
+static void xic_preedit_draw_callback (XIC, XPointer,
XIMPreeditDrawCallbackStruct *);
+static void xic_preedit_caret_callback (XIC, XPointer,
XIMPreeditCaretCallbackStruct *);
+static void xic_preedit_done_callback (XIC, XPointer, XPointer);
+static int xic_preedit_start_callback (XIC, XPointer, XPointer);
+
+static XIMCallback Xxic_preedit_draw_callback = { NULL,
+ (XIMProc)
xic_preedit_draw_callback };
+static XIMCallback Xxic_preedit_caret_callback = { NULL,
+ (XIMProc)
xic_preedit_caret_callback };
+static XIMCallback Xxic_preedit_done_callback = { NULL,
+ (XIMProc)
xic_preedit_done_callback };
+static XIMCallback Xxic_preedit_start_callback = { NULL,
+ (void *)
xic_preedit_start_callback };
#if defined HAVE_X_WINDOWS && defined USE_X_TOOLKIT
/* Create an X fontset on frame F with base font name BASE_FONTNAME. */
@@ -2608,6 +2620,23 @@ xic_free_xfontset (struct frame *f)
FRAME_XIC_FONTSET (f) = NULL;
}
+/* Create XIC for frame F. */
+
+
+#define STYLE_OFFTHESPOT (XIMPreeditArea | XIMStatusArea)
+#define STYLE_OVERTHESPOT (XIMPreeditPosition | XIMStatusNothing)
+#define STYLE_ROOT (XIMPreeditNothing | XIMStatusNothing)
+#define STYLE_CALLBACK (XIMPreeditCallbacks | XIMStatusNothing)
+#define STYLE_NONE (XIMPreeditNothing | XIMStatusNothing)
+
+static const XIMStyle supported_xim_styles[] =
+ {
+ STYLE_CALLBACK,
+ STYLE_NONE,
+ STYLE_OVERTHESPOT,
+ STYLE_OFFTHESPOT,
+ STYLE_ROOT
+ };
/* Value is the best input style, given user preferences USER (already
checked to be supported by Emacs), and styles supported by the
@@ -2616,8 +2645,15 @@ xic_free_xfontset (struct frame *f)
static XIMStyle
best_xim_style (XIMStyles *xim)
{
- /* Return the default style. This is what GTK3 uses and
- should work fine with all modern input methods. */
+ int i, j;
+ int nr_supported = ARRAYELTS (supported_xim_styles);
+
+ for (i = 0; i < nr_supported; ++i)
+ for (j = 0; j < xim->count_styles; ++j)
+ if (supported_xim_styles[i] == xim->supported_styles[j])
+ return supported_xim_styles[i];
+
+ /* Return the default style. */
return XIMPreeditNothing | XIMStatusNothing;
}
@@ -2692,6 +2728,22 @@ create_frame_xic (struct frame *f)
goto out;
}
+ if (xic_style & XIMPreeditCallbacks)
+ {
+ spot.x = 0;
+ spot.y = 0;
+ preedit_attr = XVaCreateNestedList (0,
+ XNSpotLocation, &spot,
+ XNPreeditStartCallback,
&Xxic_preedit_start_callback,
+ XNPreeditDoneCallback,
&Xxic_preedit_done_callback,
+ XNPreeditDrawCallback,
&Xxic_preedit_draw_callback,
+ XNPreeditCaretCallback,
&Xxic_preedit_caret_callback,
+ NULL);
+
+ if (!preedit_attr)
+ goto out;
+ }
+
if (preedit_attr && status_attr)
xic = XCreateIC (xim,
XNInputStyle, xic_style,
@@ -2768,7 +2820,12 @@ xic_set_preeditarea (struct window *w, int x, int y)
spot.x = WINDOW_TO_FRAME_PIXEL_X (w, x) + WINDOW_LEFT_FRINGE_WIDTH (w) +
WINDOW_LEFT_MARGIN_WIDTH(w);
spot.y = WINDOW_TO_FRAME_PIXEL_Y (w, y) + FONT_BASE (FRAME_FONT (f));
- attr = XVaCreateNestedList (0, XNSpotLocation, &spot, NULL);
+ attr = XVaCreateNestedList (0, XNSpotLocation, &spot,
+ XNPreeditStartCallback,
&Xxic_preedit_start_callback,
+ XNPreeditDoneCallback,
&Xxic_preedit_done_callback,
+ XNPreeditDrawCallback,
&Xxic_preedit_draw_callback,
+ XNPreeditCaretCallback,
&Xxic_preedit_caret_callback,
+ NULL);
XSetICValues (FRAME_XIC (f), XNPreeditAttributes, attr, NULL);
XFree (attr);
}
@@ -2816,9 +2873,273 @@ xic_set_statusarea (struct frame *f)
XFree (attr);
}
+static struct frame *
+x_xic_to_frame (XIC xic)
+{
+ Lisp_Object tail, tem;
+ struct frame *f;
+
+ FOR_EACH_FRAME (tail, tem)
+ {
+ f = XFRAME (tem);
+
+ if (FRAME_X_P (f) && FRAME_XIC (f) == xic)
+ return f;
+ }
+
+ return NULL;
+}
+
+static int
+xic_preedit_start_callback (XIC xic, XPointer client_data,
+ XPointer call_data)
+{
+ struct frame *f = x_xic_to_frame (xic);
+ struct x_output *output;
+
+ if (f)
+ {
+ output = FRAME_X_OUTPUT (f);
+
+ output->preedit_size = 0;
+ output->preedit_active = true;
+
+ if (output->preedit_chars)
+ xfree (output->preedit_chars);
+
+ output->preedit_chars = NULL;
+ }
+
+ return -1;
+}
+
+static void
+xic_preedit_caret_callback (XIC xic, XPointer client_data,
+ XIMPreeditCaretCallbackStruct *call_data)
+{
+
+}
+
+
+static void
+xic_preedit_done_callback (XIC xic, XPointer client_data,
+ XPointer call_data)
+{
+ struct frame *f = x_xic_to_frame (xic);
+ struct x_output *output;
+ struct input_event ie;
+
+ if (f)
+ {
+ ie.kind = PREEDIT_TEXT_EVENT;
+ ie.arg = Qnil;
+ XSETFRAME (ie.frame_or_window, f);
+ XSETINT (ie.x, 0);
+ XSETINT (ie.y, 0);
+ kbd_buffer_store_event (&ie);
+
+ output = FRAME_X_OUTPUT (f);
+
+ if (output->preedit_chars)
+ xfree (output->preedit_chars);
+
+ output->preedit_size = 0;
+ output->preedit_active = false;
+ output->preedit_chars = NULL;
+ }
+}
+
+/* The string returned is not null-terminated. */
+static char *
+x_xim_text_to_utf8_unix (XIMText *text, ptrdiff_t *length)
+{
+ unsigned char *wchar_buf;
+ ptrdiff_t wchar_actual_length, i;
+ ptrdiff_t nbytes;
+ struct coding_system coding;
+
+ if (text->encoding_is_wchar)
+ {
+ wchar_buf = xmalloc ((text->length + 1) * MAX_MULTIBYTE_LENGTH);
+ wchar_actual_length = 0;
+
+ for (i = 0; i < text->length; ++i)
+ wchar_actual_length += CHAR_STRING (text->string.wide_char[i],
+ wchar_buf + wchar_actual_length);
+ *length = wchar_actual_length;
+
+ return (char *) wchar_buf;
+ }
+
+ nbytes = strlen (text->string.multi_byte);
+ setup_coding_system (Qutf_8_unix, &coding);
+ coding.mode |= (CODING_MODE_LAST_BLOCK
+ | CODING_MODE_SAFE_ENCODING);
+ coding.source = (const unsigned char *) text->string.multi_byte;
+ coding.dst_bytes = 2048;
+ coding.destination = xmalloc (2048);
+ decode_coding_object (&coding, Qnil, 0, 0, nbytes, nbytes, Qnil);
+
+ /* coding.destination has either been allocated by us, or
+ reallocated by decode_coding_object. */
+
+ *length = coding.produced;
+ return (char *) coding.destination;
+}
+
+static void
+xic_preedit_draw_callback (XIC xic, XPointer client_data,
+ XIMPreeditDrawCallbackStruct *call_data)
+{
+ struct frame *f = x_xic_to_frame (xic);
+ struct x_output *output;
+ ptrdiff_t text_length;
+ ptrdiff_t charpos;
+ ptrdiff_t original_size;
+ char *text;
+ char *chg_start, *chg_end;
+ struct input_event ie;
+
+ if (f)
+ {
+ output = FRAME_X_OUTPUT (f);
+
+ if (!output->preedit_active)
+ return;
+
+ if (call_data->text)
+ text = x_xim_text_to_utf8_unix (call_data->text, &text_length);
+ else
+ text = NULL;
+
+ original_size = output->preedit_size;
+
+ /* This is an ordinary insertion: reallocate the buffer to hold
+ enough for TEXT. */
+ if (!call_data->chg_length)
+ {
+ if (!text)
+ goto im_abort;
+
+ if (output->preedit_chars)
+ output->preedit_chars = xrealloc (output->preedit_chars,
+ output->preedit_size +=
text_length);
+ else
+ output->preedit_chars = xmalloc (output->preedit_size +=
text_length);
+ }
+
+ chg_start = output->preedit_chars;
+
+ /* The IM sent bad data: the buffer is empty, but the change
+ position is more than 0. */
+ if (!output->preedit_chars && call_data->chg_first)
+ goto im_abort;
+
+ /* Find the byte position for the character position where the
+ first change is to be made. */
+ if (call_data->chg_first)
+ {
+ charpos = 0;
+
+ while (charpos < call_data->chg_first)
+ {
+ chg_start += BYTES_BY_CHAR_HEAD (*chg_start);
+
+ if ((chg_start - output->preedit_chars) > output->preedit_size)
+ /* The IM sent bad data: chg_start is larger than the
+ current buffer. */
+ goto im_abort;
+ ++charpos;
+ }
+ }
+
+ if (!call_data->chg_length)
+ {
+ if (!text)
+ goto im_abort;
+
+ memmove (chg_start + text_length, chg_start,
+ original_size - (chg_start - output->preedit_chars));
+ memcpy (chg_start, text, text_length);
+ }
+ else
+ {
+ if (call_data->chg_length < 1)
+ goto im_abort;
+
+ charpos = 0;
+ chg_end = chg_start;
+
+ while (charpos < call_data->chg_length)
+ {
+ chg_end += BYTES_BY_CHAR_HEAD (*chg_end);
+
+ if ((chg_end - output->preedit_chars) > output->preedit_size)
+ /* The IM sent bad data: chg_end ends someplace outside
+ the current buffer. */
+ goto im_abort;
+ ++charpos;
+ }
+
+ memmove (chg_start, chg_end, ((output->preedit_chars
+ + output->preedit_size) - chg_end));
+ output->preedit_size -= (chg_end - chg_start);
+
+ if (text)
+ {
+ original_size = output->preedit_size;
+ output->preedit_chars = xrealloc (output->preedit_chars,
+ output->preedit_size +=
text_length);
+
+ /* Find chg_start again, since preedit_chars was reallocated. */
+
+ chg_start = output->preedit_chars;
+ charpos = 0;
-/* Set X fontset for XIC of frame F, using base font name
- BASE_FONTNAME. Called when a new Emacs fontset is chosen. */
+ while (charpos < call_data->chg_first)
+ {
+ chg_start += BYTES_BY_CHAR_HEAD (*chg_start);
+
+ if ((chg_start - output->preedit_chars) >
output->preedit_size)
+ /* The IM sent bad data: chg_start is larger than the
+ current buffer. */
+ goto im_abort;
+ ++charpos;
+ }
+
+ memmove (chg_start + text_length, chg_start,
+ original_size - (chg_start - output->preedit_chars));
+ memcpy (chg_start, text, text_length);
+ }
+ }
+
+ if (text)
+ xfree (text);
+
+ /* This is okay because this callback is called from the big XIM
+ event filter, which runs inside XTread_socket. */
+
+ ie.kind = PREEDIT_TEXT_EVENT;
+ XSETFRAME (ie.frame_or_window, f);
+ ie.arg = make_string_from_utf8 (output->preedit_chars,
+ output->preedit_size);
+ XSETINT (ie.x, 0);
+ XSETINT (ie.y, 0);
+
+ kbd_buffer_store_event (&ie);
+ }
+
+ return;
+
+ im_abort:
+ if (text)
+ xfree (text);
+ if (output->preedit_chars)
+ xfree (output->preedit_chars);
+ output->preedit_chars = NULL;
+ output->preedit_size = 0;
+ output->preedit_active = false;
+}
void
xic_set_xfontset (struct frame *f, const char *base_fontname)
diff --git a/src/xterm.c b/src/xterm.c
index 1d4c775753..73c0bcf89e 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -5198,7 +5198,7 @@ x_detect_focus_change (struct x_display_info *dpyinfo,
struct frame *frame,
#ifdef HAVE_XINPUT2
case GenericEvent:
{
- XIEvent *xi_event = (XIEvent *) event;
+ XIEvent *xi_event = (XIEvent *) event->xcookie.data;
struct frame *focus_frame = dpyinfo->x_focus_event_frame;
int focus_state
@@ -14046,6 +14046,9 @@ x_free_frame_resources (struct frame *f)
#ifdef HAVE_X_I18N
if (FRAME_XIC (f))
free_frame_xic (f);
+
+ if (f->output_data.x->preedit_chars)
+ xfree (f->output_data.x->preedit_chars);
#endif
#ifdef USE_CAIRO
diff --git a/src/xterm.h b/src/xterm.h
index d4600bdf80..dcac573252 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -788,6 +788,12 @@ struct x_output
They are used when creating the cairo surface next time. */
int cr_surface_desired_width, cr_surface_desired_height;
#endif
+
+#ifdef HAVE_X_I18N
+ ptrdiff_t preedit_size;
+ char *preedit_chars;
+ bool preedit_active;
+#endif
};
enum
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- master 751789471c: Display pre-edit information from X input methods,
Po Lu <=