/* Pure Gtk+-3 communication module. -*- coding: utf-8 -*- Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2018 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ /* This should be the first include, as it may set up #defines affecting interpretation of even the system includes. */ #include #include #include "pgtkterm.h" #include "keyboard.h" #include "gtkconfig.h" #include "pgtkim.h" void im_context_commit_cb (GtkIMContext *imc, gchar *str, gpointer user_data) { struct pgtk_display_info *dpyinfo = user_data; struct frame *f = dpyinfo->im.focused_frame; if (dpyinfo->im.context == NULL) return; if (f == NULL) return; pgtk_enqueue_string (f, str); } gboolean im_context_retrieve_surrounding_cb (GtkIMContext *imc, gpointer user_data) { gtk_im_context_set_surrounding (imc, "", -1, 0); return TRUE; } gboolean im_context_delete_surrounding_cb (GtkIMContext *imc, int offset, int n_chars, gpointer user_data) { return TRUE; } static Lisp_Object make_color_string (PangoAttrColor *pac) { char buf[256]; sprintf (buf, "#%02x%02x%02x", pac->color.red >> 8, pac->color.green >> 8, pac->color.blue >> 8); return build_string (buf); } void im_context_preedit_changed_cb (GtkIMContext *imc, gpointer user_data) { struct pgtk_display_info *dpyinfo = user_data; struct frame *f = dpyinfo->im.focused_frame; char *str; PangoAttrList *attrs; int pos; if (dpyinfo->im.context == NULL) return; if (f == NULL) return; gtk_im_context_get_preedit_string (imc, &str, &attrs, &pos); /* * ( * (TEXT (ul . COLOR) (bg . COLOR) (fg . COLOR)) * ... * ) */ Lisp_Object list = Qnil; PangoAttrIterator *iter; iter = pango_attr_list_get_iterator (attrs); do { int st, ed; int has_underline = 0; Lisp_Object part = Qnil; pango_attr_iterator_range (iter, &st, &ed); if (ed > strlen (str)) ed = strlen (str); if (st >= ed) continue; Lisp_Object text = make_string (str + st, ed - st); part = Fcons (text, part); PangoAttrInt *ul = (PangoAttrInt *) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE); if (ul != NULL) { if (ul->value != PANGO_UNDERLINE_NONE) has_underline = 1; } PangoAttrColor *pac; if (has_underline) { pac = (PangoAttrColor *) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE_COLOR); if (pac != NULL) part = Fcons (Fcons (Qul, make_color_string (pac)), part); else part = Fcons (Fcons (Qul, Qt), part); } pac = (PangoAttrColor *) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND); if (pac != NULL) part = Fcons (Fcons (Qfg, make_color_string (pac)), part); pac = (PangoAttrColor *) pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND); if (pac != NULL) part = Fcons (Fcons (Qbg, make_color_string (pac)), part); part = Fnreverse (part); list = Fcons (part, list); } while (pango_attr_iterator_next (iter)); list = Fnreverse (list); pgtk_enqueue_preedit (f, list); g_free (str); pango_attr_list_unref (attrs); } void im_context_preedit_end_cb (GtkIMContext *imc, gpointer user_data) { struct pgtk_display_info *dpyinfo = user_data; struct frame *f = dpyinfo->im.focused_frame; if (dpyinfo->im.context == NULL) return; if (f == NULL) return; pgtk_enqueue_preedit (f, Qnil); } void im_context_preedit_start_cb (GtkIMContext *imc, gpointer user_data) { struct pgtk_display_info *dpyinfo = user_data; struct frame *f = dpyinfo->im.focused_frame; if (dpyinfo->im.context != NULL) { gdouble x, y; x = XWINDOW (f->selected_window)->pixel_left + XWINDOW (f->selected_window)->phys_cursor.x + FRAME_COLUMN_WIDTH (f) + FRAME_LEFT_FRINGE_WIDTH (f) + WINDOW_LEFT_MARGIN_WIDTH (XWINDOW (f->selected_window)); y = XWINDOW (f->selected_window)->pixel_top + XWINDOW (f->selected_window)->phys_cursor.y; GdkRectangle rect; rect.x = (int) x; rect.y = (int) y; rect.width = FRAME_CURSOR_WIDTH (f); rect.height = FRAME_LINE_HEIGHT (f); gtk_im_context_set_cursor_location (dpyinfo->im.context, &rect); } } void pgtk_im_focus_in (struct frame *f) { struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); if (dpyinfo->im.context != NULL) { gtk_im_context_reset (dpyinfo->im.context); #ifndef HAVE_GTK4 gtk_im_context_set_client_window #else gtk_im_context_set_client_widget #endif (dpyinfo->im.context, #ifndef HAVE_GTK4 gtk_widget_get_window (FRAME_GTK_WIDGET (f)) #else FRAME_GTK_WIDGET (f) #endif ); gtk_im_context_focus_in (dpyinfo->im.context); gdouble x, y; x = XWINDOW (f->selected_window)->pixel_left + XWINDOW (f->selected_window)->phys_cursor.x + FRAME_LEFT_FRINGE_WIDTH (f) + WINDOW_LEFT_MARGIN_WIDTH (XWINDOW (f->selected_window)); y = XWINDOW (f->selected_window)->pixel_top + XWINDOW (f->selected_window)->phys_cursor.y; GdkRectangle rect; rect.x = (int) x; rect.y = (int) y; rect.width = FRAME_CURSOR_WIDTH (f); rect.height = FRAME_LINE_HEIGHT (f); gtk_im_context_set_cursor_location (dpyinfo->im.context, &rect); } dpyinfo->im.focused_frame = f; } void pgtk_im_focus_out (struct frame *f) { struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); if (dpyinfo->im.focused_frame == f) { if (dpyinfo->im.context != NULL) { gtk_im_context_reset (dpyinfo->im.context); gtk_im_context_focus_out (dpyinfo->im.context); #ifndef HAVE_GTK4 gtk_im_context_set_client_window (dpyinfo->im.context, NULL); #else gtk_im_context_set_client_widget (dpyinfo->im.context, NULL); #endif } dpyinfo->im.focused_frame = NULL; } } #ifndef HAVE_GTK4 bool pgtk_im_filter_keypress (struct frame *f, GdkEventKey *ev) #else bool pgtk_im_filter_keypress (struct frame *f, GdkEvent *ev) #endif { struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); if (dpyinfo->im.context != NULL) { gdouble x, y; x = XWINDOW (f->selected_window)->pixel_left + XWINDOW (f->selected_window)->phys_cursor.x + FRAME_LEFT_FRINGE_WIDTH (f) + WINDOW_LEFT_MARGIN_WIDTH (XWINDOW (f->selected_window)); y = XWINDOW (f->selected_window)->pixel_top + XWINDOW (f->selected_window)->phys_cursor.y; GdkRectangle rect; rect.x = (int) x; rect.y = (int) y; rect.width = FRAME_CURSOR_WIDTH (f); rect.height = FRAME_LINE_HEIGHT (f); gtk_im_context_set_cursor_location (dpyinfo->im.context, &rect); if (gtk_im_context_filter_keypress (dpyinfo->im.context, ev)) return true; } return false; } void pgtk_im_init (struct pgtk_display_info *dpyinfo) { dpyinfo->im.context = NULL; } void pgtk_im_finish (struct pgtk_display_info *dpyinfo) { if (dpyinfo->im.context != NULL) g_object_unref (dpyinfo->im.context); dpyinfo->im.context = NULL; } DEFUN ("pgtk-use-im-context", Fpgtk_use_im_context, Spgtk_use_im_context, 1, 2, 0, doc: /* Set whether GTK input methods will be used. */) (Lisp_Object use_p, Lisp_Object terminal) { struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal); if (NILP (use_p)) { if (dpyinfo->im.context != NULL) { gtk_im_context_reset (dpyinfo->im.context); gtk_im_context_focus_out (dpyinfo->im.context); #ifndef HAVE_GTK4 gtk_im_context_set_client_window (dpyinfo->im.context, NULL); #else gtk_im_context_set_client_widget (dpyinfo->im.context, NULL); #endif } } else { if (dpyinfo->im.context == NULL) { dpyinfo->im.context = gtk_im_multicontext_new (); g_signal_connect (dpyinfo->im.context, "commit", G_CALLBACK (im_context_commit_cb), dpyinfo); g_signal_connect (dpyinfo->im.context, "retrieve-surrounding", G_CALLBACK (im_context_retrieve_surrounding_cb), dpyinfo); g_signal_connect (dpyinfo->im.context, "delete-surrounding", G_CALLBACK (im_context_delete_surrounding_cb), dpyinfo); g_signal_connect (dpyinfo->im.context, "preedit-changed", G_CALLBACK (im_context_preedit_changed_cb), dpyinfo); g_signal_connect (dpyinfo->im.context, "preedit-end", G_CALLBACK (im_context_preedit_end_cb), dpyinfo); g_signal_connect (dpyinfo->im.context, "preedit-start", G_CALLBACK (im_context_preedit_start_cb), dpyinfo); gtk_im_context_set_use_preedit (dpyinfo->im.context, TRUE); } if (dpyinfo->im.focused_frame) pgtk_im_focus_in (dpyinfo->im.focused_frame); } return Qnil; } void syms_of_pgtkim (void) { defsubr (&Spgtk_use_im_context); DEFSYM (Qpgtk_refresh_preedit, "pgtk-refresh-preedit"); DEFSYM (Qul, "ul"); DEFSYM (Qfg, "fg"); DEFSYM (Qbg, "bg"); }