[comp.sources.x] v12i041: xdvorak: Dvorak keyboard layout for X, Part01/01

ramsdell@linus.mitre.org (03/20/91)

Submitted-by: ramsdell@linus.mitre.org
Posting-number: Volume 12, Issue 41
Archive-name: xdvorak/part01

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	Imakefile
#	patchlevel.h
#	xdvorak.c
# This archive created: Thu Mar 14 09:56:20 1991
export PATH; PATH=/bin:$PATH
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
Xdvorak -- Dvorak keyboard layout for X.

This is README version 1.1 of 91/03/14.

USAGE

Xdvorak installs a Dvorak keyboard map with the command:

		xdvorak

With no arguments, the program toggles between a Dvorak and a qwerty
keyboard map.  Any arguments force the installation of a qwerty
keyboard map.  Execute "xdvorak -qwerty" when you log out.

INSTALLATION

There are no special installation instructions.

HISTORY

The original program was distributed as contributed software in X11R3.
Recently, Jim Blandy donated some patches that allow it to work on
servers with a keymaps that were not anticipated.  He also rewrote
some parts of the code as to make them easier to understand.
SHAR_EOF
fi # end of overwriting check
if test -f 'Imakefile'
then
	echo shar: will not over-write existing file "'Imakefile'"
else
cat << \SHAR_EOF > 'Imakefile'
/* This is Imakefile version 1.1 of 91/03/14 for xdvorak. */

        DEPLIBS = $(DEPXLIB)
LOCAL_LIBRARIES = $(XLIB)

SimpleProgramTarget(xdvorak)
SHAR_EOF
fi # end of overwriting check
if test -f 'patchlevel.h'
then
	echo shar: will not over-write existing file "'patchlevel.h'"
else
cat << \SHAR_EOF > 'patchlevel.h'
/* 1.1 */
SHAR_EOF
fi # end of overwriting check
if test -f 'xdvorak.c'
then
	echo shar: will not over-write existing file "'xdvorak.c'"
else
cat << \SHAR_EOF > 'xdvorak.c'
/* xdvorak installs a Dvorak keyboard map on an X server. */
/* This is xdvorak.c version 1.1 of 91/03/14. */

/* Install a Dvorak keyboard map with the command:

		xdvorak

With no arguments, the program toggles between a Dvorak and a qwerty
keyboard map.  Any arguments force the installation of a qwerty
keyboard map.  Execute "xdvorak -qwerty" when you log out. */

/* John D. Ramsdell -- July 1988.  Original version. */

/* Jim Blandy -- March 1991.  Modified xdvorak so that KeyCode
permutations can be the result of changes required by KeySym's in
other than the the first column of a keymap row, and made the program
easier to understand. */

static char copyright[] = "Copyright 1991 by The MITRE Corporation.";
/* Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies.  The
MITRE Corporation makes no representations about the suitability of
this software for any purpose.  It is provided "as is" without express
or implied warranty.  */

static char what[] = "@(#)xdvorak.c	1.1";

/* Compile with the command: cc -O -o xdvorak xdvorak.c -lX
   Possible switches:
	-DDEBUG spews info about which keys are being remapped.
	-DDO_NOT_REMAP doesn't actually give the new mapping to the server.
*/

#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>

int is_qwerty(dpy)		/* Root window is checked for a */
     Display *dpy;		/* property that gives the keycode */
{				/* of the key marked D. */
  KeyCode current_key_marked_D;
  Window root;
  Atom key, type, actual_type_return;
  int actual_format_return;
  unsigned long nitems_return;
  unsigned long bytes_after_return;
  unsigned char *prop_return;

  current_key_marked_D = XKeysymToKeycode(dpy, XK_D);
  key = XInternAtom(dpy, "KEYCODE_OF_KEY_MARKED_D", False);
  type = XInternAtom(dpy, "KEYCODE", False);
  root = XDefaultRootWindow(dpy);

  XGetWindowProperty(dpy, root, key, 0L, 1L, False, type,
		     &actual_type_return, &actual_format_return,
		     &nitems_return, &bytes_after_return,
		     &prop_return);

  if (actual_type_return != type) { /* Property not there. */
    XChangeProperty(dpy, root, key, type, 8*sizeof(KeyCode),
		    PropModeReplace,
		    (unsigned char *)(&current_key_marked_D), 1);
    XFlush(dpy);		/* Record keycode with root window. */
    return True;
  } else
    return *((KeyCode *) prop_return) == current_key_marked_D;
}

