[Pkg-kbd-devel] Bug#524057: [PATCH] loadkeys: Auto-convert “traditional”/Unicode keysyms

Michael Schutte michi at uiae.at
Tue Apr 14 17:45:49 UTC 2009


The Linux kernel distinguishes between K(KTYP, KVAL) keysyms and Unicode
characters.  This patch makes loadkeys query the console’s Unicode mode
and convert between the two keysym types according to the result.  The
theoretical advantage is that less keymaps need both an 8-bit and a
Unicode variant (cf. trq[u], ua[-utf]).

A similar patch (read_keymaps_fmt) has been in use in Debian’s version
of kbd since 2004; see <http://bugs.debian.org/251550> for a discussion.
Credit for this goes to Denis Barbier <barbier at linuxfr.org>.

Signed-off-by: Michael Schutte <michi at uiae.at>
---

Alexey, may I ask you to keep the Cc when you reply to this mail?  I’d
like to keep a record in the Debian BTS <http://bugs.debian.org/524057>.

I tried my best to check that this patch does no harm, but it is quite
invasive.  I think you really have to triple-check before you git-am – I
don’t want to lock out users from their machines because I missed one or
the other corner case.

src/loadkeys.c is not included in this patch for readability reasons.

 src/dumpkeys.c |    3 +-
 src/ksyms.c    |  112 ++++++++++++++++++++++++++++++++------------------------
 src/ksyms.h    |    4 +-
 src/loadkeys.y |   72 ++++++++++++++----------------------
 4 files changed, 95 insertions(+), 96 deletions(-)

