/* * keyboard.c * * Part of gwm, the Gratuitous Window Manager, * by Gary Wong, . * * Copyright (C) 2009 Gary Wong * * This program is free software: you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * 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, see . * * $Id$ */ #include #include #include #include #include "gwm.h" #include "actions.h" #include "keyboard.h" enum _keysym { /* See X Window System Protocol (version 11, release 6.7), Appendix A. */ KEYSYM_TAB = 0xFF09, KEYSYM_MENU = 0xFF67, KEYSYM_NUM_LOCK = 0xFF7F, KEYSYM_F1 = 0xFFBE, KEYSYM_F2 = 0xFFBF, KEYSYM_F3 = 0xFFC0, KEYSYM_F4 = 0xFFC1, KEYSYM_F5 = 0xFFC2, KEYSYM_F6 = 0xFFC3, KEYSYM_F7 = 0xFFC4, KEYSYM_F8 = 0xFFC5, KEYSYM_F9 = 0xFFC6, KEYSYM_F10 = 0xFFC7, KEYSYM_F11 = 0xFFC8, KEYSYM_F12 = 0xFFC9, KEYSYM_SHIFT_L = 0xFFE1, KEYSYM_SHIFT_R = 0xFFE2, KEYSYM_CONTROL_L = 0xFFE3, KEYSYM_CONTROL_R = 0xFFE4, KEYSYM_CAPS_LOCK = 0xFFE5, KEYSYM_SHIFT_LOCK = 0xFFE6, KEYSYM_META_L = 0xFFE7, KEYSYM_META_R = 0xFFE8, KEYSYM_ALT_L = 0xFFE9, KEYSYM_ALT_R = 0xFFEA }; /* Map of keysyms: first index is keycode, second index is group/modifier. */ xcb_keysym_t keyboard_map[ 0x100 ][ 4 ]; /* Map of modifiers, indexed by xcb_map_index_t. */ xcb_keycode_t *modifier_map[ 8 ]; const struct key_action key_actions[] = { /* FIXME This table should be configurable, of course. */ { KEYSYM_TAB, XCB_MOD_MASK_1, action_raise_lowest }, { KEYSYM_F1, XCB_MOD_MASK_1, action_iconify_window }, { KEYSYM_F5, XCB_MOD_MASK_1, action_stack_opposite } }; const int num_key_actions = sizeof key_actions / sizeof *key_actions; static void establish_grabs( void ) { int populated, i, j, screen, lock, num_lock; populated = FALSE; lock = num_lock = -1; for( i = 0; i < 8; i++ ) if( modifier_map[ i ] ) { populated = TRUE; for( j = 0; modifier_map[ i ][ j ]; j++ ) if( keyboard_map[ modifier_map[ i ][ j ] ][ 0 ] == KEYSYM_CAPS_LOCK || keyboard_map[ modifier_map[ i ][ j ] ][ 0 ] == KEYSYM_SHIFT_LOCK ) lock = i; else if( keyboard_map[ modifier_map[ i ][ j ] ][ 0 ] == KEYSYM_NUM_LOCK ) num_lock = i; } if( !populated ) /* The modifier map is unpopulated: there's nothing we can do yet, and we'll be invoked again later once there is. */ return; for( screen = 0; screen < num_screens; screen++ ) { /* Undo any previous grabs. */ xcb_ungrab_key( c, XCB_GRAB_ANY, screens[ screen ]->root, XCB_GRAB_ANY ); for( i = 0; i < num_key_actions; i++ ) { for( j = 0; j < 0x100 && keyboard_map[ j ][ 0 ] != key_actions[ i ].keysym; j++ ) ; if( j >= 0x100 ) /* No key is currently bound to keysym; omit this action. */ continue; xcb_grab_key( c, FALSE, screens[ screen ]->root, key_actions[ i ].modifiers, j, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC ); if( lock >= 0 ) xcb_grab_key( c, FALSE, screens[ screen ]->root, key_actions[ i ].modifiers | ( 1 << lock ), j, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC ); if( num_lock >= 0 ) xcb_grab_key( c, FALSE, screens[ screen ]->root, key_actions[ i ].modifiers | ( 1 << num_lock ), j, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC ); if( lock >= 0 && num_lock >= 0 ) xcb_grab_key( c, FALSE, screens[ screen ]->root, key_actions[ i ].modifiers | ( 1 << lock ) | ( 1 << num_lock ), j, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC ); } } } static void handle_get_keyboard_mapping( unsigned int sequence, void *reply, xcb_generic_error_t *error, union callback_param cp ) { if( error ) { show_error( error ); free( error ); } if( reply ) { xcb_get_keyboard_mapping_reply_t *r = reply; xcb_keysym_t *in, *out; int i, j, syms_used, count = r->length / r->keysyms_per_keycode; syms_used = r->keysyms_per_keycode < 4 ? r->keysyms_per_keycode : 4; in = xcb_get_keyboard_mapping_keysyms( r ); out = keyboard_map[ cp.l ]; for( i = 0; i < count; i++ ) { for( j = 0; j < syms_used; j++ ) *out++ = *in++; for( ; j < 4; j++ ) *out++ = XCB_NONE; if( r->keysyms_per_keycode > 4 ) in += r->keysyms_per_keycode - 4; } free( reply ); establish_grabs(); } } extern void get_keyboard_mapping( int first, int count ) { union callback_param cp; cp.l = first; handle_async_reply( xcb_get_keyboard_mapping( c, first, count ).sequence, handle_get_keyboard_mapping, cp ); } static void handle_get_modifier_mapping( unsigned int sequence, void *reply, xcb_generic_error_t *error, union callback_param cp ) { xcb_keycode_t *in, *out; xcb_get_modifier_mapping_reply_t *r; int i, j, codes_used; assert( !error ); /* the protocol defines no error conditions... */ assert( reply ); /* ...so we should always get a valid response */ r = reply; codes_used = r->keycodes_per_modifier < 8 ? r->keycodes_per_modifier : 8; in = xcb_get_modifier_mapping_keycodes( r ); for( i = 0; i < 8; i++, in += r->keycodes_per_modifier ) { if( modifier_map[ i ] ) free( modifier_map[ i ] ); for( codes_used = 0, j = 0; j < r->keycodes_per_modifier; j++ ) if( in[ j ] ) codes_used++; if( codes_used ) { modifier_map[ i ] = out = xmalloc( ( codes_used + 1 ) * sizeof *out ); for( j = 0; j < r->keycodes_per_modifier; j++ ) if( in[ j ] ) *out++ = in[ j ]; *out = 0; } else modifier_map[ i ] = NULL; } free( reply ); establish_grabs(); } extern void get_modifier_mapping( void ) { union callback_param cp; cp.l = 0; handle_async_reply( xcb_get_modifier_mapping( c ).sequence, handle_get_modifier_mapping, cp ); } #if DEBUG extern void cleanup_keyboard( void ) { int i; for( i = 0; i < 8; i++ ) if( modifier_map[ i ] ) free( modifier_map[ i ] ); } #endif