Thread: Capturing keyboard scancodes....

  1. #1
    Registered User
    Join Date
    Mar 2014
    Location
    Corning, New York, USA
    Posts
    96

    Capturing keyboard scancodes....

    Hi.

    What's a good way to capture keyboard scan codes? Something that guarantees something doesn't grab it before it hits my program?

    For example, let's take a look at F1. Lets just say I want to write a program where when the user hits the F1 key, my program says Hello, you hit F1.

    This is what I've tried:
    Code:
    #include <stdio.h>
    #include <ncurses/curses.h>
    
    int main() {
            int x = 0;
    
            initscr();              /* Start curses mode    */
            raw();                  /* Line buffering disabled, raw mode so the user */
                                      /* can't CTRL-D or CTRL-Z */
            echo();                 /* Turn echo on while we getch() */
            keypad(stdscr, TRUE);   /* Give us control over */
                                                /* keys like up, down, F1, F2, etc. */
    
            timeout(-1);    /* Set to -1 so we have a blocking */
                                /* read (ie, waits indefinitely for input */
    
            printw(" Please type ? and hit enter for a list of available commands.\n\n");
            refresh();
    
            while(x!='q' && x!='Q' && x!=KEY_F(1)) {
                   printw("Franklin-WPH3497: # ");
                    x = getch();
                    if(x==KEY_F(2)) {
                            printw("\nYou entered: F2.\n");
                    } else {
                            printw("\nYou entered: %c.\n",x);
                    }
                    refresh();
                    if(x=='?') {
                            printw("? -- display this help.\n");
                            printw("q -- quit this shell.\n");
                            printw("F1 -- quit.\n");
                            refresh();
                    }
            }
    
            endwin();
    
            return 0;
    }
    I know I shouldn't be posting the whole program but I thought perhaps there's something that I'm missing some where’s. I'm using ncurses for this but I'm okay with using something else, so long as it's fairly portable. I'd like to stay away from termio / termios stuff because I don't really have much of a way to compile that kind stuff in Windows.

    If I hit F2, my program says Hey, you hit F2. if I hit F1, my terminal program's Help menu pops up. If I run it from a pure console mode, hitting F1 exits the program as expected.

    Is there any way to capture that F1 and not let my terminal program steal it? Perhaps some really really old, long forgotten way to read scancodes or something? Maybe something that deals with the BIOS or something? Any thoughts?

    Thanks.

  2. #2
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    I'm not sure what you mean. Are you talking about some kind of deamon/resident-app that takes over the keyboard for the whole system? If so, it's not advisable to attempt such a thing.
    "Talk is cheap, show me the code" - Linus Torvalds

  3. #3
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by Spork Schivago View Post
    Is there any way to capture that F1 and not let my terminal program steal it?
    Use a different terminal?

    You see, the problem is that your program receives all keystrokes and other events *from* the terminal, and the terminal from the desktop environment. The terminal doesn't "steal" them, it just does not forward them to your program.

    Normally, we just avoid keys typically used by the desktop environment.

    The only portable solution, really, is to let the user override the key codes. In Linux, *BSD, and Unix systems it is common to store those as a text file in $HOME/.yourprogram.rc or $HOME/.yourprogram/keys . In Windows, there's a similar directory elsewhere (don't know the exact recommended one offhand, but it was something about Application Settings). Typically the format is human readable, something like
    Code:
    KEY_Menu = F1
    KEY_Quit = Ctrl-Q
    KEY_Load = Ctrl-O
    KEY_Copy = Ctrl-C
    KEY_Cut = Ctrl-X
    KEY_Paste = Ctrl-V
    Of course, parsing these files is a bit of a pain in the butt, since you have to do it yourself -- it's not at all complicated, just a lot of code (if clauses or arrays with Curses key constants). Fortunately, the Curses constants (like F(1) ) won't change while the program is running, so you only need to do the parsing once, at the beginning of your program.

    Many Unix/Linux/BSD programs support multiple such configuration files; one for distribution defaults (say, /usr/share/yourprog/keys), one for system-specific overrides (say, /etc/yourprog/keys), and one for the user-specific overrides (say, $HOME/.yourprog/keys), read in this order, with missing files ignored. This is because Unix/Linux/BSDs are inherently multi-user systems. One solution is to have an array of locations tried, and just populate it differently on different architectures.

    To expand the environment variable references in config file paths, UNIX/Linux/BSD systems often use POSIX.1 wordexp() function to generate the exact paths to the configuration files. The Windows ExpandEnvironmentStrings() function seems reasonably similar, so you can wrap the differences around #if defined(_WIN32) || defined(_WIN64) ... #else ... #endif. There is a Wiki on predefined macros per operating system, if you need other OS-specific quirks.
    Last edited by Nominal Animal; 08-22-2015 at 01:58 PM.

  4. #4
    Registered User
    Join Date
    Mar 2014
    Location
    Corning, New York, USA
    Posts
    96
    Quote Originally Posted by Nominal Animal View Post
    Use a different terminal?

    You see, the problem is that your program receives all keystrokes and other events *from* the terminal, and the terminal from the desktop environment. The terminal doesn't "steal" them, it just does not forward them to your program.

    Normally, we just avoid keys typically used by the desktop environment.

    The only portable solution, really, is to let the user override the key codes. In Linux, *BSD, and Unix systems it is common to store those as a text file in $HOME/.yourprogram.rc or $HOME/.yourprogram/keys . In Windows, there's a similar directory elsewhere (don't know the exact recommended one offhand, but it was something about Application Settings). Typically the format is human readable, something like
    Code:
    KEY_Menu = F1
    KEY_Quit = Ctrl-Q
    KEY_Load = Ctrl-O
    KEY_Copy = Ctrl-C
    KEY_Cut = Ctrl-X
    KEY_Paste = Ctrl-V
    Of course, parsing these files is a bit of a pain in the butt, since you have to do it yourself -- it's not at all complicated, just a lot of code (if clauses or arrays with Curses key constants). Fortunately, the Curses constants (like F(1) ) won't change while the program is running, so you only need to do the parsing once, at the beginning of your program.

    Many Unix/Linux/BSD programs support multiple such configuration files; one for distribution defaults (say, /usr/share/yourprog/keys), one for system-specific overrides (say, /etc/yourprog/keys), and one for the user-specific overrides (say, $HOME/.yourprog/keys), read in this order, with missing files ignored. This is because Unix/Linux/BSDs are inherently multi-user systems. One solution is to have an array of locations tried, and just populate it differently on different architectures.

    To expand the environment variable references in config file paths, UNIX/Linux/BSD systems often use POSIX.1 wordexp() function to generate the exact paths to the configuration files. The Windows ExpandEnvironmentStrings() function seems reasonably similar, so you can wrap the differences around #if defined(_WIN32) || defined(_WIN64) ... #else ... #endif. There is a Wiki on predefined macros per operating system, if you need other OS-specific quirks.
    Thank you. I guess I wasn't 100% clear on my question, although you just about answered it. Without changing terminal programs, is there away to capture the F1 before the terminal program captures it? I'm okay with using #ifdef's because I'm sure this is OS independent. So in Linux, do you know of a way? Essentailly, I'd like my program to receive the scancodes from the desktop environment, instead of from the terminal program...Would this be something I'd need to use the GTK+ or Qt libraries for?

  5. #5
    Registered User
    Join Date
    Mar 2014
    Location
    Corning, New York, USA
    Posts
    96
    Quote Originally Posted by migf1 View Post
    I'm not sure what you mean. Are you talking about some kind of deamon/resident-app that takes over the keyboard for the whole system? If so, it's not advisable to attempt such a thing.
    Sorry for the confusion. Nominal Animal had it right. I run my program in a terminal shell. When I press F1, I believe the Desktop environment gets the code first, seeing that I pressed F1, it sends it to the terminal program, and the terminal program opens up a help, instead of passing it to my program. Without my program running, in my terminal window, if I hit F1, help pops open. If I hit F2, nothing happens. With my program running, if I hit F1, help pops open. if I hit F2, my program says, hey, you hit F2. The terminal program never passes F1 to my program. I want to know if there's a way to intercept it before it hits the terminal program. This might be the wrong place to ask such a question. I wasn't really thinking clearly last night, but it's obvious this is OS specific and not C specific. I guess it would be more or less for a Linux C programming forum. I was hoping someone would have the experience and would be able to help. If not, I can try to find a more fitting forum.

    In Windows, I don't think I'll have such problems.

  6. #6
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by Spork Schivago View Post
    Without changing terminal programs, is there away to capture the F1 before the terminal program captures it?
    Nothing portable as far as I know. (Some terminals might allow it somehow, but it's rare and not portable.)

    Quote Originally Posted by Spork Schivago View Post
    So in Linux, do you know of a way?
    I don't think there is. (For one, there are too many terminals in use.)

    Of course, in Linux you can always access the input subsystem directly, for example dedicate a specific keyboard or mouse for your program, but that's not what you are asking I think.

    Quote Originally Posted by Spork Schivago View Post
    Would this be something I'd need to use the GTK+ or Qt libraries for?
    Something like those, yes. That way your program is a first-class citizen of the desktop environment, and receives the events like keypresses directly from the desktop environment, instead of an intermediate program (terminal or similar).

    Quote Originally Posted by Spork Schivago View Post
    I wasn't really thinking clearly last night, but it's obvious this is OS specific
    Nope, it happens on all OSes. The keys vary a bit, though. For both local console/Curses applications, as well as running console/Curses applications remotely, using an SSH or Telnet client (like PuTTY for example).

    I'm not exactly sure which keys the standard Windows command prompt client (terminal) grabs, but it definitely does not forward them all to your application. So, the exact same problem awaits you there, only possibly with different keys.

    If you prefer programming in C over C++, I do recommend you get into GTK+. It is portable (see downloads; for Linux, use your package manager and packages your distribution provides, and for compiling new programs, remember to install the development packages too, like libgtk-3-dev in Debian and Ubuntu).

    Want an example? If you have a Windows machine you'd like to try and compile and run it, I'd be very interested in working it out. It is portable, I just personally don't have Windows, but would like to have a better recipe to give on how to do it, instead of just telling others it is possible.

  7. #7
    Registered User
    Join Date
    Mar 2014
    Location
    Corning, New York, USA
    Posts
    96
    I definitely have a Windows box and would love trying it out. When I said it was OS specific, I didn't mean how it worked, I meant the code on how to read the key scancodes. I thought to read them in Linux would be totally different than reading them in Windows. I have setup GTK+ and MinGW-W64 (and 32) on my Windows machine (and my Linux box so I can cross-compile). I also have the GNU C compiler, gcc, on my Linux box. I don't know C++ anymore, hence the reason I'd rather stick with C.

    The GTK+ Windows example that I run after installing and configuring GTK+ seems to act a little weird. Like perhaps it's not the best choice. Scrolling seems to be off, it's not very smooth, it appears choppy. I dunno if that's just something with an old example or if it's something with the GTK+ library. It's odd because the 32-bit and the 64-bit version of GTK+'s application have the same issue.

    Just so I understand this correctly,
    there is a way to do what I want, using GTK+. I can execute my application from a terminal shell in X (not an actual console shell), and when I hit something like F1, my application will respond instead of the terminal shell opening help? Even though my application is being started through the terminal shell, because it's using the GTK+ library, it'd have precedence over the terminal shell? I'd love an example if you don't mind!

  8. #8
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    Quote Originally Posted by Spork Schivago View Post
    ...
    The GTK+ Windows example that I run after installing and configuring GTK+ seems to act a little weird. Like perhaps it's not the best choice. Scrolling seems to be off, it's not very smooth, it appears choppy. I dunno if that's just something with an old example or if it's something with the GTK+ library. It's odd because the 32-bit and the 64-bit version of GTK+'s application have the same issue.
    Cannot speak for GTK+3 (haven't tested it enough) but GTK+2 works just fine on Windows. It's not that snappy as the native Win32 API of course, but I haven't had any speed issues so far. Perhaps it's something specific to your example, or the Windows version of GTK+3, or your setup. Btw, the Windows binaries provided on the GTK+ site, are build with plain mingw. You may try compiling GTK+ from sources with mingw-w64 (or search for such binaries compiled by someone else). Or if your project can afford it, use plain mingw.

    Keep also in mind that the 64-bit version for Windows is officially flagged as experimental in the GTK+ site.

    ...I'd love an example if you don't mind!
    Following is an example of handling "key_release_event"s with GTK+2. The function: on_key_release_event() is the signal handler, while the function: mygtk2_key_event_print() prints on the console everything related to the key-event once any key is released (this function interests you the most).

    Try keypresses with several combos. For example, along with a mouse-button, Ctrl, Alt, etc and see the output on the console.

    The following code has been tested (by me) on Windows 8.1 64-bit, with plain mingw (gcc v4.8.1) and the official all-in-one-bundle 32-bit GTK+2 binaries.

    Those mygtk2_/MYGTK2_ stuff are just excerpts from a mini GTK+2 interface I had made when I was dealing with GTK+2 in the past.

    Perhaps the following code is a bit more complicated than what you would expect, but I consider it fairly complete and re-usable (check it for any bugs though).

    You should also check the documentation of GdkEventKey so the code of the function: mygtk2_key_event_print() becomes more obvious.

    Code:
    #include <gtk/gtk.h>
    #include <gdk/gdkkeysyms.h>   /* all keyboard symbols */
    //#include <glib.h>
    #include <glib/gprintf.h>
    #include <glib/gi18n.h>       /* for internationalization with gettext() */
    
    #ifdef MYGTK2_NODEBUG
    gboolean global_mygtk2debug = FALSE;
    #else
    gboolean global_mygtk2debug = TRUE;
    #endif
    
    /**
     * Mostly used for providing a buffer-size for g_snprintf
     */
    #define MYGTK2_BUFSIZ  4096
    
    /**
     * Macro to validate key events (pressed or released)
     */
    #define MYGTK2_VALID_GDK_EVENT_KEY( ev )    \
    (                                           \
        GDK_KEY_PRESS == (ev)->type         \
        || GDK_KEY_RELEASE == (ev)->type    \
    )
    
    /**
     * Macro displaying an error-message with debugging info within a GUI alert-box
     * (this is a convenient wrapper of the custom function mygtk2_alert_box()).
     *
     * @param wowner The owner-window of the alert-box (may be NULL).
     * @param errmsg The message to be displayed.
     */
    #define MYGTK2_DBG_ERRMSG( wowner, errmsg )                     \
    do {                                                            \
        gchar msgout[MYGTK2_BUFSIZ] = {'\0'};                   \
        if ( !global_mygtk2debug )                              \
            break;                                          \
        g_snprintf(                                             \
            msgout,                                         \
            MYGTK2_BUFSIZ,                                  \
            "File : %s\nFunc: %s\nLine: %d\n\n%s",          \
            __FILE__, __func__,  __LINE__,                  \
            (const gchar *)(errmsg) ? errmsg : "\0"         \
            );                                              \
        mygtk2_alert_box( GTK_WIDGET(wowner), NULL, msgout );   \
    } while(0)
    
    /*************************************************//**
     * Display the specified message inside a simple modal dialog.
     *
     * The owner of the dialog is the window specified in
     * the first argument, which usually is the main window
     * of the application (if passed as NULL, the alert-box
     * will not have an owner, which is fine but a bit odd).
     *****************************************************
     */
    void mygtk2_alert_box(
        GtkWidget   *wowner,
        const gchar *title,
        const gchar *message
        )
    {
        GtkWidget *w = NULL;   /* the alert-box window */
    
        if ( wowner ) {
            gtk_widget_set_sensitive( wowner, FALSE );
        }
    
        w = gtk_message_dialog_new(
            GTK_WINDOW(wowner),
            GTK_DIALOG_DESTROY_WITH_PARENT,
            GTK_MESSAGE_INFO,
            GTK_BUTTONS_OK,
            "%s", message
            );
    
        gtk_window_set_position( GTK_WINDOW(w), GTK_WIN_POS_CENTER_ALWAYS );
        gtk_window_set_title(
            GTK_WINDOW( w ),
            NULL == title ? _("Alert Box") : title
            );
        gtk_dialog_run( GTK_DIALOG(w) );
        gtk_widget_destroy( w );
    
        if ( NULL != wowner ) {
            gtk_widget_set_sensitive( wowner, TRUE );
        }
    }
    
    /****************************************************
     *
     ****************************************************
     */
    gboolean mygtk2_key_event_print(
        const GdkEventKey *event
        )
    {
        if ( NULL == event ) {
            MYGTK2_DBG_ERRMSG( NULL, "NULL pointer argument (event)" );
            return FALSE;
        }
    
        if ( !MYGTK2_VALID_GDK_EVENT_KEY(event) ) {
            MYGTK2_DBG_ERRMSG( NULL, "Not a valid GdkEventKey (event)" );
            return FALSE;
        }
    
        /* gdk-window */
        GdkWindow *window = event->window;
        g_print( "GdkWindow @ %p\n", NULL == window ? 0x0 : (void *)window );
    
        /* send_event */
        gint8 send_event = event->send_event;
        g_print( "Explicitly sent ? %s\n", send_event ? "Yes" : "No" );
    
        /* time */
        guint32 time = event->time;
        g_print( "Time            : %lu\n", (long unsigned) time );
    
        /* keyval */
        guint keyval = event->keyval;
        gchar *keyname = gdk_keyval_name( keyval );
        g_print(
            "Keyval          : %u (%s)\n",
            keyval,
            keyname ? keyname : "<None>"
            );
    
        /* type */
        GdkEventType type = event->type;
        const gchar *txttype = (GDK_KEY_PRESS == type) ? "Pressed" : "Released";
        g_print( "Type (%d)        : %s\n", type, txttype );
    
        /* string (deprecated) */
        gchar *string = event->string;
        g_print(
            "String          : <%s>\n",
            0 == event->length ? "None" : string
            );
        
        /* hardware_keycode */
        guint16 hardware_keycode = event->hardware_keycode;
        g_print( "Hardware code   : %u\n", hardware_keycode );
    
        /* is_modifier */
        guint is_modifier = event->is_modifier;
        g_print(
            "Is hardware-code a modifier? %s\n",
            is_modifier ? "Yes" : "No"
            );
    
        /* group */
        guint8 group = event->group;
        g_print( "Keyboard group  : %u\n", group );
    
        /* state of modifier keys & mouse-buttons */
        guint state = event->state;
        g_print(
            "CapsLock: %s\n",
            GDK_LOCK_MASK & state ? "On" : "Off"
            );
        g_print(
            "Shift   : %spressed\n",
            GDK_SHIFT_MASK & state ? "\0" : "not "
            );
        g_print(
            "Control : %spressed\n",
            GDK_CONTROL_MASK & state ? "\0" : "not "
            );
        g_print(
            "Alt     : %spressed\n",
            GDK_MOD1_MASK & state ? "\0" : "not "
            );
        g_print(
            "Meta    : %spressed\n",
            GDK_META_MASK & state ? "\0" : "not "
            );
        g_print(
            "1st mouse button: %spressed\n",
            GDK_BUTTON1_MASK & state ? "\0" : "not "
            );
        g_print(
            "2nd mouse button: %spressed\n",
            GDK_BUTTON2_MASK & state ? "\0" : "not "
            );
        g_print(
            "3rd mouse button: %spressed\n",
            GDK_BUTTON3_MASK & state ? "\0" : "not "
            );
    
        putchar( '\n' );
        return TRUE;
    }
    
    /****************************************************
     *
     ****************************************************
     */
    gboolean on_key_release_event(
        GtkWidget   *widget,
        GdkEventKey *event,
        gpointer    userdata
        )
    {
        gchar tmptxt[MYGTK2_BUFSIZ] = {'\0'};
        gchar *keyname = gdk_keyval_name( event->keyval );
    
        /* avoid compiler warning for unused params */
        (void)widget;
        (void)userdata;
    
        /* display on the console everything related to the key-release-event */
        mygtk2_key_event_print( event );
    
        /* display in an alert box the name of the released key */
        g_snprintf(
            tmptxt,
            sizeof( tmptxt ),
            "%s key was released",
            NULL == keyname ? "No" : keyname
            );
        mygtk2_alert_box( NULL, NULL, tmptxt );
    
        return FALSE; 
    }
    
    /****************************************************
     *
     ****************************************************
     */
    int main( int argc, char *argv[] )
    {
        GtkWidget *window = NULL;
    
        gtk_init (&argc, &argv );
    
        window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
        g_signal_connect(
            G_OBJECT( window ),
            "destroy",
            G_CALLBACK( gtk_main_quit ),
            NULL
            );
        g_signal_connect(
            G_OBJECT( window ),
            "key_release_event",
            G_CALLBACK( on_key_release_event ),
            NULL
            );
    
        gtk_widget_show_all( window );
        gtk_main();
    
        return 0;
    }
    "Talk is cheap, show me the code" - Linus Torvalds

  9. #9
    Registered User
    Join Date
    Mar 2014
    Location
    Corning, New York, USA
    Posts
    96
    Wow, this is perfect. I can use this in my code or no? Do you just want me to use it to study from and write my own code or can I copy this and modify it to work for my program?

    For the Windows GTK stuff, I've only noticed it with the sample program they have me run when I set up the GTK stuff. I have to run pkg-config, and then a few other odd commands, just once. At the end, it says run this command to make sure GTK+ works. I don't actually compile anything. Perhaps my code that I will write myself won't have this problem?

    This program you wrote is awesome! The code is very clean and easy to read. I noticed when I run it, I get an error. It compiles clean, I'm using GTK+3. When I run, I get:
    Code:
    Gtk-Message: GtkDialog mapped without a transient parent. This is discouraged.
    every time I do something. I believe this is because the GtkDialog doesn't have a parent to be modal to. The mouse buttons, on my machine at least, don't do anything. I click but I get no messages. I'd like to try and fix those two problems come tomorrow or the next day if you don't mind. When I run it, the program doesn't show up in my taskbar. This happens with YaST as well. But just system software. I've never had it happen to my knowledge with an actual user program program. There's only two or three programs on my system that have this issue. It'd be nice if I can find out why. I also have the same problem with the very simple Hello World GTK+ app I wrote. Do you know why? One quick question, with this

    Code:
    g_print(        "CapsLock: %s\n",
    
            GDK_LOCK_MASK & state ? "On" : "Off"
    
            );
    How does the GDK_LOCK_MASK & state ? "On" : "Off" work again?

    GDK_LOCK_MASK, we can say is (1 << 1), right? Which means 00000010 (because it was 00000001 and we bit shifted it over one, right?) So we AND GDK_LOCK_MASK with that binary number and if it comes back all 0's we print On, if it comes back all ones, we print Off? I remember bits and pieces of this from college. I don't remember how it completely works though.

    Thank you for everything!!! I gotta get some sleep now.

  10. #10
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    Quote Originally Posted by Spork Schivago View Post
    Wow, this is perfect. I can use this in my code or no? Do you just want me to use it to study from and write my own code or can I copy this and modify it to work for my program?
    Of course you can (but run it through your own test-cases for possible bugs).

    For the Windows GTK stuff, I've only noticed it with the sample program they have me run when I set up the GTK stuff. I have to run pkg-config, and then a few other odd commands, just once. At the end, it says run this command to make sure GTK+ works. I don't actually compile anything. Perhaps my code that I will write myself won't have this problem?
    GTK+ comes with a demo app that presents examples with code for a great deal of stuff. It's called gtk-demo for GTK+2, and gtk3-demo for GTK+3 (it's in GTK's bin folder). That's the one the instructions encourage you to run, to make sure that GTK+ has been setup correctly. Both of them run correctly on my current Windows machine Win8.1 x64 (and on my other and/or previous ones: Win7 x64 and WinXP x86).

    This program you wrote is awesome! The code is very clean and easy to read. I noticed when I run it, I get an error. It compiles clean, I'm using GTK+3. When I run, I get:
    Code:
    Gtk-Message: GtkDialog mapped without a transient parent. This is discouraged.
    every time I do something. I believe this is because the GtkDialog doesn't have a parent to be modal to.
    Thanks for the kind word (the code could be a bit more consistent though).

    Well yeah, most probably GTK+3 doesn't like non-transient modal dialogs any more. My guess is that the offending code lies in the function: mygtk2_alert_box() when it is called with wowner passed as NULL, as I've done in line 223, in the function: on_key_release_event(). Try changing the 1st argument when calling mygtk2_alert_box() in line 223, from "NULL" to "widget" and see if it removes the GTK+3 warning.

    If it does, then the same action should be taken for all calls of the macro MYGTK2_DBG_ERRMSG(), to make sure that its first argument is not NULL (this means for example that you should pass a non-NULL owner window as an extra argument to the function: mygtk2_key_event_print(), to be used in all containing calls of MYGTK2_DBG_ERRMSG() ). Or you can simply modify the code of the macro so that it prints to the console (instead of using a GUI dialog).

    The mouse buttons, on my machine at least, don't do anything. I click but I get no messages.
    Check the console output. For every keypress there's a bunch of info printed in the console. The last 3 lines should tell you which mouse-buttons were pressed when the key was released: Mouse-buttons 1, 2 and 3 correspond to left, middle and right, respectively.

    I'd like to try and fix those two problems come tomorrow or the next day if you don't mind. When I run it, the program doesn't show up in my taskbar. This happens with YaST as well. But just system software. I've never had it happen to my knowledge with an actual user program program. There's only two or three programs on my system that have this issue. It'd be nice if I can find out why. I also have the same problem with the very simple Hello World GTK+ app I wrote. Do you know why?
    Not really, because as I stated before, I haven't worked with GTK+3. Perhaps it's related to GTK+3, because with GTK+2 the app shows up normally in the taskbar.

    That said, GTK+ provides a GtkWindow attribute for deciding on whether you want a window to be displayed in the taskbar or not. It is set/unset via the function: gtk_window_set_skip_taskbar_hint(). It could be that those apps set this attribute to TRUE. However, my code certainly does NOT, so I cannot really tell why you are getting such a behavior (perhaps GTK+3 sets it to TRUE by default, but I seriously doubt it... must be something else).

    One quick question, with this

    Code:
    g_print(        "CapsLock: %s\n",
    
            GDK_LOCK_MASK & state ? "On" : "Off"
    
            );
    How does the GDK_LOCK_MASK & state ? "On" : "Off" work again?

    GDK_LOCK_MASK, we can say is (1 << 1), right? Which means 00000010 (because it was 00000001 and we bit shifted it over one, right?) So we AND GDK_LOCK_MASK with that binary number and if it comes back all 0's we print On, if it comes back all ones, we print Off? I remember bits and pieces of this from college. I don't remember how it completely works though.
    No need to overthinking about it imho. GDK_LOCK_MASK, etc, are predefined bitflags provided by GTK+. If you want to check whether the GDK_LOCK_MASK bit is turned on inside event->state, you just & (AND) event->state with GDK_LOCK_MASK. If the result is TRUE, then the bit is turned on. Here's the relative GTK+2 documentation for the bitflags we are talking about: enum GdkModifierType.

    Thank you for everything!!! I gotta get some sleep now.
    You are welcome. Please come back with feedback on the above when you are ready. However, keep in mind that GTK+2 and GTK+3 are NOT compatible. I'm not familiar with GTK+3, so my help may prove quite limited if it depends solely on GTK+3 stuff.
    Last edited by migf1; 08-23-2015 at 02:54 AM. Reason: Added link for GTK+2's enum GdkModifierType
    "Talk is cheap, show me the code" - Linus Torvalds

  11. #11
    Registered User
    Join Date
    Mar 2014
    Location
    Corning, New York, USA
    Posts
    96
    Oh! For the mouse, I thought it would display a pop-up message or it would display something at the console window when I clicked a mouse button. It does neither. However, if I have a mouse button held down, in Linux or Windows 7, and then I hit a button, like A, it'll tell me the mouse button is pressed.

    The whole reason I was asking about the GDK_LOCK_MASK stuff is my program deals with a byte. Each bit represents a different state. The way I currently read what the values are is by a whole bunch of if then statements. I'm going to be printing the information regardless, this GDK_LOCK_MASK example seems a much more efficient way to do it.

    I currently have to do something like,
    Code:
    if(status_byte & AUTO_LOCKED) {
       printf("Auto Lock: Enabled\n");
    } else {
       printf("Auto Lock: Disabled\n");
    }
    As for the gtk3-demo.exe program, that's the one. When I scroll down through any sample code or through the list of stuff on the left side, the text appears messed up. This could just be something with this laptop's video card. I can try it with another Windows machine eventually.

    I had no trouble compiling the code on Windows either. Everything works as expected, same with the mouse thing. I have to click and then hit a button on the keyboard. I wonder if the missing taskbar thing could have something to do with some custom Gnome settings my friend setup on my Linux box. I can't remember exactly what all of them where, but there was stuff like Add the X to each window, upper right. Group similiar items together on the taskbar, etc. The fact that I get the program showing up on my Windows system's taskbar, to me, that means it's not GTK+ 3.0 or the application. Thanks again for all the help. I'll let you know how it works out.

  12. #12
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by Spork Schivago View Post
    I can execute my application from a terminal shell in X (not an actual console shell), and when I hit something like F1, my application will respond instead of the terminal shell opening help?
    For X, you need to open a new window, so that that window will receive the keypresses. It does not need to be visible, but it does need to have focus (or grab the keyboard) to get the keypresses. (Some desktop environments may not allow that if the window is not visible, however.) It would be best to use that window for your output, instead of the terminal.

    I suppose it would not be too difficult to write an application skeleton that provides a subset of ncursesw (so you can use all Unicode glyphs) as a text window. However, it is not difficult to write fully graphic GTK+ applications, so I don't see why you would want to do that.

    The GTK+ web site has pretty good documentation in my opinion. See GTK+ 2 and GTK+ 3 reference manuals for details and examples.

  13. #13
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    Quote Originally Posted by Spork Schivago View Post
    Oh! For the mouse, I thought it would display a pop-up message or it would display something at the console window when I clicked a mouse button. It does neither. However, if I have a mouse button held down, in Linux or Windows 7, and then I hit a button, like A, it'll tell me the mouse button is pressed.
    Yes, that's what the above code is supposed to do. You first respond to the "key-release-event" via the signal-handler function, and then inside that function you extract all the info related to the event (including any pressed mouse-buttons).

    To respond to mouse-button events first, you need to react to a different event, namely "button_press_event" (or "button_release_event") and connect a different signal handler function to the window. For example, line 245 would change to:

    Code:
        ...
        g_signal_connect(
            G_OBJECT( window ),
            "button_press_event",
            G_CALLBACK( on_button_press_event ),
            NULL
            );
        ...
    with on_button_press_event() being the signal handler function for any pressed mouse-button:

    Code:
    ...
    /* Validation macro for mouse-button events */
    #define MYGTK2_VALID_GDK_EVENT_BUTTON( ev )       \
    (                                           \
        GDK_BUTTON_PRESS == (ev)->type      \
        || GDK_BUTTON_RELEASE == (ev)->type \
        || GDK_2BUTTON_PRESS == (ev)->type  \
        || GDK_3BUTTON_PRESS == (ev)->type  \
    )
    ...
    gboolean mygtk2_button_event_print( const GdkEvent *event );
    
    /****************************************************
     *
     ****************************************************
     */
    gboolean on_button_press_event(
        GtkWidget   *widget,
        GdkEvent    *event,
        gpointer    userdata
        )
    {
    
        gchar tmptxt[MYGTK2_BUFSIZ] = {'\0'};
        guint msbutton = event->button.button;
     
        /* avoid compiler warning for unused params */
        (void)widget;
        (void)userdata;
     
        /* display on the console everything related to the button-press-event */
        mygtk2_button_event_print( event );
      
        /* display in an alert box the name of the pressed button */
        g_snprintf(
            tmptxt,
            sizeof( tmptxt ),
            "%s mouse-button was clicked",
            1 == msbutton
                ? "Left"
                : 2 == msbutton
                    ? "Middle"
                    : 3 == msbutton
                        ? "Right"
                        : "Other/Uknown"
            );
        mygtk2_alert_box( NULL, NULL, tmptxt );
      
        return FALSE; 
    }
    
    /*************************************************//**
     * see:
     * https://developer.gnome.org/gdk2/stable/gdk2-Event-Structures.html#GdkEventButton
     * https://developer.gnome.org/gdk2/stable/gdk2-Windows.html#GdkModifierType
     *****************************************************
     */
    gboolean mygtk2_button_event_print( const GdkEvent *event )
    {
        if ( NULL == event ) {
            MYGTK2_DBG_STDERR_MSG( "NULL pointer argument (event)" );
            return FALSE;
        }
    
        if ( !MYGTK2_VALID_GDK_EVENT_BUTTON(event) ) {
            MYGTK2_DBG_STDERR_MSG( "Not a valid GdkEventButton (event)" );
            return FALSE;
        }
    
        /* device */
        const gchar *txtdevice = gdk_device_get_name( event->button.device );
        g_print( "Device: %s\n", txtdevice );
    
        /* gdk-window */
        GdkWindow *window = event->button.window;
        g_print( "GdkWindow @ %p\n", NULL == window ? 0x0 : (void *)window );
    
        guint32 time = event->button.time;
        g_print( "Time           : %lu\n", (long unsigned) time );
    
        /* type & number of clicks */
        GdkEventType type = event->button.type;
        const gchar *txttype
            = ( GDK_3BUTTON_PRESS == type )
            ? "Triple click"
            : ( GDK_2BUTTON_PRESS == type )
                ? "Double click"
                : ( GDK_BUTTON_PRESS == type )
                    ? "Single click"
                    : ( GDK_BUTTON_RELEASE)
                        ? "Release"
                        : "Unknown";
        g_print( "Type (%d)       : %s\n", type,txttype );
    
        /* mouse-button */
        guint msbutton = event->button.button;
        const gchar *txtmsbutton
            = ( 1 == msbutton )
            ? "Left"
            : ( 2 == msbutton )
                ? "Middle"
                : ( 3 == msbutton )
                    ? "Right"
                    : "Neither left, nor middle, nor right";
        g_print( "Mouse button   : %s (%u)\n", txtmsbutton, msbutton );
    
        /* relative & absolute coordinates */
        g_print( /* gdouble */
            "Relative coords: (x:%.2lf, y:%.2lf)\n",
            event->button.x, event->button.y
            );
        g_print( /* gdouble */
            "Absolute coords: (x:%.2lf, y:%.2lf)\n",
            event->button.x_root, event->button.y_root
            );
    
        /* state of modifier keys & mouse-buttons */
        guint state = event->button.state;
        g_print(
            "CapsLock: %s\n",
            GDK_LOCK_MASK & state ? "On" : "Off"
            );
        g_print(
            "Shift   : %spressed\n",
            GDK_SHIFT_MASK & state ? "\0" : "not "
            );
        g_print(
            "Control : %spressed\n",
            GDK_CONTROL_MASK & state ? "\0" : "not "
            );
        g_print(
            "Alt     : %spressed\n",
            GDK_MOD1_MASK & state ? "\0" : "not "
            );
        g_print(
            "Meta    : %spressed\n",
            GDK_META_MASK & state ? "\0" : "not "
            );
        g_print(
            "1st mouse button: %spressed\n",
            GDK_BUTTON1_MASK & state ? "\0" : "not "
            );
        g_print(
            "2nd mouse button: %spressed\n",
            GDK_BUTTON2_MASK & state ? "\0" : "not "
            );
        g_print(
            "3rd mouse button: %spressed\n",
            GDK_BUTTON3_MASK & state ? "\0" : "not "
            );
    
        putchar( '\n' );
        return TRUE;
    }
    There is an inconsistency (on my part) between this code and the code for the "key_release_event". This code treats the "event" pointer as a GdkEvent pointer (instead of GdkEventButton), while the previous code (for the keys) was using GdkEventKey. Actually, GdkEvent is a union, so you can use it either way you like.

    However, since I now use GdkEvent I do an extra referral step to the event pointer, in order to access the GdkEventButton members.

    Put otherwise, to get for example the GDK window reported in the (GdkEvent *)event, I now write: "event->button.window". If I was handling event as a GdkEventButton pointer, I would have written: "event->window" instead.

    I should fix this in mygtk2.c/mygtk2.h (someday ).

    The whole reason I was asking about the GDK_LOCK_MASK stuff is my program deals with a byte. Each bit represents a different state. The way I currently read what the values are is by a whole bunch of if then statements. I'm going to be printing the information regardless, this GDK_LOCK_MASK example seems a much more efficient way to do it.

    I currently have to do something like,
    Code:
    if(status_byte & AUTO_LOCKED) {
       printf("Auto Lock: Enabled\n");
    } else {
       printf("Auto Lock: Disabled\n");
    }

    Well, I really don't see how this differs from what I did. It looks exactly the same to me.

    As for the gtk3-demo.exe program, that's the one. When I scroll down through any sample code or through the list of stuff on the left side, the text appears messed up. This could just be something with this laptop's video card. I can try it with another Windows machine eventually.
    Well, since you have mentioned it in this thread, I run it again on my Windows 8.1 machine, in order to compare it with GTK+2's gtk-demo. You are right, there is a significant slow-down in the gtk3-demo.

    Actually GTK+3 theming engine has been drastically changed compared to GTK+2's. The obvious visual difference between those 2 demos is the fonts, but there's a lot more going on under the hood. Also, GTK3 has dropped some convenient gtk_ functions for drawing/rendering stuff, and now forces you to use directly the Cairo library. Frankly, that's one of the reasons I'm still reluctant to jump on the GTK+3 train. Another reason is that it seem like its Windows version is not thoroughly tested (I may be wrong, but that's what I've concluded so far). The latest Windows version GTK+2 seems to be far more stable and predictable.

    I had no trouble compiling the code on Windows either. Everything works as expected, same with the mouse thing. I have to click and then hit a button on the keyboard. I wonder if the missing taskbar thing could have something to do with some custom Gnome settings my friend setup on my Linux box. I can't remember exactly what all of them where, but there was stuff like Add the X to each window, upper right. Group similiar items together on the taskbar, etc. The fact that I get the program showing up on my Windows system's taskbar, to me, that means it's not GTK+ 3.0 or the application. Thanks again for all the help. I'll let you know how it works out.
    Sorry, but I can't help you there. I'm not into GTK+3 (not yet anyway).
    Last edited by migf1; 08-23-2015 at 03:07 PM.
    "Talk is cheap, show me the code" - Linus Torvalds

  14. #14
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    I just re-read my last post and I realized that you may conclude that you cannot connect both a key-press and a mouse-button press signal handler to your window (or any widget for that matter). You can connect as many signal handlers as you want to any widget.

    Also, I'm using the macro MYGTK2_DBG_STDERR_MSG() but haven't given code for it:

    Code:
    /**
     * Macro displaying an error-message with debugging info in the stderr stream.
     */
    #define MYGTK2_DBG_STDERR_MSG( errmsg )                         \
    do {                                                            \
        if ( !global_mygtk2debug )                              \
            break;                                          \
        fputs( "*** ERROR:\n", stderr );                        \
        fprintf(stderr,                                         \
            "*** File : %s | Func: %s | Line: %d\n*** %s\n",\
            __FILE__, __func__,  __LINE__,                  \
            (const char *)(errmsg) ? errmsg : "\0"          \
            );                                              \
    } while(0)
    PS. Another inconsistency on my part. For keys I use MYGTK2_DBG_ERRMSG(), while for mouse-buttons I use MYGTK2_DBG_STDERR_MSG() ... I should fix that too (someday )
    Last edited by migf1; 08-23-2015 at 03:40 PM. Reason: code for MYGTK2_DBG_STDERR_MSG()
    "Talk is cheap, show me the code" - Linus Torvalds

  15. #15
    Registered User
    Join Date
    Mar 2014
    Location
    Corning, New York, USA
    Posts
    96
    Thanks Nominal Animal. I agree, GTK+ is the best way to go for this. I didn't realize how easy it was to write GUI's. I remember I tried back in 96 or so with some Microsoft Visual compiler of one sort or another. I don't remember the name. Maybe Visual Turbo C++ or just Turbo C++. I had a book on it, it was a nightmare. Worst thing I ever seen. If I wanted a window, I had to draw the box. There weren't any APIs that I could call or anything. At least not from what I remember. I'm definitely working on the GTK+.

    It's interested. Even though I use the MinGW, the version in Linux that creates Windows executables is more than double in size compared to the ones that I compile natively in Windows. Maybe they need to be stripped. Also, my Makefile needs work. I want it so I have different directories. I don't know if this is the correct structure or not, but this is how I'm working on setting it up currently:

    .\src
    .\include
    .\obj
    .\lib
    .\bin\$(arch)

    .\src would include my .c files. They're becoming cumbersome, having them in the main directory. .\include would include the .h header files for the source files in the .\src directory. The object files will be stored in .\obj while they're getting compiled. .\lib would include the source code to the external libraries that need to be compiled for my project. My .\src files will depend on them, later on. The .\bin directory would be for the executables that get created for the various architectures. I'd like to have it where I could have one name for the end product, right now, that's main. My Makefile will make a main_x86.exe when I call make win32, it'll make a main_x64.exe while I call make win64 and when I call make linux, it'll make a linux_x64. I don't want it like that. I'd rather have make win32 make a .\bin\win32\main.exe make win64 would make a .\bin\win64\make.exe and of course, make linux would make a .\bin\Linux\main (with no extensions).

    I'm having trouble setting this up though. I have it almost completely setup correctly. The only problem is if I don't have the files that are in the src directory in the . directory, it errors out saying it can't find the file. I've tried everything I could think of. Can I post my Makefile and have someone look at it to tell me where I'm going wrong?

    If I have the files that are the src directory in the root directory, where I run make, the files in the src directory do get compiled, not the ones in the root directory. I'd just like to remove the ones in the root directory.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. capturing keystroke from keyboard
    By bepof in forum C Programming
    Replies: 6
    Last Post: 06-12-2013, 10:51 AM
  2. Capturing keyboard input, one character at a time
    By Paul Omans in forum C Programming
    Replies: 17
    Last Post: 05-31-2013, 03:57 PM
  3. Capturing keyboard activity
    By Trooper88 in forum C Programming
    Replies: 2
    Last Post: 02-11-2012, 05:27 PM
  4. Replies: 5
    Last Post: 05-05-2010, 04:24 AM
  5. keyboard capturing
    By xlnk in forum Linux Programming
    Replies: 4
    Last Post: 01-31-2003, 01:02 PM