#define NCHAR 256
KeySym qwerty[NCHAR], dvorak[NCHAR]; /* These will contain the permutations. */
int old_keysym_to_map_index[NCHAR];
Display *dpy;

/* translate from marked key to dvorak value.  If Q or D are too large
   for the table, we need to make a new table.  */
#define translate(Q, D)				\
{						\
  KeySym q = (Q);				\
  KeySym d = (D);				\
						\
  if (q >= NCHAR || d >= NCHAR)			\
    abort ();					\
  dvorak[q] = d;				\
  qwerty[d] = q;				\
}

/* Here is the ANSI Standard Dvorak layout.

	! @ # $ % ^ & * ( ) [ +
	1 2 3 4 5 6 7 8 9 0 ] =

	 " , . P Y F G C R L ?
	 ' , . p y f g c r l /

	  A O E U I D H T N S _
	  a o e u i d h t n s -

	   : Q J K X B M W V Z
	   ; Q J K X B M W V Z
*/

/* The following is a fairly machine independent description of
   minimal changes required to make a Dvorak keyboard.  You might want
   to tailor this for your machine.  */

make_permutation()
{
  int i;
  for (i = 0; i < NCHAR; i++) {
    dvorak[i] = i;		/* Initialize with the  */
    qwerty[i] = i;		/* identify permutation. */
  }

  translate (XK_minus, 	XK_bracketleft);
  /* Next row. */
  translate (XK_Q,	XK_quoteright);
  translate (XK_W,	XK_comma);
  translate (XK_E,	XK_period);
  translate (XK_R,	XK_P);
  translate (XK_T,	XK_Y);
  translate (XK_Y,	XK_F);
  translate (XK_U,	XK_G);
  translate (XK_I,	XK_C);
  translate (XK_O,	XK_R);
  translate (XK_P,	XK_L);
  translate (XK_bracketleft,	XK_slash);
  /* Next row. */
  translate (XK_S,	XK_O);
  translate (XK_D,	XK_E);
  translate (XK_F,	XK_U);
  translate (XK_G,	XK_I);
  translate (XK_H,	XK_D);
  translate (XK_J,	XK_H);
  translate (XK_K,	XK_T);
  translate (XK_L,	XK_N);
  translate (XK_semicolon,	XK_S);
  translate (XK_quoteright,	XK_minus);
  /* Next row. */
  translate (XK_Z,	XK_semicolon);
  translate (XK_X,	XK_Q);
  translate (XK_C,	XK_J);
  translate (XK_V,	XK_K);
  translate (XK_B,	XK_X);
  translate (XK_N,	XK_B);
  translate (XK_comma,	XK_W);
  translate (XK_period,	XK_V);
  translate (XK_slash,	XK_Z);
}

