GTK3 功能键;哪些被消耗了(并且无法从我的 GTK 应用程序中使用)?

GTK3 function keys; which are consumed (and unusable from my GTK app)?

FWIW,引发这个问题的确切程序是(在 Linux/Debian/Sid/x86-64)我的 bismon on github, commit d43a75fb9f8e13。 GTK3 是 3.22.24。如果您需要尝试,请使用 make 和 运行 ./bismon 构建它。它处于 alpha 阶段,除了我之外,其他人仍然没有兴趣。它是某种具有 GTK 接口和持久堆的 DSL 解释器。

如果您想要显示某些内容,请单击以关注中间的小部件,键入 the_system 然后按 Ctrl Return ,但这与这个问题无关。

我成功了,with commandview_BM(它是中间的小部件)是一个 GtkTextView 小部件

g_signal_connect (commandview_BM, "key-press-event",
                G_CALLBACK (handlekeypresscmd_BM), NULL);

处理一些键盘事件(FWIW,我的物理键盘是 AZERTY 布局罗技 G610)

我想在我的程序中处理功能键。但是一些功能键(例如F7F8F9F10 ...) 是 "preempted" 由桌面或环境和不到达我的程序。达到其他人,例如F6 从那个 handlekeypresscmd_BM 函数得到我的 debugging 输出:

gui_BM.c:3135: handlekeypresscmd_BM keyval 0xffc3 KEY_F6 

这让我很高兴(因为以后我可以编写一些有用的代码)。相关代码如下:

gboolean
handlekeypresscmd_BM (GtkWidget * widg, GdkEventKey * evk, gpointer data)
{
  assert (GTK_IS_TEXT_VIEW (widg));
  assert (evk != NULL);
  assert (data == NULL);
  if (evk->keyval == GDK_KEY_Return) { /* do something unrelated */ 
    return true; }
  else if (evk->keyval == GDK_KEY_Tab)
    {
      tabautocompletecmd_BM ();
      return true;
    }
  else if (evk->keyval >= GDK_KEY_F1 && evk->keyval <= GDK_KEY_F10)
    {
      GdkModifierType modmask = gtk_accelerator_get_default_mod_mask ();
      bool withctrl = (evk->state & modmask) == GDK_CONTROL_MASK;
      bool withshift = (evk->state & modmask) == GDK_SHIFT_MASK;
      DBGPRINTF_BM ("handlekeypresscmd_BM keyval %#x KEY_F%d %s%s",
                    evk->keyval, evk->keyval - (GDK_KEY_F1 - 1),
                    withctrl ? " ctrl" : "", withshift ? " shift" : "");
      return false;
  };
  return false;                 // propagate the event
}                               /* end handlekeypresscmd_BM */

显然 DBGPRINTF_BM(一些调试打印宏)应该达到 F7 而不仅仅是 F6 但它不是(我不明白为什么)。当然我检查过我系统的 /usr/include/gtk-3.0/gdk/gdkkeysyms.h 包含

#define GDK_KEY_F5 0xffc2
#define GDK_KEY_F6 0xffc3
#define GDK_KEY_F7 0xffc4
#define GDK_KEY_F8 0xffc5
#define GDK_KEY_F9 0xffc6

我使用 Gnome、XFCE4 或 LXDE 作为我的桌面环境。据我所知,我没有专门针对功能键配置这些。

是否可以访问并记录预定义功能键列表(那些已经被其他东西使用,也许 window 管理器)?在哪里?我是否应该查看 EWMH、ICCCCM、Gnome 人机界面,深入研究每个 window 经理的文档?

更重要的是,有没有一种方法可以从 GTK3 应用程序以编程方式查询某些小部件将达到哪些功能键(像 GtkTextView? 我想不出任何...

否则,我可以合理地期望从 Linux 上的 Gtk3 应用程序(使用 X11,也许稍后使用 Wayland)上使用的一小组功能键是什么?

FWIW, xev 看到从 F1F12

的所有功能键

附录

为了部分回答我的问题,gtk/gtktextview.c(来自 GTK3 的源代码)大约在第 1630 行:

  /* Caret mode */
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_F7, 0,
                "toggle-cursor-visible", 0);

(顺便说一句,有什么方法可以禁用它吗?)

但这并不能解释 F8 的行为,也不要告诉我如何查询可以使用哪些功能键。

我的文本视图在一些 GtkPaned 里面,在 gtk/gtkpaned.c 第 635 行

  gtk_binding_entry_add_signal (binding_set,
                GDK_KEY_F8, 0,
                "cycle-handle-focus", 1,
                G_TYPE_BOOLEAN, FALSE);

所以也许我应该尝试找出如何查询此类绑定键。仍然没有想法。应该调查 GtkBindingSet.

正在尝试分解您的问题以便更容易回答。

首先,我建议阅读 The GTK+ Input and Event Handling Model 并注意“GTK+ 从 windowing 系统 接收事件”。

I'm using either Gnome, or XFCE4, or LXDE as my desktop environments. AFAIK I did not configure these specially regarding function keys.

嗯,你没有,但 "distributions"/window 经理有。

例如,检查 GNOME Shell Keyboard shortcuts

大多数 window 经理使用 ALT+Fn 键,ALT+F4 最出名的是关闭 window. CTRL键更适合用户"shortcuts/accelerators".

More importantly, is there a way, from a GTK3 application, to programmatically query which function keys will be reached by some widget (like a GtkTextView) ? I can't think of any...

不,没有。事件是 emitted/triggered 和小部件,如果被编程为这样做,相应地接收和处理它们,然后通过检查事件,就像您对 key-press-event 处理程序所做的那样,您可以触发 "actions"。好吧,理论上,您可以在自己的小部件中开发一组已处理键的设置器和获取器,但老实说,您看不到这样做的意义。

Or else, what is the small set of function keys that I reasonably can expect to use from a Gtk3 application on Linux (with X11, and perhaps later with Wayland)?

我会选择 CTRL+FnSHIFT+Fn 和正常的 F1 F12 保留 ALT+Fn 为窗口系统保留。

现在查看 Github 上的具体示例,您的处理程序已经忽略了 F11F12 由于条件:

...
... if (evk->keyval >= GDK_KEY_F1 && evk->keyval <= GDK_KEY_F10)
...

将您的代码与控制台的初始打印相匹配:

gboolean
handlekeypresscmd_BM (GtkWidget * widg, GdkEventKey * evk, gpointer data)
{
  assert (GTK_IS_TEXT_VIEW (widg));
  assert (evk != NULL);
  assert (data == NULL);

  if (evk->keyval >= GDK_KEY_F1 && evk->keyval <= GDK_KEY_F12) {
     g_print ("handlekeypresscmd_BM KEY_F%d\n", evk->keyval - (GDK_KEY_F1 - 1));
  }
  // see <gdk/gdkkeysyms.h> for names of keysyms
  if (evk->keyval == GDK_KEY_Return)
    {
      GdkModifierType modmask = gtk_accelerator_get_default_mod_mask ();
      bool withctrl = (evk->state & modmask) == GDK_CONTROL_MASK;
      bool withshift = (evk->state & modmask) == GDK_SHIFT_MASK;
      if (withctrl)
        run_then_erase_command_BM ();
      else if (withshift)
        run_then_keep_command_BM ();
      else                      // plain RETURN key, propagate it
        return false;
      return true;
    }
  else if (evk->keyval == GDK_KEY_Tab)
    {
      tabautocompletecmd_BM ();
      return true;
    }
  else if (evk->keyval >= GDK_KEY_F1 && evk->keyval <= GDK_KEY_F10)
    {
      GdkModifierType modmask = gtk_accelerator_get_default_mod_mask ();
      bool withctrl = (evk->state & modmask) == GDK_CONTROL_MASK;
      bool withshift = (evk->state & modmask) == GDK_SHIFT_MASK;
      DBGPRINTF_BM ("handlekeypresscmd_BM keyval %#x KEY_F%d %s%s",
                    evk->keyval, evk->keyval - (GDK_KEY_F1 - 1),
                    withctrl ? " ctrl" : "", withshift ? " shift" : "");
      return false;
    }
  return false;                 // propagate the event
}  

在 运行 您的应用程序 bismon 之后,将焦点放在 GtkTreeView 上并按下几个 Fn 键和 CTRL+Fn 键,结果为:

handlekeypresscmd_BM KEY_F1
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffbe KEY_F1 
handlekeypresscmd_BM KEY_F2
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffbf KEY_F2 
handlekeypresscmd_BM KEY_F3
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc0 KEY_F3 
handlekeypresscmd_BM KEY_F4
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc1 KEY_F4 
handlekeypresscmd_BM KEY_F5
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc2 KEY_F5 
handlekeypresscmd_BM KEY_F6
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc3 KEY_F6 
handlekeypresscmd_BM KEY_F7
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc4 KEY_F7 
handlekeypresscmd_BM KEY_F8
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc5 KEY_F8 
handlekeypresscmd_BM KEY_F9
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc6 KEY_F9 
handlekeypresscmd_BM KEY_F11
handlekeypresscmd_BM KEY_F12
handlekeypresscmd_BM KEY_F1
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffbe KEY_F1  ctrl
handlekeypresscmd_BM KEY_F2
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffbf KEY_F2  ctrl
handlekeypresscmd_BM KEY_F3
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc0 KEY_F3  ctrl
handlekeypresscmd_BM KEY_F4
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc1 KEY_F4  ctrl
handlekeypresscmd_BM KEY_F5
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc2 KEY_F5  ctrl
handlekeypresscmd_BM KEY_F6
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc3 KEY_F6  ctrl
handlekeypresscmd_BM KEY_F7
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc4 KEY_F7  ctrl
handlekeypresscmd_BM KEY_F8
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc5 KEY_F8  ctrl
handlekeypresscmd_BM KEY_F9
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc6 KEY_F9  ctrl
handlekeypresscmd_BM KEY_F10
gui_BM.c:3138: handlekeypresscmd_BM keyval 0xffc7 KEY_F10  ctrl
handlekeypresscmd_BM KEY_F11
handlekeypresscmd_BM KEY_F12

如您所见,F10 没有显示,那是因为当使用 GtkMenus 时,F10 被保留来触发它们(注意在 GNOME 3 上,F10 被提议用于齿轮菜单虽然没有像 GtkMenuShell 那样硬编码)。 SHIFT+F10 也用作上下文菜单键盘快捷键。

我还注意到在您的应用程序中 F6SHIFT+F6 会将焦点移动到 GtkTreeView 下面的小部件,而不是确定您是否自己编写了此代码,但在您的代码中找不到对它的任何引用(下面的另一个示例中有更多内容)。 F8 也做了类似的事情,但如果在每次按键后你强制将焦点放在 GtkTreeView 上(用鼠标左键单击它),那么你将获得所有按键。 CTRL+Fn 适用于所有键(您有自己的解决方案)。

作为最后一个示例,使用来自 zetcode 的简单、改编的 Treeview 示例,您可以验证所有功能键,使用 ALT 和 [=60 的功能键除外=]SHIFT+F10(上下文菜单),工作:

#include <gtk/gtk.h>

gboolean
keyHandler (GtkWidget *widget,
               GdkEventKey  *event,
               gpointer   user_data) {
   if (event->keyval >= GDK_KEY_F1 && event->keyval <= GDK_KEY_F12) {
      GdkModifierType modmask = gtk_accelerator_get_default_mod_mask ();
      gboolean ctrl = (event->state & modmask) == GDK_CONTROL_MASK;
      gboolean shift = (event->state & modmask) == GDK_SHIFT_MASK;
      g_print ("HANDLED %s%sKEY_F%d\n", ctrl ? "CTRL+" : "", shift ? "SHIFT+" : "", event->keyval - (GDK_KEY_F1 - 1));
   }

   return FALSE;
}

int main(int argc, char *argv[]) {

  GtkWidget *window;
  GtkWidget *view;
  GtkWidget *vbox;

  GtkTextBuffer *buffer;
  GtkTextIter start, end;
  GtkTextIter iter;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
  gtk_window_set_title(GTK_WINDOW(window), "GtkTextView");

  vbox = gtk_vbox_new(FALSE, 0);
  view = gtk_text_view_new();
  gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

  gtk_text_buffer_create_tag(buffer, "gap",
        "pixels_above_lines", 30, NULL);

  gtk_text_buffer_create_tag(buffer, "lmarg", 
      "left_margin", 5, NULL);
  gtk_text_buffer_create_tag(buffer, "blue_fg", 
      "foreground", "blue", NULL); 
  gtk_text_buffer_create_tag(buffer, "gray_bg", 
      "background", "gray", NULL); 
  gtk_text_buffer_create_tag(buffer, "italic", 
      "style", PANGO_STYLE_ITALIC, NULL);
  gtk_text_buffer_create_tag(buffer, "bold", 
      "weight", PANGO_WEIGHT_BOLD, NULL);

  gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);

  gtk_text_buffer_insert(buffer, &iter, "Plain text\n", -1);
  gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, 
        "Colored Text\n", -1, "blue_fg", "lmarg",  NULL);
  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, 
        "Text with colored background\n", -1, "lmarg", "gray_bg", NULL);

  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, 
        "Text in italics\n", -1, "italic", "lmarg",  NULL);

  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, 
        "Bold text\n", -1, "bold", "lmarg",  NULL);

  gtk_container_add(GTK_CONTAINER(window), vbox);

  g_signal_connect(G_OBJECT(view), "key-press-event", G_CALLBACK(keyHandler), NULL);

  g_signal_connect(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

结果是(显示上下文菜单!):

另请注意,应用程序 没有菜单 ,因此 F10 处理没有问题。

编辑:在快捷方式

上检查这个Wikipedia entry