2011-04-30 18:26:10 -04:00
|
|
|
|
%{
|
|
|
|
|
/*
|
|
|
|
|
* vim:ts=4:sw=4:expandtab
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include <X11/Xlib.h>
|
2012-05-30 05:11:08 -04:00
|
|
|
|
#include <X11/XKBlib.h>
|
2011-04-30 18:26:10 -04:00
|
|
|
|
|
2011-10-23 08:16:56 -04:00
|
|
|
|
#include "libi3.h"
|
|
|
|
|
|
2011-04-30 18:26:10 -04:00
|
|
|
|
extern Display *dpy;
|
|
|
|
|
|
|
|
|
|
struct context {
|
|
|
|
|
int line_number;
|
|
|
|
|
char *line_copy;
|
|
|
|
|
|
|
|
|
|
char *compact_error;
|
|
|
|
|
|
|
|
|
|
/* These are the same as in YYLTYPE */
|
|
|
|
|
int first_column;
|
|
|
|
|
int last_column;
|
|
|
|
|
|
|
|
|
|
char *result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
|
|
|
|
extern int yylex(struct context *context);
|
|
|
|
|
extern int yyparse(void);
|
|
|
|
|
extern FILE *yyin;
|
|
|
|
|
YY_BUFFER_STATE yy_scan_string(const char *);
|
|
|
|
|
|
|
|
|
|
static struct context *context;
|
2012-08-01 20:14:56 -04:00
|
|
|
|
static xcb_connection_t *conn;
|
|
|
|
|
static xcb_key_symbols_t *keysyms;
|
2011-04-30 18:26:10 -04:00
|
|
|
|
|
|
|
|
|
/* We don’t need yydebug for now, as we got decent error messages using
|
|
|
|
|
* yyerror(). Should you ever want to extend the parser, it might be handy
|
|
|
|
|
* to just comment it in again, so it stays here. */
|
|
|
|
|
//int yydebug = 1;
|
|
|
|
|
|
|
|
|
|
void yyerror(const char *error_message) {
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
fprintf(stderr, "CONFIG: %s\n", error_message);
|
|
|
|
|
fprintf(stderr, "CONFIG: line %d:\n",
|
|
|
|
|
context->line_number);
|
|
|
|
|
fprintf(stderr, "CONFIG: %s\n", context->line_copy);
|
|
|
|
|
fprintf(stderr, "CONFIG: ");
|
|
|
|
|
for (int c = 1; c <= context->last_column; c++)
|
|
|
|
|
if (c >= context->first_column)
|
|
|
|
|
fprintf(stderr, "^");
|
|
|
|
|
else fprintf(stderr, " ");
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int yywrap() {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *rewrite_binding(const char *bindingline) {
|
|
|
|
|
char *result = NULL;
|
|
|
|
|
|
2012-08-01 20:14:56 -04:00
|
|
|
|
conn = xcb_connect(NULL, NULL);
|
|
|
|
|
if (conn == NULL || xcb_connection_has_error(conn)) {
|
|
|
|
|
fprintf(stderr, "Cannot open display\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
keysyms = xcb_key_symbols_alloc(conn);
|
|
|
|
|
|
2011-04-30 18:26:10 -04:00
|
|
|
|
context = calloc(sizeof(struct context), 1);
|
|
|
|
|
|
|
|
|
|
yy_scan_string(bindingline);
|
|
|
|
|
|
|
|
|
|
if (yyparse() != 0) {
|
|
|
|
|
fprintf(stderr, "Could not parse configfile\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = context->result;
|
|
|
|
|
|
|
|
|
|
if (context->line_copy)
|
|
|
|
|
free(context->line_copy);
|
|
|
|
|
free(context);
|
2012-08-01 20:14:56 -04:00
|
|
|
|
xcb_key_symbols_free(keysyms);
|
|
|
|
|
xcb_disconnect(conn);
|
2011-04-30 18:26:10 -04:00
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* XXX: does not work for combinations of modifiers yet */
|
|
|
|
|
static char *modifier_to_string(int modifiers) {
|
|
|
|
|
//printf("should convert %d to string\n", modifiers);
|
|
|
|
|
if (modifiers == (1 << 3))
|
2011-07-11 13:41:15 -04:00
|
|
|
|
return strdup("$mod+");
|
2011-04-30 18:26:10 -04:00
|
|
|
|
else if (modifiers == ((1 << 3) | (1 << 0)))
|
2011-07-11 13:41:15 -04:00
|
|
|
|
return strdup("$mod+Shift+");
|
2011-05-01 07:56:35 -04:00
|
|
|
|
else if (modifiers == (1 << 9))
|
2011-07-11 13:41:15 -04:00
|
|
|
|
return strdup("$mod+");
|
2011-05-01 07:56:35 -04:00
|
|
|
|
else if (modifiers == ((1 << 9) | (1 << 0)))
|
2011-07-11 13:41:15 -04:00
|
|
|
|
return strdup("$mod+Shift+");
|
|
|
|
|
else if (modifiers == (1 << 0))
|
|
|
|
|
return strdup("Shift+");
|
|
|
|
|
else return strdup("");
|
2011-04-30 18:26:10 -04:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-01 20:14:56 -04:00
|
|
|
|
/*
|
|
|
|
|
* Returns true if sym is bound to any key except for 'except_keycode' on the
|
|
|
|
|
* first four layers (normal, shift, mode_switch, mode_switch + shift).
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) {
|
|
|
|
|
xcb_keycode_t i,
|
|
|
|
|
min_keycode = xcb_get_setup(conn)->min_keycode,
|
|
|
|
|
max_keycode = xcb_get_setup(conn)->max_keycode;
|
|
|
|
|
|
|
|
|
|
for (i = min_keycode; i && i <= max_keycode; i++) {
|
|
|
|
|
if (i == except_keycode)
|
|
|
|
|
continue;
|
|
|
|
|
for (int level = 0; level < 4; level++) {
|
|
|
|
|
if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym)
|
|
|
|
|
continue;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-30 18:26:10 -04:00
|
|
|
|
%}
|
|
|
|
|
|
|
|
|
|
%error-verbose
|
|
|
|
|
%lex-param { struct context *context }
|
|
|
|
|
|
|
|
|
|
%union {
|
|
|
|
|
int number;
|
|
|
|
|
char *string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
%token <number>NUMBER "<number>"
|
|
|
|
|
%token <string>STR "<string>"
|
|
|
|
|
%token TOKBINDCODE
|
2011-05-01 07:56:35 -04:00
|
|
|
|
%token TOKMODVAR "$mod"
|
2011-04-30 18:26:10 -04:00
|
|
|
|
%token MODIFIER "<modifier>"
|
|
|
|
|
%token TOKCONTROL "control"
|
|
|
|
|
%token TOKSHIFT "shift"
|
|
|
|
|
%token WHITESPACE "<whitespace>"
|
|
|
|
|
|
|
|
|
|
%%
|
|
|
|
|
|
|
|
|
|
lines: /* empty */
|
|
|
|
|
| lines WHITESPACE bindcode
|
|
|
|
|
| lines error
|
|
|
|
|
| lines bindcode
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
bindcode:
|
|
|
|
|
TOKBINDCODE WHITESPACE binding_modifiers NUMBER WHITESPACE STR
|
|
|
|
|
{
|
|
|
|
|
//printf("\tFound keycode binding mod%d with key %d and command %s\n", $<number>3, $4, $<string>6);
|
|
|
|
|
int level = 0;
|
|
|
|
|
if (($<number>3 & (1 << 0))) {
|
|
|
|
|
/* When shift is included, we really need to use the second-level
|
|
|
|
|
* symbol (upper-case). The lower-case symbol could be on a
|
|
|
|
|
* different key than the upper-case one (unlikely for letters, but
|
|
|
|
|
* more likely for special characters). */
|
|
|
|
|
level = 1;
|
2012-08-01 20:14:56 -04:00
|
|
|
|
|
|
|
|
|
/* Try to use the keysym on the first level (lower-case). In case
|
|
|
|
|
* this doesn’t make it ambiguous (think of a keyboard layout
|
|
|
|
|
* having '1' on two different keys, but '!' only on keycode 10),
|
|
|
|
|
* we’ll stick with the keysym of the first level.
|
|
|
|
|
*
|
|
|
|
|
* This reduces a lot of confusion for users who switch keyboard
|
|
|
|
|
* layouts from qwerty to qwertz or other slight variations of
|
|
|
|
|
* qwerty (yes, that happens quite often). */
|
|
|
|
|
KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, 0);
|
|
|
|
|
if (!keysym_used_on_other_key(sym, $4))
|
|
|
|
|
level = 0;
|
2011-04-30 18:26:10 -04:00
|
|
|
|
}
|
2012-05-30 05:11:08 -04:00
|
|
|
|
KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, level);
|
2011-04-30 18:26:10 -04:00
|
|
|
|
char *str = XKeysymToString(sym);
|
|
|
|
|
char *modifiers = modifier_to_string($<number>3);
|
2011-10-23 08:16:56 -04:00
|
|
|
|
sasprintf(&(context->result), "bindsym %s%s %s\n", modifiers, str, $<string>6);
|
2011-04-30 18:26:10 -04:00
|
|
|
|
free(modifiers);
|
|
|
|
|
}
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
binding_modifiers:
|
|
|
|
|
/* NULL */ { $<number>$ = 0; }
|
|
|
|
|
| binding_modifier
|
|
|
|
|
| binding_modifiers '+' binding_modifier { $<number>$ = $<number>1 | $<number>3; }
|
|
|
|
|
| binding_modifiers '+' { $<number>$ = $<number>1; }
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
binding_modifier:
|
|
|
|
|
MODIFIER { $<number>$ = $<number>1; }
|
2011-05-01 07:56:35 -04:00
|
|
|
|
| TOKMODVAR { $<number>$ = $<number>1; }
|
2011-04-30 18:26:10 -04:00
|
|
|
|
| TOKCONTROL { $<number>$ = (1 << 2); }
|
|
|
|
|
| TOKSHIFT { $<number>$ = (1 << 0); }
|
|
|
|
|
;
|