/*      $Id: keys_management.c 23834 2006-11-12 09:09:28Z olivier $

        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation; either version 2, or (at your option)
        any later version.
 
        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
 
        You should have received a copy of the GNU General Public License
        along with this program; if not, write to the Free Software
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
        keys_management - (c) 2002-2006 Olivier Fourdan
 
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <X11/Xlib.h>
#include <X11/keysym.h>

#include <glib.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "keys_management.h"

unsigned int KeyMask = 0;
unsigned int ButtonMask = 0;
unsigned int ButtonKeyMask = 0;
unsigned int AltMask = 0;
unsigned int MetaMask = 0;
unsigned int NumLockMask = 0;
unsigned int ScrollLockMask = 0;
unsigned int SuperMask = 0;
unsigned int HyperMask = 0;

MyKey *
parseKeyString (char *str)
{
    MyKey *my_key;
    GdkDisplay *gdisplay;
    char *k;

    gdisplay = gdk_display_get_default ();

    g_return_val_if_fail (str != NULL, NULL);

    my_key = g_new (MyKey, 1);
    my_key->keycode = 0;
    my_key->modifier = 0;

    k = strrchr (str, '+');
    if (k)
    {
        /* There is a modifier */
        gchar *tmp;

        tmp = g_ascii_strdown ((gchar *) str, strlen (str));

        my_key->keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (gdisplay), XStringToKeysym (++k));
        if (strstr (tmp, "shift"))
        {
            my_key->modifier = my_key->modifier | ShiftMask;
        }
        if (strstr (tmp, "control"))
        {
            my_key->modifier = my_key->modifier | ControlMask;
        }
        if (strstr (tmp, "alt"))
        {
            my_key->modifier = my_key->modifier | AltMask;
        }
        if (strstr (tmp, "meta"))
        {
            my_key->modifier = my_key->modifier | MetaMask;
        }
        if (strstr (tmp, "super"))
        {
            my_key->modifier = my_key->modifier | SuperMask;
        }
        if (strstr (tmp, "hyper"))
        {
            my_key->modifier = my_key->modifier | HyperMask;
        }
        if (strstr (tmp, "mod1"))
        {
            my_key->modifier = my_key->modifier | Mod1Mask;
        }
        if (strstr (tmp, "mod2"))
        {
            my_key->modifier = my_key->modifier | Mod2Mask;
        }
        if (strstr (tmp, "mod3"))
        {
            my_key->modifier = my_key->modifier | Mod3Mask;
        }
        if (strstr (tmp, "mod4"))
        {
            my_key->modifier = my_key->modifier | Mod4Mask;
        }
        if (strstr (tmp, "mod5"))
        {
            my_key->modifier = my_key->modifier | Mod5Mask;
        }
        g_free (tmp);
    }
    else
    {
        my_key->keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (gdisplay), XStringToKeysym (str));
        my_key->modifier = 0;
    }

    return (my_key);
}

/* 
 * Beware, this can cause BadAccess X errors, enclose the call to this
 * routine within gdk_error_trap_push ()/gdk_error_trap_pop ().
 * This is not performed here because that could potentially cause
 * a performance hit when grabbing several keys.
 */
void
grab_key (MyKey * key)
{
    gint i, nscreens;
    GdkDisplay *gdisplay;

    gdisplay = gdk_display_get_default ();
    nscreens = gdk_display_get_n_screens(gdisplay);

    for(i = 0; i < nscreens; i++) 
    {
        GdkScreen *gscr;
        Window w;
        
        gscr = gdk_display_get_screen(gdisplay, i);
        w = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (gscr));

        if (key->keycode)
        {
            if (key->modifier == 0)
            {
                XGrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, AnyModifier, 
                          w, FALSE, GrabModeAsync, GrabModeAsync);
            }
            else
            {
                /* Here we grab all combinations of well known modifiers */
                XGrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                          key->modifier, w, FALSE, GrabModeAsync, GrabModeAsync);
                XGrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                          key->modifier | ScrollLockMask, w, FALSE, GrabModeAsync, GrabModeAsync);
                XGrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                          key->modifier | NumLockMask, w, FALSE, GrabModeAsync, GrabModeAsync);
                XGrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                          key->modifier | LockMask, w, FALSE, GrabModeAsync, GrabModeAsync);
                XGrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                          key->modifier | ScrollLockMask | NumLockMask, w, FALSE, GrabModeAsync, GrabModeAsync);
                XGrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                          key->modifier | ScrollLockMask | LockMask, w, FALSE, GrabModeAsync, GrabModeAsync);
                XGrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                          key->modifier | LockMask | NumLockMask, w, FALSE, GrabModeAsync, GrabModeAsync);
                XGrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode,
                          key->modifier | ScrollLockMask | LockMask | NumLockMask, w, FALSE, GrabModeAsync, GrabModeAsync);
            }
        }
    }
}

