public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Takao Fujiwara <tfujiwar@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/ibus] autotool: Implement shortcut keys on emoji dialog
Date: Sun, 31 May 2026 02:06:30 GMT	[thread overview]
Message-ID: <178019319064.1.16712814926975530344.rpms-ibus-6a3b67701391@fedoraproject.org> (raw)

            A new commit has been pushed.

            Repo   : rpms/ibus
            Branch : autotool
            Commit : 6a3b6770139172a4fad44e499971f9d2a52066d6
            Author : Takao Fujiwara <tfujiwar@redhat.com>
            Date   : 2017-03-15T12:41:29+09:00
            Stats  : +550/-1 in 2 file(s)
            URL    : https://src.fedoraproject.org/rpms/ibus/c/6a3b6770139172a4fad44e499971f9d2a52066d6?branch=autotool

            Log:
            Implement shortcut keys on emoji dialog

- Implemented Ctrl-[f|b|n|p|h|e|a|u] for cursor operations on emoji dialog
- Added XSetIOErrorHandler() for GNOME3 desktop

---
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index 45d0eef..56a760c 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -1223,3 +1223,548 @@ index 8cecea8..5e126e9 100644
 -- 
 2.9.3
 
+From ab6c38c192cdf22356cbf254b98fb5b3d9d9a680 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 15 Mar 2017 11:48:24 +0900
+Subject: [PATCH] client/x11: Add XSetIOErrorHandler() for GNOME3 desktop
+
+When log into GNOME3 desktop immediately after the system is booted,
+ibus-daemon is sometimes alive but ibus-x11 is dead after log out
+the session. Because gdk_x_io_error() is called as the callback of
+XSetIOErrorHandler() in gtk/gdk/x11/gdkmain-x11.c in ibus-x11.
+Now I assume the callback is called in logout.
+
+BUG=https://github.com/ibus/ibus/issues/1907
+
+Review URL: https://codereview.appspot.com/319490043
+---
+ client/x11/main.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/client/x11/main.c b/client/x11/main.c
+index a717a2c..159f430 100644
+--- a/client/x11/main.c
++++ b/client/x11/main.c
+@@ -2,6 +2,7 @@
+ /* vim:set et sts=4: */
+ /* ibus
+  * Copyright (C) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  * Copyright (C) 2007-2015 Red Hat, Inc.
+  *
+  * main.c:
+@@ -1131,6 +1132,20 @@ _xerror_handler (Display *dpy, XErrorEvent *e)
+     return 1;
+ }
+ 
++/* When log into GNOME3 desktop immediately after the system is booted,
++ * ibus-daemon is sometimes alive but ibus-x11 is dead after log out
++ * the session. Because gdk_x_io_error() is called as the callback of
++ * XSetIOErrorHandler() in gtk/gdk/x11/gdkmain-x11.c in ibus-x11.
++ * Now I assume the callback is called in logout.
++ */
++static int
++_xerror_io_handler (Display *dpy)
++{
++    if (_kill_daemon)
++        _atexit_cb ();
++    return 0;
++}
++
+ int
+ main (int argc, char **argv)
+ {
+@@ -1146,6 +1161,7 @@ main (int argc, char **argv)
+ 
+     gtk_init (&argc, &argv);
+     XSetErrorHandler (_xerror_handler);
++    XSetIOErrorHandler (_xerror_io_handler);
+ 
+     while (1) {
+         static struct option long_options [] = {
+-- 
+2.9.3
+
+From 58f6140f427815adc947a5bb5c7dea4f3e315ae8 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 15 Mar 2017 11:52:39 +0900
+Subject: [PATCH] ui/gtk3: Implement shortcut keys on emoji dialog
+
+- Implement Ctrl-f, Ctrl-b, Ctrl-n, Ctrl-p, Ctrl-h, Ctrh-e for
+  cursor movements; forward, back, next, previous, head, end
+  on emoji grid.
+- Implement Ctrl-a and Shift+arrow for text selection on emoji annotation.
+- Implement Ctrl-u to delete text on emoji annotation.
+- Implement to delete a selected text on emoji annotation.
+- Change to show page indices to candidate indices on emoji.
+- Sorted emoji categories.
+- Added timeout of m_enter_notify_enable = false to bring back mouse.
+
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/315700043
+---
+ ui/gtk3/emojier.vala | 311 +++++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 215 insertions(+), 96 deletions(-)
+
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 5e126e9..7da96c7 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -214,6 +214,7 @@ class IBusEmojier : Gtk.Window {
+     private string[] m_favorites = {};
+     private bool m_enter_notify_enable = true;
+     private uint m_entry_notify_show_id;
++    private uint m_entry_notify_disable_id;
+ 
+     public signal void candidate_clicked(uint index, uint button, uint state);
+     public signal void loaded_emoji_dict();
+@@ -525,6 +526,18 @@ class IBusEmojier : Gtk.Window {
+         show_category_list();
+     }
+ 
++    private string get_title_string(string orig) {
++        StringBuilder buff = new StringBuilder();
++        for (int i = 0; i < orig.char_count(); i++) {
++            unichar ch = orig.get_char(i);
++            if (i == 0)
++                buff.append_unichar(ch.toupper());
++            else
++                buff.append_unichar(ch);
++        }
++        return buff.str;
++    }
++
+     private void show_category_list() {
+         remove_all_children();
+         m_scrolled_window = new EScrolledWindow();
+@@ -586,19 +599,14 @@ class IBusEmojier : Gtk.Window {
+             }
+             GLib.List<unowned string> categories =
+                     m_category_to_emojis_dict.get_keys();
++            categories.sort((a, b) => {
++                return GLib.strcmp(_(a), _(b));
++            });
+             foreach (unowned string category in categories) {
+                 EBoxRow row = new EBoxRow(category);
+                 string locale_category = _(category);
+-                StringBuilder capital_category = new StringBuilder();
+-                for (int i = 0; i < locale_category.char_count(); i++) {
+-                    unichar ch = locale_category.get_char(i);
+-                    if (i == 0)
+-                        capital_category.append_unichar(ch.toupper());
+-                    else
+-                        capital_category.append_unichar(ch);
+-                }
+                 EPaddedLabel widget =
+-                        new EPaddedLabel(capital_category.str,
++                        new EPaddedLabel(get_title_string(locale_category),
+                                          Gtk.Align.CENTER);
+                 row.add(widget);
+                 m_list_box.add(row);
+@@ -650,7 +658,7 @@ class IBusEmojier : Gtk.Window {
+                 IBus.Text text = new IBus.Text.from_string(emoji);
+                 m_lookup_table.append_candidate(text);
+             }
+-            m_backward = row.text;
++            m_backward = get_title_string(row.text);
+         }
+         show_candidate_panel();
+     }
+@@ -759,9 +767,7 @@ class IBusEmojier : Gtk.Window {
+         uint page_end_pos = uint.min(page_start_pos + page_size, ncandidates);
+         if (m_backward != null) {
+             string backward_desc =
+-                    "%s (%u / %u)".printf(m_backward,
+-                                          cursor / page_size + 1,
+-                                          ncandidates / page_size + 1);
++                    "%s (%u / %u)".printf(m_backward, cursor, ncandidates - 1);
+             EPaddedLabel label = new EPaddedLabel(backward_desc,
+                                                   Gtk.Align.CENTER,
+                                                   TravelDirection.BACKWARD);
+@@ -805,23 +811,23 @@ class IBusEmojier : Gtk.Window {
+                 candidate_clicked(index, e.button, e.state);
+                 return true;
+             });
+-            // m_enter_notify_enable is added because
+-            // enter_notify_event conflicts with keyboard operations.
+-            if (m_enter_notify_enable) {
+-                candidate_ebox.enter_notify_event.connect((e) => {
+-                    m_lookup_table.set_cursor_pos(index);
+-                    if (m_entry_notify_show_id > 0) {
++            candidate_ebox.enter_notify_event.connect((e) => {
++                // m_enter_notify_enable is added because
++                // enter_notify_event conflicts with keyboard operations.
++                if (!m_enter_notify_enable)
++                    return true;
++                m_lookup_table.set_cursor_pos(index);
++                if (m_entry_notify_show_id > 0) {
+                         GLib.Source.remove(m_entry_notify_show_id);
+-                    }
+-                    // If timeout is not added, memory leak happens and
+-                    // button_press_event signal does not work above.
+-                    m_entry_notify_show_id = GLib.Timeout.add(100, () => {
++                }
++                // If timeout is not added, memory leak happens and
++                // button_press_event signal does not work above.
++                m_entry_notify_show_id = GLib.Timeout.add(100, () => {
+                         show_candidate_panel();
+                         return false;
+-                    });
+-                    return true;
+                 });
+-            }
++                return true;
++            });
+             grid.attach(candidate_ebox,
+                         n % (int)EMOJI_GRID_PAGE, n / (int)EMOJI_GRID_PAGE,
+                         1, 1);
+@@ -844,16 +850,23 @@ class IBusEmojier : Gtk.Window {
+                 widget.show_all();
+                 return;
+             }
+-            unowned IBus.EmojiData data =
++            unowned IBus.EmojiData? data =
+                     m_emoji_to_data_dict.lookup(candidate.text);
+-            unowned string description = data.get_description();
+-            if (description != "") {
++            if (data == null) {
++                // TODO: Provide a description for the favorite emojis.
+                 EPaddedLabel widget = new EPaddedLabel(
+-                        _("Description: %s").printf(description),
++                        _("Description: %s").printf(_("None")),
+                         Gtk.Align.START);
+                 m_vbox.add(widget);
+                 widget.show_all();
++                return;
+             }
++            unowned string description = data.get_description();
++            EPaddedLabel desc_widget = new EPaddedLabel(
++                    _("Description: %s").printf(description),
++                    Gtk.Align.START);
++            m_vbox.add(desc_widget);
++            desc_widget.show_all();
+             unowned GLib.SList<unowned string>? annotations =
+                     data.get_annotations();
+             GLib.StringBuilder buff = new GLib.StringBuilder();
+@@ -922,8 +935,21 @@ class IBusEmojier : Gtk.Window {
+         m_result = text.text;
+     }
+ 
+-    private void candidate_panel_cursor_down() {
++    private void enter_notify_disable_with_timer() {
++        // Enable keyboard operation and disable mouse operation.
+         m_enter_notify_enable = false;
++        if (m_entry_notify_disable_id > 0) {
++            GLib.Source.remove(m_entry_notify_disable_id);
++        }
++        // Bring back the mouse operation after a timeout.
++        m_entry_notify_show_id = GLib.Timeout.add(100, () => {
++            m_enter_notify_enable = true;
++            return false;
++        });
++    }
++
++    private void candidate_panel_cursor_down() {
++        enter_notify_disable_with_timer();
+         uint ncandidates = m_lookup_table.get_number_of_candidates();
+         uint cursor = m_lookup_table.get_cursor_pos();
+         if ((cursor + EMOJI_GRID_PAGE) < ncandidates) {
+@@ -937,11 +963,11 @@ class IBusEmojier : Gtk.Window {
+     }
+ 
+     private void candidate_panel_cursor_up() {
+-        m_enter_notify_enable = false;
++        enter_notify_disable_with_timer();
+         int ncandidates = (int)m_lookup_table.get_number_of_candidates();
+         int cursor = (int)m_lookup_table.get_cursor_pos();
+         int highest_pos =
+-            (ncandidates / (int)EMOJI_GRID_PAGE * (int)EMOJI_GRID_PAGE)
++            ((ncandidates - 1)/ (int)EMOJI_GRID_PAGE * (int)EMOJI_GRID_PAGE)
+             + (cursor % (int)EMOJI_GRID_PAGE);
+         if ((cursor - (int)EMOJI_GRID_PAGE) >= 0) {
+             m_lookup_table.set_cursor_pos(cursor - (int)EMOJI_GRID_PAGE);
+@@ -967,13 +993,119 @@ class IBusEmojier : Gtk.Window {
+         show_category_list();
+     }
+ 
++    private bool key_press_cursor_horizontal(uint keyval,
++                                             uint modifiers) {
++        assert (keyval == Gdk.Key.Left || keyval == Gdk.Key.Right);
++
++        if (m_candidate_panel_is_visible) {
++            enter_notify_disable_with_timer();
++            if (keyval == Gdk.Key.Left)
++                m_lookup_table.cursor_up();
++            else if (keyval == Gdk.Key.Right)
++                m_lookup_table.cursor_down();
++            show_candidate_panel();
++        } else if (m_entry.get_text().len() > 0) {
++            int step = 0;
++            if (keyval == Gdk.Key.Left)
++                step = -1;
++            else if (keyval == Gdk.Key.Right)
++                step = 1;
++            GLib.Signal.emit_by_name(
++                    m_entry, "move-cursor",
++                    Gtk.MovementStep.VISUAL_POSITIONS,
++                    step,
++                    (modifiers & Gdk.ModifierType.SHIFT_MASK) != 0
++                            ? true : false);
++        } else {
++            // For Gdk.Key.f and Gdk.Key.b
++            if (keyval == Gdk.Key.Left)
++                keyval = Gdk.Key.Up;
++            else if (keyval == Gdk.Key.Right)
++                keyval = Gdk.Key.Down;
++            category_list_cursor_move(keyval);
++        }
++        return true;
++    }
++
++    private bool key_press_cursor_vertical(uint keyval) {
++        assert (keyval == Gdk.Key.Down || keyval == Gdk.Key.Up);
++
++        if (m_candidate_panel_is_visible) {
++            if (keyval == Gdk.Key.Down)
++                candidate_panel_cursor_down();
++            else if (keyval == Gdk.Key.Up)
++                candidate_panel_cursor_up();
++        } else {
++            category_list_cursor_move(keyval);
++        }
++        return true;
++    }
++
++    private bool key_press_cursor_home_end(uint keyval,
++                                           uint modifiers) {
++        assert (keyval == Gdk.Key.Home || keyval == Gdk.Key.End);
++
++        if (m_candidate_panel_is_visible) {
++            enter_notify_disable_with_timer();
++            if (keyval == Gdk.Key.Home) {
++                m_lookup_table.set_cursor_pos(0);
++            } else if (keyval == Gdk.Key.End) {
++                uint ncandidates = m_lookup_table.get_number_of_candidates();
++                m_lookup_table.set_cursor_pos(ncandidates - 1);
++            }
++            show_candidate_panel();
++            return true;
++        }
++        if (m_entry.get_text().len() > 0) {
++            int step = 0;
++            if (keyval == Gdk.Key.Home)
++                step = -1;
++            else if (keyval == Gdk.Key.End)
++                step = 1;
++            GLib.Signal.emit_by_name(
++                    m_entry, "move-cursor",
++                    Gtk.MovementStep.DISPLAY_LINE_ENDS,
++                    step,
++                    (modifiers & Gdk.ModifierType.SHIFT_MASK) != 0
++                            ? true : false);
++            return true;
++        }
++        return false;
++    }
++
++    private bool key_press_cursor_escape() {
++        if (m_candidate_panel_is_visible) {
++            hide_candidate_panel();
++            return true;
++        } else if (m_current_category_type == CategoryType.LANG) {
++            m_current_category_type = CategoryType.EMOJI;
++            show_candidate_panel();
++            return true;
++        } else if (m_entry.get_text().length == 0) {
++            m_loop.quit();
++            hide_candidate_panel();
++            return true;
++        }
++        m_entry.delete_text(0, -1);
++        return true;
++    }
++
+     private void entry_enter_keyval(uint keyval) {
+         unichar ch = IBus.keyval_to_unicode(keyval);
+-        if (!ch.isgraph())
++        if (ch.iscntrl())
+             return;
+         string str = ch.to_string();
+ 
+         // what gtk_entry_commit_cb() do
++        if (m_entry.get_selection_bounds(null, null)) {
++            m_entry.delete_selection();
++        } else {
++            if (m_entry.get_overwrite_mode()) {
++               uint text_length = m_entry.get_buffer().get_length();
++               if (m_entry.cursor_position < text_length)
++                   m_entry.delete_from_cursor(Gtk.DeleteType.CHARS, 1);
++            }
++        }
+         int pos = m_entry.get_position();
+         m_entry.insert_text(str, -1, ref pos);
+         m_entry.set_position(pos);
+@@ -1084,19 +1216,8 @@ class IBusEmojier : Gtk.Window {
+         }
+         switch (keyval) {
+         case Gdk.Key.Escape:
+-            if (m_candidate_panel_is_visible) {
+-                hide_candidate_panel();
+-                return true;
+-            } else if (m_current_category_type == CategoryType.LANG) {
+-                m_current_category_type = CategoryType.EMOJI;
+-                show_candidate_panel();
++            if (key_press_cursor_escape())
+                 return true;
+-            } else if (m_entry.get_text().length == 0) {
+-                m_loop.quit();
+-                hide_candidate_panel();
+-                return true;
+-            }
+-            m_entry.delete_text(0, -1);
+             break;
+         case Gdk.Key.Return:
+             if (m_candidate_panel_is_visible) {
+@@ -1126,7 +1247,7 @@ class IBusEmojier : Gtk.Window {
+                 break;
+             }
+             if (m_candidate_panel_is_visible) {
+-                m_enter_notify_enable = false;
++                enter_notify_disable_with_timer();
+                 m_lookup_table.cursor_down();
+                 show_candidate_panel();
+             }
+@@ -1135,48 +1256,20 @@ class IBusEmojier : Gtk.Window {
+             }
+             return true;
+         case Gdk.Key.Right:
+-            if (m_candidate_panel_is_visible) {
+-                m_enter_notify_enable = false;
+-                m_lookup_table.cursor_down();
+-                show_candidate_panel();
+-                return true;
+-            }
+-            if (m_entry.get_text().len() > 0) {
+-                GLib.Signal.emit_by_name(m_entry, "move-cursor",
+-                                         Gtk.MovementStep.VISUAL_POSITIONS,
+-                                         1, false);
+-                return true;
+-            }
+-            break;
++            key_press_cursor_horizontal(keyval, modifiers);
++            return true;
+         case Gdk.Key.Left:
+-            if (m_candidate_panel_is_visible) {
+-                m_enter_notify_enable = false;
+-                m_lookup_table.cursor_up();
+-                show_candidate_panel();
+-                return true;
+-            }
+-            if (m_entry.get_text().len() > 0) {
+-                GLib.Signal.emit_by_name(m_entry, "move-cursor",
+-                                         Gtk.MovementStep.VISUAL_POSITIONS,
+-                                         -1, false);
+-                return true;
+-            }
+-            break;
++            key_press_cursor_horizontal(keyval, modifiers);
++            return true;
+         case Gdk.Key.Down:
+-            if (m_candidate_panel_is_visible)
+-                candidate_panel_cursor_down();
+-            else
+-                category_list_cursor_move(Gdk.Key.Down);
++            key_press_cursor_vertical(keyval);
+             return true;
+         case Gdk.Key.Up:
+-            if (m_candidate_panel_is_visible)
+-                candidate_panel_cursor_up();
+-            else
+-                category_list_cursor_move(Gdk.Key.Up);
++            key_press_cursor_vertical(keyval);
+             return true;
+         case Gdk.Key.Page_Down:
+             if (m_candidate_panel_is_visible) {
+-                m_enter_notify_enable = false;
++                enter_notify_disable_with_timer();
+                 m_lookup_table.page_down();
+                 show_candidate_panel();
+                 return true;
+@@ -1184,33 +1277,59 @@ class IBusEmojier : Gtk.Window {
+             break;
+         case Gdk.Key.Page_Up:
+             if (m_candidate_panel_is_visible) {
+-                m_enter_notify_enable = false;
++                enter_notify_disable_with_timer();
+                 m_lookup_table.page_up();
+                 show_candidate_panel();
+                 return true;
+             }
+             break;
+         case Gdk.Key.Home:
+-            if (m_entry.get_text().len() > 0) {
+-                GLib.Signal.emit_by_name(m_entry, "move-cursor",
+-                                         Gtk.MovementStep.DISPLAY_LINE_ENDS,
+-                                         -1, false);
++            if (key_press_cursor_home_end(keyval, modifiers))
+                 return true;
+-            }
+             break;
+         case Gdk.Key.End:
+-            if (m_entry.get_text().len() > 0) {
+-                GLib.Signal.emit_by_name(m_entry, "move-cursor",
+-                                         Gtk.MovementStep.DISPLAY_LINE_ENDS,
+-                                         1, false);
++            if (key_press_cursor_home_end(keyval, modifiers))
+                 return true;
+-            }
+-            break;
+-        default:
+-            entry_enter_keyval(keyval);
+             break;
+         }
+ 
++        if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) {
++            switch (keyval) {
++            case Gdk.Key.f:
++                key_press_cursor_horizontal(Gdk.Key.Right, modifiers);
++                return true;
++            case Gdk.Key.b:
++                key_press_cursor_horizontal(Gdk.Key.Left, modifiers);
++                return true;
++            case Gdk.Key.n:
++                key_press_cursor_vertical(Gdk.Key.Down);
++                return true;
++            case Gdk.Key.p:
++                key_press_cursor_vertical(Gdk.Key.Up);
++                return true;
++            case Gdk.Key.h:
++                if (key_press_cursor_home_end(Gdk.Key.Home, modifiers))
++                    return true;
++                break;
++            case Gdk.Key.e:
++                if (key_press_cursor_home_end(Gdk.Key.End, modifiers))
++                    return true;
++                break;
++            case Gdk.Key.u:
++                if (key_press_cursor_escape())
++                    return true;
++                break;
++            case Gdk.Key.a:
++                if (m_entry.get_text().len() > 0) {
++                    m_entry.select_region(0, -1);
++                    return true;
++                }
++                break;
++            }
++            return false;
++        }
++
++        entry_enter_keyval(keyval);
+         return true;
+     }
+ 
+-- 
+2.9.3
+

diff --git a/ibus.spec b/ibus.spec
index 3166733..ca0f66b 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -28,7 +28,7 @@
 
 Name:           ibus
 Version:        1.5.15
-Release:        3%{?dist}
+Release:        4%{?dist}
 Summary:        Intelligent Input Bus for Linux OS
 License:        LGPLv2+
 Group:          System Environment/Libraries
@@ -426,6 +426,10 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || :
 %{_datadir}/gtk-doc/html/*
 
 %changelog
+* Wed Mar 15 2017 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.15-4
+- Implemented Ctrl-[f|b|n|p|h|e|a|u] for cursor operations on emoji dialog
+- Added XSetIOErrorHandler() for GNOME3 desktop
+
 * Mon Mar 13 2017 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.15-3
 - Emoji dialog enhancements and bug fixes
   Fixed ibus_emoji_dict_load() API.

                 reply	other threads:[~2026-05-31  2:06 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178019319064.1.16712814926975530344.rpms-ibus-6a3b67701391@fedoraproject.org \
    --to=tfujiwar@redhat.com \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox