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

可紊 提交于 2020-01-04 07:06:09

问题


FWIW, the exact program motivating this question is (on Linux/Debian/Sid/x86-64) my bismon on github, commit d43a75fb9f8e13. GTK3 is 3.22.24. If you need to try it, build it with make and run ./bismon. It is in alpha stage, and still not interesting to others than me. It is some kind of DSL interpreter with a GTK interface and a persistent heap.

If you want something to appear, click to focus on the middle widget, type the_system there followed by Ctrl Return, but that is not relevant for this question.

I am successfully able, with commandview_BM (it is the middle widget) being a GtkTextView widget

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

to handle some keyboard events (FWIW, my physical keyboard is an AZERTY layout Logitech G610)

I would like to process function keys in my programs. But some function keys (e.g. F7, F8, F9, F10 ...) are "preempted" by the desktop or the environment and don't reach my program. Others are reached, e.g. F6 gives my debugging output from that handlekeypresscmd_BM function:

gui_BM.c:3135: handlekeypresscmd_BM keyval 0xffc3 KEY_F6 

and that makes me happy (because later I would be able to code something useful). The relevant code looks like:

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 */

Obviously that DBGPRINTF_BM (some debug print macro) should be reached for F7 and not just for F6 but it is not (and I don't understand why). Of course I checked that my system's /usr/include/gtk-3.0/gdk/gdkkeysyms.h contains

#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

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

Is the list of predefined function keys (those already used by other things, perhaps window managers) accessible and documented? Where? Should I look in EWMH, in ICCCCM, in Gnome Human Interface, dive into every window manager's documentation?

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...

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)?

FWIW, xev sees all the function keys from F1 to F12

addenda

To partly answer my question, gtk/gtktextview.c (from source code of GTK3) has around line 1630:

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

(BTW, is there some way to disable that?)

but that doesn't explain the behavior of F8 and don't tell me how to query which function keys can be used.

My textview is inside some GtkPaned which has in gtk/gtkpaned.c line 635

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

so maybe I should try to find out how to query such bound keys. Still have no ideas. Should investigate GtkBindingSet.


回答1:


Trying to decompose your questions so that its easier to answer.

First of all, i would recommend reading The GTK+ Input and Event Handling Model and notice that "GTK+ receives events from the windowing system".

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

Well, you didn't but "distributions"/window managers do.

As an example, check GNOME Shell Keyboard shortcuts

Most window managers use ALT+Fn keys, being ALT+F4 most known for closing a window. CTRL key is more suitable for user "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...

No, there isn't. Events are emitted/triggered and widgets, if programmed to do so, receive and handle them accordingly, then by checking the event, like you do with your key-press-event handler, you can trigger "actions". Well, theoretically, you could develop a set of setters and getters of handled keys in your own widgets but honestly don't see the point of doing so.

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)?

I would go for CTRL+Fn, SHIFT+Fn and normal F1 to F12 leaving ALT+Fn reserved for the Windowing system.

Now looking to your concrete example on Github, your handler is already ignoring F11 and F12 due to the condition:

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

adapting your code with an initial print to the console:

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
}  

After running your application bismon and placing the focus on the GtkTreeView and pressing the several Fn keys and CTRL+Fn keys, the result is:

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

As you can see, F10 was not shown and that is because when GtkMenus are used, F10 is reserved to trigger them (Notice that on GNOME 3, F10 is proposed for Gear Menu although not hardcoded as is in GtkMenuShell). SHIFT+F10 is also used as a ContextMenu keyboard shortcut.

I've also noticed that on your application F6 and SHIFT+F6 will move focus to the widget below the GtkTreeView, not sure if you coded this yourself but couldn't find any reference to it in your code (more below on another example). F8 also does something similar but if after every key press you force the focus on the GtkTreeView (with left mouse click over it) then you will get all key presses. CTRL+Fn works for all keys (there you have your solution).

As a final example, using a simple, adapted, Treeview example from zetcode you can verify that all functions keys, except those using ALT and SHIFT+F10(ContextMenu), work:

#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;
}

And the result is (showing the context menu!):

Also notice that the application does not have a menu, so F10 is handled without problem.

EDIT: Check this Wikipedia entry on shortcuts



来源:https://stackoverflow.com/questions/46809878/gtk3-function-keys-which-are-consumed-and-unusable-from-my-gtk-app

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!