int permute_keyboard_map(force_qwerty)
     int force_qwerty;
{
  KeySym *new_map, *old_map, *permutation;
  int min_keycode = dpy->min_keycode;
  int max_keycode = dpy->max_keycode;
  int keysyms_per_keycode;
  int am_qwerty = is_qwerty(dpy);
  int table_size;

  if (force_qwerty) {
    if (am_qwerty) return 0;	/* No change needed. */
    permutation = qwerty;
  } else permutation = am_qwerty ? dvorak : qwerty; /* Else toggle. */
  make_permutation();

  /* Note that old_map and new_map are actually TWO-dimensional arrays,
     not flat arrays as their type would suggest.  Each group of
     keysyms_per_keycode elements is a row of the table.  */
  old_map =			/* old_map will not be modified. */
    XGetKeyboardMapping(dpy,
			min_keycode,
			max_keycode - min_keycode + 1,
			&keysyms_per_keycode);
  new_map =			/* new_map gets the new map. */
    XGetKeyboardMapping(dpy,
			min_keycode,
			max_keycode - min_keycode + 1,
			&keysyms_per_keycode);
#if defined DEBUG
  printf("max, min, per= %d, %d, %d\n",
	 max_keycode, min_keycode, keysyms_per_keycode);
#endif

  table_size = (max_keycode + 1 - min_keycode) * keysyms_per_keycode;

  /* Since we want to move whole keys around, we move entire rows of
     the table around, not just particular elements.  If key X is
     becoming key Y, all the keysyms of keycode X are set to the
     keysyms of keycode Y.

     The permutation array only specifies what happens to the capital
     letters, but we need to remap all the keysyms associated with the
     key - the shifted, controlled and otherwise modified meanings.
     Thus, we use permutation to decide which rows of the keyboard
     mapping to change.  */

  /* old_keysym_to_map_index[i] is the index in old_map of the start
     of the row containing keysym i.  */
  {
    /* We scan a keycode at a time; map_row is the index of the first
       element of the current keycode's row in old_map.  */
    int map_row;

    for (map_row = 0; map_row < table_size; map_row += keysyms_per_keycode)
      {
	int i;
	
	for (i = 0; i < keysyms_per_keycode; i++)
	  if (old_map[map_row + i] < NCHAR
	      && old_map[map_row + i] != NoSymbol)
	    old_keysym_to_map_index[old_map[map_row + i]] = map_row;
      }
  }

  /* Scan new_map, looking for keycodes we'd like to swap.  */
  {
    int new_map_ix;			/* "Map Index"  */

    for (new_map_ix = 0; new_map_ix < table_size;)
      /* Does permutation remap this keysym?  */
      if (new_map[new_map_ix] < NCHAR
	  && permutation[new_map[new_map_ix]] != new_map[new_map_ix])
	{
	  int old_map_ix
	    = old_keysym_to_map_index[permutation[new_map[new_map_ix]]];
	  int i;

	  /* new_map_ix might be in the middle of a keycode's row, so back it
	     up to the beginning.  */
	  new_map_ix -= (new_map_ix % keysyms_per_keycode);

	  /* Change all the keysyms on this keycode.  */
	  for (i = 0; i < keysyms_per_keycode; i++)
	    {
#if defined DEBUG
	      if (old_map[old_map_ix] != NoSymbol)
		printf("Mapping %s->%s \t(%d->%d)\n",
		       XKeysymToString(old_map[new_map_ix]),
		       XKeysymToString(old_map[old_map_ix]),
		       old_map_ix, new_map_ix);
#endif
	      new_map[new_map_ix++] = old_map[old_map_ix++];
	    }
	}
      else
	new_map_ix++;
  }
	    
#ifndef DO_NOT_REMAP
  XChangeKeyboardMapping(dpy,	/* Install new map. */
			 min_keycode,
			 keysyms_per_keycode,
			 new_map,
			 max_keycode - min_keycode + 1);
#endif

  XFree((char *) old_map);	/* Release storage. */
  XFree((char *) new_map);
  XFlush(dpy);			/* Force out changes. */
  printf("%s keyboard installed.\n",
	 permutation == dvorak ? "Dvorak" : "Qwerty");
  return 0;			/* Permutation succeeded. */
}

main(argc, argv)
     int argc;
     char **argv;
{ argv;				/* Not used. */

  if (!(dpy = XOpenDisplay(NULL))) {
    perror("Cannot open display\n");
    exit(1);
  }

  /* Force installation of qwerty when any extra arguments are given. */
  exit (permute_keyboard_map(argc > 1));
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

--
Dan Heller
------------------------------------------------
O'Reilly && Associates		 Z-Code Software
Senior Writer			       President
argv@ora.com			argv@zipcode.com
------------------------------------------------
General Email: argv@sun.com
Comp-sources-x stuff: comp-sources.x@uunet.uu.net