void
ungrab_key (MyKey * key)
{
    gint i, nscreens;
    GdkDisplay *gdisplay;

    gdisplay = gdk_display_get_default ();
    nscreens = gdk_display_get_n_screens(gdisplay);

    for(i = 0; i < nscreens; i++) 
    {
        GdkScreen *gscr;
        Window w;
        
        gscr = gdk_display_get_screen(gdisplay, i);
        w = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (gscr));

        if (key->keycode)
        {
            if (key->modifier == 0)
            {
                XUngrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, AnyModifier, w);
            }
            else
            {
                XUngrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                            key->modifier, w);
                XUngrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                            key->modifier | ScrollLockMask, w);
                XUngrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                            key->modifier | NumLockMask, w);
                XUngrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                            key->modifier | LockMask, w);
                XUngrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                            key->modifier | ScrollLockMask | NumLockMask, w);
                XUngrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                            key->modifier | ScrollLockMask | LockMask, w);
                XUngrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                            key->modifier | LockMask | NumLockMask, w);
                XUngrabKey (GDK_DISPLAY_XDISPLAY (gdisplay), key->keycode, 
                            key->modifier | ScrollLockMask | LockMask | NumLockMask, w);
            }
        }
    }
}

void
init_modifiers (void)
{
    GdkDisplay *gdisplay;
    GdkScreen *gscr;
    Window w;

    XModifierKeymap *modmap;
    KeySym *keymap;
    int i;
    int keysyms_per_keycode;
    int min_keycode;
    int max_keycode;

    gdisplay = gdk_display_get_default ();
    gscr = gdk_display_get_default_screen (gdisplay);
    w = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (gscr));

    AltMask = 0;
    MetaMask = 0;
    NumLockMask = 0;
    ScrollLockMask = 0;
    SuperMask = 0;
    HyperMask = 0;
    keysyms_per_keycode = 0;
    min_keycode = 0;
    max_keycode = 0;

    XDisplayKeycodes (GDK_DISPLAY_XDISPLAY (gdisplay), &min_keycode, &max_keycode);
    modmap = XGetModifierMapping (GDK_DISPLAY_XDISPLAY (gdisplay));
    keymap = XGetKeyboardMapping (GDK_DISPLAY_XDISPLAY (gdisplay), min_keycode, max_keycode - min_keycode + 1, &keysyms_per_keycode);

    for (i = 3 * modmap->max_keypermod; i < 8 * modmap->max_keypermod; i++)
    {
        unsigned int keycode = modmap->modifiermap[i];

        if ((keycode >= min_keycode) && (keycode <= max_keycode))
        {
            int j;
            KeySym *syms = keymap + (keycode - min_keycode) * keysyms_per_keycode;

            for (j = 0; j < keysyms_per_keycode; j++)
            {
                if (syms[j] == XK_Num_Lock)
                {
                    NumLockMask |= (1 << (i / modmap->max_keypermod));
                }
                else if (syms[j] == XK_Scroll_Lock)
                {
                    ScrollLockMask |= (1 << (i / modmap->max_keypermod));
                }
                else if ((syms[j] == XK_Super_L) || (syms[j] == XK_Super_R))
                {
                    SuperMask |= (1 << (i / modmap->max_keypermod));
                }
                else if ((syms[j] == XK_Hyper_L) || (syms[j] == XK_Hyper_R))
                {
                    HyperMask |= (1 << (i / modmap->max_keypermod));
                }
                else if ((syms[j] == XK_Meta_L) || (syms[j] == XK_Meta_R))
                {
                    MetaMask |= (1 << (i / modmap->max_keypermod));
                }
                else if ((syms[j] == XK_Alt_L) || (syms[j] == XK_Alt_R))
                {
                    AltMask |= (1 << (i / modmap->max_keypermod));
                }
            }
        }
    }
    KeyMask = ControlMask | ShiftMask | AltMask | MetaMask | SuperMask | HyperMask;

    ButtonMask = Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask;

    ButtonKeyMask = KeyMask | ButtonMask;
}


syntax highlighted by Code2HTML, v. 0.9.1