diff --git a/src/dumpkeys.c b/src/dumpkeys.c
index 879c96f..1e6acba 100644
--- a/src/dumpkeys.c
+++ b/src/dumpkeys.c
@@ -135,8 +135,7 @@ print_keysym(int code, char numeric) {
 	t = KTYP(code);
 	v = KVAL(code);
 	if (t >= syms_size) {
-		code = code ^ 0xf000;
-		if (!numeric && (p = unicodetoksym(code)) != NULL)
+		if (!numeric && (p = codetoksym(code)) != NULL)
 			printf("%-16s", p);
 		else
 			printf("U+%04x          ", code);
diff --git a/src/ksyms.c b/src/ksyms.c
index e8a494a..6946e0b 100644
--- a/src/ksyms.c
+++ b/src/ksyms.c
@@ -1644,7 +1644,7 @@ struct cs {
 
 /* Functions for both dumpkeys and loadkeys. */
 
-static int prefer_unicode = 0;
+int prefer_unicode = 0;
 static const char *chosen_charset = NULL;
 
 void
@@ -1685,11 +1685,6 @@ set_charset(const char *charset) {
 	sym *p;
 	unsigned int i;
 
-	if (!strcasecmp(charset, "unicode")) {
-		prefer_unicode = 1;
-		return 0;
-	}
-
 	for (i = 1; i < sizeof(charsets)/sizeof(charsets[0]); i++) {
 		if (!strcasecmp(charsets[i].charset, charset)) {
 			charsets[0].charset = charsets[i].charset;
@@ -1700,7 +1695,7 @@ set_charset(const char *charset) {
 				if(p->name[0])
 					syms[0].table[i] = p->name;
 			}
-			chosen_charset = charset;
+			chosen_charset = strdup(charset);
 			return 0;
 		}
 	}
@@ -1710,38 +1705,66 @@ set_charset(const char *charset) {
 }
 
 const char *
-unicodetoksym(int code) {
+codetoksym(int code) {
 	unsigned int i;
 	int j;
 	sym *p;
 
 	if (code < 0)
 		return NULL;
-	if (code < 0x80)
-		return iso646_syms[code];
-	for (i = 0; i < sizeof(charsets)/sizeof(charsets[0]); i++) {
-		p = charsets[i].charnames;
-		for (j = charsets[i].start; j < 256; j++, p++) {
-			if (p->uni == code && p->name[0])
+
+	if (code < 0x1000) {	/* "traditional" keysym */
+		if (KTYP(code) == KT_META)
+			return NULL;
+		if (KTYP(code) == KT_LETTER)
+			code = K(KT_LATIN, KVAL(code));
+		if (KTYP(code) > KT_LATIN)
+			return syms[KTYP(code)].table[KVAL(code)];
+
+		for (i = 0; i < sizeof(charsets)/sizeof(charsets[0]); i++) {
+			p = charsets[i].charnames;
+			if (!p)
+				continue;
+			p += KVAL(code) - charsets[i].start;
+			if (p->name[0])
 				return p->name;
 		}
 	}
+
+	else {			/* Unicode keysym */
+		code ^= 0xf000;
+
+		if (code < 0x80)
+			return iso646_syms[code];
+
+		for (i = 0; i < sizeof(charsets)/sizeof(charsets[0]); i++) {
+			p = charsets[i].charnames;
+			if (!p)
+				continue;
+			for (j = charsets[i].start; j < 256; j++, p++) {
+				if (p->uni == code && p->name[0])
+					return p->name;
+			}
+		}
+	}
+
 	return NULL;
 }
 
 /* Functions for loadkeys. */
 
-int unicode_used = 0;
-
 int
 ksymtocode(const char *s) {
 	unsigned int i;
 	int j, jmax;
 	int keycode;
 	sym *p;
+	int save_prefer_unicode;
 
 	if (!strncmp(s, "Meta_", 5)) {
+		save_prefer_unicode = prefer_unicode;
 		keycode = ksymtocode(s+5);
+		prefer_unicode = save_prefer_unicode;
 		if (KTYP(keycode) == KT_LATIN)
 			return K(KT_META, KVAL(keycode));
 
@@ -1767,10 +1790,8 @@ ksymtocode(const char *s) {
 		for (i = 0; i < sizeof(charsets)/sizeof(charsets[0]); i++) {
 			p = charsets[i].charnames;
 			for (j = charsets[i].start; j < 256; j++, p++)
-				if (!strcmp(s,p->name)) {
-					unicode_used = 1;
-					return (p->uni ^ 0xf000); /* %%% */
-				}
+				if (!strcmp(s,p->name))
+					return (p->uni ^ 0xf000);
 		}
 	} else /* if (!chosen_charset) */ {
 		/* note: some keymaps use latin1 but with euro,
@@ -1821,38 +1842,33 @@ ksymtocode(const char *s) {
 }
 
 int
-unicodetocode(int code) {
-	const char *s;
-
-	s = unicodetoksym(code);
-	if (s)
-		return ksymtocode(s);
-	else {
-		unicode_used = 1;
-		return (code ^ 0xf000); /* %%% */
-	}
+convert_code(int code)
+{
+	const char *ksym;
+
+	if (KTYP(code) == KT_META)
+		return code;
+	else if (prefer_unicode == (code >= 0x1000))
+		return code;		/* no conversion necessary */
+
+	/* depending on prefer_unicode, this will give us either an 8-bit
+	 * K(KTYP, KVAL) or a Unicode keysym xor 0xf000 */
+	ksym = codetoksym(code);
+	if (ksym)
+		return ksymtocode(ksym);
+	else
+		return code;
 }
 
 int
 add_capslock(int code)
 {
-	char buf[7];
-	const char *p;
-
-	if (KTYP(code) == KT_LATIN)
+	if (KTYP(code) == KT_LATIN && (!prefer_unicode || code < 0x80))
 		return K(KT_LETTER, KVAL(code));
-	if ((unsigned) KTYP(code) >= syms_size) {
-		if ((p = unicodetoksym(code ^ 0xf000)) == NULL) {
-			sprintf(buf, "U+%04x", code ^ 0xf000);
-			p = buf;
-		}
-	} else {
-		sprintf(buf, "0x%04x", code);
-		p = buf;
-	}
-#if 0
-	/* silence the common usage  dumpkeys | loadkeys -u  */
-	fprintf(stderr, _("plus before %s ignored\n"), p);
-#endif
-	return code;
+	else if ((code ^ 0xf000) < 0x100)
+		/* Unicode Latin-1 Supplement */
+		/* a bit dirty to use KT_LETTER here, but it should work */
+		return K(KT_LETTER, code ^ 0xf000);
+	else
+		return convert_code(code);
 }
diff --git a/src/ksyms.h b/src/ksyms.h
index 74cff92..b3c3d0c 100644
--- a/src/ksyms.h
+++ b/src/ksyms.h
@@ -26,10 +26,10 @@ extern const unsigned int syn_size;
 #define CODE_FOR_UNKNOWN_KSYM (-1)
 
 extern int set_charset(const char *name);
-extern const char *unicodetoksym(int code);
+extern const char *codetoksym(int code);
 extern void list_charsets(FILE *f);
 extern int ksymtocode(const char *s);
-extern int unicodetocode(int code);
+extern int convert_code(int code);
 extern int add_capslock(int code);
 
 #endif
diff --git a/src/loadkeys.y b/src/loadkeys.y
index 9ff4759..13faf42 100644
--- a/src/loadkeys.y
+++ b/src/loadkeys.y
@@ -63,7 +63,7 @@ static void killkey(int index, int table);
 static void compose(int diacr, int base, int res);
 static void do_constant(void);
 static void do_constant_key (int, u_short);
-static void loadkeys(char *console, int *warned);
+static void loadkeys(char *console);
 static void mktable(void);
 static void bkeymap(void);
 static void strings_as_usual(void);
@@ -73,10 +73,10 @@ static void strings_as_usual(void);
 static void compose_as_usual(char *charset);
 static void lkfatal0(const char *, int);
 extern int set_charset(const char *charset);
+extern int prefer_unicode;
 extern char *xstrdup(char *);
 int key_buf[MAX_NR_KEYMAPS];
 int mod;
-extern int unicode_used;
 int private_error_ct = 0;
 
 extern int rvalct;
@@ -240,11 +240,13 @@ rvalue1		: rvalue
 			}
 		;
 rvalue		: NUMBER
-			{$$=$1;}
-		| UNUMBER
-			{$$=($1 ^ 0xf000); unicode_used=1;}
+			{$$=convert_code($1);}
                 | PLUS NUMBER
                         {$$=add_capslock($2);}
+		| UNUMBER
+			{$$=convert_code($1^0xf000);}
+		| PLUS UNUMBER
+			{$$=add_capslock($2^0xf000);}
 		| LITERAL
 			{$$=$1;}
                 | PLUS LITERAL
@@ -302,8 +304,9 @@ main(int argc, char *argv[]) {
 		{ NULL, 0, NULL, 0 }
 	};
 	int c;
+	int fd;
+	int mode;
 	char *console = NULL;
-        int warned = 0;
 
 	set_progname(argv[0]);
 
@@ -333,7 +336,7 @@ main(int argc, char *argv[]) {
 				opts = 1;
 				break;
 			case 'u':
-				set_charset("unicode");
+				prefer_unicode = 1;
 				break;
 			case 'q':
 				quiet = 1;
@@ -349,8 +352,20 @@ main(int argc, char *argv[]) {
 		}
 	}
 
+	if (!optm && !prefer_unicode) {
+		/* no -u option: auto-enable it if console is in Unicode mode */
+		fd = getfd(NULL);
+		if (ioctl(fd, KDGKBMODE, &mode)) {
+			perror("KDGKBMODE");
+			fprintf(stderr, _("loadkeys: error reading keyboard mode\n"));
+			exit(1);
+		}
+		if (mode == K_UNICODE)
+			prefer_unicode = 1;
+		close(fd);
+	}
+
 	args = argv + optind - 1;
-	unicode_used = 0;
 	yywrap();	/* set up the first input file, if any */
 	if (yyparse() || private_error_ct) {
 		fprintf(stderr, _("syntax error in map file\n"));
@@ -375,14 +390,14 @@ main(int argc, char *argv[]) {
 		char ch = *e;
 		*e = '\0';
 		if (verbose) printf("%s\n", s);
-	        loadkeys(s, &warned);
+	        loadkeys(s);
 		*e = ch;
 		s = e;
 	      }
 	    free(buf);
 	  }
 	else
-	  loadkeys(NULL, &warned);
+	  loadkeys(NULL);
 	exit(0);
 }
 
@@ -811,20 +826,10 @@ compose(int diacr, int base, int res) {
 }
 
 static int
-defkeys(int fd, char *cons, int *warned) {
+defkeys(int fd) {
 	struct kbentry ke;
 	int ct = 0;
 	int i,j,fail;
-	int oldm;
-
-	if (unicode_used) {
-	     /* Switch keyboard mode for a moment -
-		do not complain about errors.
-		Do not attempt a reset if the change failed. */
-	     if (ioctl(fd, KDGKBMODE, &oldm)
-	        || (oldm != K_UNICODE && ioctl(fd, KDSKBMODE, K_UNICODE)))
-		  oldm = K_UNICODE;
-	}
 
 	for(i=0; i<MAX_NR_KEYMAPS; i++) {
 	    if (key_map[i]) {
@@ -891,27 +896,6 @@ defkeys(int fd, char *cons, int *warned) {
 	    }
 	}
 
-	if(unicode_used && oldm != K_UNICODE) {
-	     if (ioctl(fd, KDSKBMODE, oldm)) {
-		  fprintf(stderr, _("%s: failed to restore keyboard mode\n"),
-			  progname);
-	     }
-
-	     if (!warned++)
-	       {
-		     int kd_mode = -1;
-		     if (ioctl(fd, KDGETMODE, &kd_mode) || (kd_mode != KD_GRAPHICS))
-		       {
-			 /*
-			  * It is okay for the graphics console to have a non-unicode mode.
-			  * only talk about other consoles
-			  */
-			 fprintf(stderr, _("%s: warning: this map uses Unicode symbols, %s mode=%d\n"
-				     "    (perhaps you want to do `kbd_mode -u'?)\n"),
-			     progname, cons ? cons : "NULL", kd_mode);
-		       }
-	       }
-	}
 	return ct;
 }
 
@@ -1044,12 +1028,12 @@ do_constant (void) {
 }
 
 static void
-loadkeys (char *console, int *warned) {
+loadkeys (char *console) {
         int fd;
         int keyct, funcct, diacct = 0;
 
 	fd = getfd(console);
-	keyct = defkeys(fd, console, warned);
+	keyct = defkeys(fd);
 	funcct = deffuncs(fd);
 	if (verbose) {
 	        printf(_("\nChanged %d %s and %d %s.\n"),
-- 
1.6.2.1


-- 
Michael Schutte <michi at uiae.at>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 489 bytes
Desc: Digital signature
URL: <http://lists.alioth.debian.org/pipermail/pkg-kbd-devel/attachments/20090414/b2b37ab4/attachment.pgp>


More information about the Pkg-kbd-devel mailing list