/* keyboard.c: takes care of the keyboard window. */ #include #include #include #include #include #include #include #include #include #include #include "globaldefs.h" #include "config.h" #include "keyboard.h" #include "menus.h" #include "smileys.h" #include "text.h" #include "trace.h" #include "utils.h" int rowsd[0xff], colsd[0xff], rowsq[0xff], colsq[0xff] ; int *rowsp = rowsd, *colsp = colsd, *rowse = rowsd, *colse = colsd ; Widget keys[5][14], keyframes[5][14], shifts[2], shiftframes[2] ; int lastshift = -1, nextshift = -1 ; Pixel fgpix, bgpix ; /* a pixmap that is 25% foregroud/ 75% background and a pixmap that is all background */ Pixmap keytoppm25, keytoppmbg ; list *speedsmileys = NULL, *mistakesmileys = NULL ; Widget statusform ; /* this is where the smileys are */ Widget speedbox, mistakebox ; /* list of keyboard brands that are recognized; contents of the next few tables should be sorted in this order */ typedef enum { SunKeyboard, DECKeyboard, HPKeyboard, PCKeyboard, KinesisKeyboard, NumVendors } VendorID ; /* this table should contain the keycodes of the keys 'a' and 's' */ int v_tab [][2] = { { 84, 85 }, /* Sun */ { 194, 199 }, /* DEC */ { 53, 52 }, /* HP */ { 38, 39 }, /* PC */ { 38, 39 }, /* Kinesis */ } ; /* this table should keep the number of keys that the left hand is responsible for */ int vlr_tab [][5] = { { 5, 5, 5, 5, 0 }, /* Sun */ { 6, 5, 5, 5, 0 }, /* DEC */ { 6, 5, 5, 5, 0 }, /* HP */ { 6, 5, 5, 5, 0 }, /* PC */ { 6, 5, 5, 5, 1 }, /* Kinesis */ } ; int *lr_tab ; /* points to the right vlr_tab */ /* key layouts....Dvorak */ Keyboard vd_tab[][2] = { { { /* Sun */ "1234567890[]=`", "',.pyfgcrl/\\", "aoeuidhtns-", ";qjkxbmwvz", " " }, { "!@#$%^&*(){}+~", "\"<>PYFGCRL?|", "AOEUIDHTNS_", ":QJKXBMWVZ", " " } }, { { /* DECstation */ "`1234567890[]", "',.pyfgcrl/=", "aoeuidhtns-\\", ";qjkxbmwvz", " " }, { "~!@#$%^&*(){}", "\"<>PYFGCRL?+", "AOEUIDHTNS_|", ":QJKXBMWVZ", " " } }, { { /* HP */ "`1234567890[]", "',.pyfgcrl/=\\", "aoeuidhtns-", ";qjkxbmwvz", " " }, { "~!@#$%^&*(){}", "\"<>PYFGCRL?+|", "AOEUIDHTNS_", ":QJKXBMWVZ", " " } }, { { /* PC */ "`1234567890[]=", "',.pyfgcrl/\\", "aoeuidhtns-", ";qjkxbmwvz", " " }, { "~!@#$%^&*(){}+", "\"<>PYFGCRL?|", "AOEUIDHTNS_", ":QJKXBMWVZ", " " } }, { { /* Kinesis */ "`1234567890=", "',.pyfgcrl/", "aoeuidhtns-", ";qjkxbmwvz", "\\ []" }, { "~!@#$%^&*()+", "\"<>PYFGCRL?", "AOEUIDHTNS_", ":QJKXBMWVZ", "| {}" } } }; /* ...and qwerty */ Keyboard vq_tab[][2] = { { { /* Sun */ "1234567890-=\\`", "qwertyuiop[]", "asdfghjkl;'", "zxcvbnm,./", " " }, { "!@#$%^&*()_+|~", "QWERTYUIOP{}", "ASDFGHJKL:\"", "ZXCVBNM<>?", " " } }, { { /* DECstation */ "`1234567890-=", "qwertyuiop[]", "asdfghjkl;'\\", "zxcvbnm,./", " " }, { "~!@#$%^&*()_+", "QWERTYUIOP{}", "ASDFGHJKL:\"|", "ZXCVBNM<>?", " " } }, { { /* HP */ "`1234567890-=", "qwertyuiop[]\\", "asdfghjkl;'", "zxcvbnm,./", " " }, { "~!@#$%^&*()_+", "QWERTYUIOP{}|", "ASDFGHJKL:\"", "ZXCVBNM<>?", " " } }, { { /* PC */ "`1234567890-=\\", "qwertyuiop[]", "asdfghjkl;'", "zxcvbnm,./", " " }, { "~!@#$%^&*()_+|", "QWERTYUIOP{}", "ASDFGHJKL:\"", "ZXCVBNM<>?", " " } }, { { /* Kinesis */ "=1234567890-", "qwertyuiop\\", "asdfghjkl;'", "zxcvbnm,./", "` []" }, { "+!@#$%^&*()_", "QWERTYUIOP|", "ASDFGHJKL:\"", "ZXCVBNM<>?", "~ {}" } } }; /* points to the appropriate table...Dvorak, qwerty, emulated, physical */ Keyboard *d_tab, *q_tab, *e_tab, *p_tab ; char translationtable[0xff] ; /* sizes of the "empty" keys on left and right */ int vlpad[][NUMROWS] = { { 3, 5, 7, 9, 22 }, { 0, 6, 8, 10, 23 }, { 0, 6, 8, 10, 23 }, { 0, 6, 8, 10, 23 }, { 0, 3, 3, 3, 8 } } ; int vrpad[][NUMROWS] = { { 0, 6, 9, 12, 0 }, { 8, 5, 3, 11, 0 }, { 6, 0, 6, 9, 0 }, { 2, 4, 7, 10, 0 }, { 0, 0, 0, 3, 0 } } ; int *lpad, *rpad ; /* space on the left */ int vlspace[] = { 37, 37, 42, 37, 80 } ; int lspace ; #ifdef _NO_PROTO static void buildtranslatetables() ; static VendorID vendor() ; static int isdvorak() ; #else static void buildtranslatetables(void) ; static VendorID vendor(Widget) ; static int isdvorak(Widget, VendorID) ; #endif /* The keyboard window */ Widget MakeKeyboard(toplevel) Widget toplevel ; { Widget mainwindow, rowcol, frame, button, outer ; Widget lower ; Widget menubar ; Widget separator ; Widget scrolledtext, scrolledwindow ; XmString label ; int row, col ; VendorID v ; TextUserData *textu ; if ((v = vendor(toplevel)) == ERROR) { fprintf(stderr, "Sorry, I don't know who made your keyboard.\n") ; fprintf(stderr, "I will assume a Sun workstation.\n") ; v = SunKeyboard ; } q_tab = vq_tab[v] ; d_tab = vd_tab[v] ; lpad = vlpad[v] ; rpad = vrpad[v] ; lspace = vlspace[v] ; lr_tab = vlr_tab[v] ; if (isdvorak(toplevel, v)) { p_tab = d_tab ; rowsp = rowsd ; colsp = colsd ; } else { p_tab = q_tab ; rowsp = rowsq ; colsp = colsq ; } if (app_data.emulated_keyboard == Dvorak) { e_tab = d_tab ; rowse = rowsd ; colse = colsd ; } else { e_tab = q_tab ; rowse = rowsq ; colse = colsq ; } mainwindow = XtVaCreateManagedWidget("mainwindow", xmMainWindowWidgetClass, toplevel, NULL) ; outer = XtVaCreateWidget("outer", xmRowColumnWidgetClass, mainwindow, XmNorientation, XmVERTICAL, NULL) ; frame = XtVaCreateManagedWidget("inner", xmFrameWidgetClass, outer, NULL) ; label = XmStringCreateSimple("The Keyboard Practicer") ; button = XtVaCreateManagedWidget("name", xmLabelWidgetClass, frame, XmNlabelString, label, NULL) ; XmStringFree(label) ; /* the practice text */ frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, outer, NULL) ; scrolledwindow = XtVaCreateManagedWidget("scrolledwindow", xmScrolledWindowWidgetClass, frame, NULL) ; textu = (TextUserData *)XtMalloc(sizeof(TextUserData)) ; textu->isreading = False ; textu->original = textu->filtered = NULL ; scrolledtext = XtVaCreateManagedWidget("text", xmTextWidgetClass, scrolledwindow, XmNeditable, True, XmNeditMode, XmMULTI_LINE_EDIT, XmNverifyBell, False, XmNblinkRate, 0, XmNcursorPositionVisible, False, XmNautoShowCursorPosition, False, XmNrows, 7, XmNcolumns, 80, XmNscrollLeftSide, True, XmNscrollVertical, True, XmNuserData, textu, NULL) ; XtAddCallback(scrolledtext, XmNmodifyVerifyCallback, (XtCallbackProc)TextModify, scrolledtext) ; XtAddCallback(scrolledtext, XmNmotionVerifyCallback, (XtCallbackProc)CursorMove, scrolledtext) ; /* the "status" box: figures and smileys plus status of options */ statusform = XtVaCreateWidget("inner", xmFormWidgetClass, outer, NULL) ; frame = XtVaCreateManagedWidget("mistakeframe", xmFrameWidgetClass, statusform, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 3, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 0, NULL) ; sprintf(buf, "Mistakes: %.3f wpm", (double)0.0) ; label = XmStringCreateSimple(buf) ; mistakebox = XtVaCreateManagedWidget("mistake", xmLabelGadgetClass, frame, XmNlabelString, label, NULL) ; XmStringFree(label) ; mistakesmileys = (list *) XtMalloc(sizeof(list)) ; mistakesmileys->w = frame ; mistakesmileys->next = NULL ; frame = XtVaCreateManagedWidget("speedframe", xmFrameWidgetClass, statusform, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 3, XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET, XmNrightWidget, mistakesmileys->w, XmNrightOffset, 0, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, mistakesmileys->w, XmNbottomOffset, 5, NULL) ; label = XmStringCreateSimple("Speed: 0 wpm") ; speedbox = XtVaCreateManagedWidget("speed", xmLabelGadgetClass, frame, XmNlabelString, label, NULL) ; XmStringFree(label) ; speedsmileys = (list *) XtMalloc(sizeof(list)) ; speedsmileys->w = frame ; speedsmileys->next = NULL ; sprintf(buf, " Keyboard: %s ", app_data.emulated_keyboard == Dvorak ? "Dvorak" : "Qwerty") ; label = XmStringCreateSimple(buf) ; keyboardlabel = XtVaCreateManagedWidget("speed", xmLabelWidgetClass, statusform, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 0, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 0, XmNlabelString, label, NULL) ; XmStringFree(label) ; label = XmStringCreateSimple(app_data.show_next_key ? " Case insensitive " : " Case sensitive ") ; ignorecaseslabel = XtVaCreateManagedWidget("speed", xmLabelWidgetClass, statusform, XmNtopAttachment, XmATTACH_WIDGET, XmNtopOffset, 0, XmNtopWidget, keyboardlabel, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftOffset, 0, XmNleftWidget, keyboardlabel, XmNlabelString, label, NULL) ; XmStringFree(label) ; label = XmStringCreateSimple(app_data.show_keytop ? " Show keytop symbols " : " Don't show keytop symbols ") ; keytoplabel = XtVaCreateManagedWidget("speed", xmLabelWidgetClass, statusform, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, keyboardlabel, XmNtopOffset, 0, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, keyboardlabel, XmNrightOffset, 10, XmNlabelString, label, NULL) ; XmStringFree(label) ; label = XmStringCreateSimple(app_data.show_next_key ? " Show next key " : " Don't show next key ") ; nextkeylabel = XtVaCreateManagedWidget("speed", xmLabelWidgetClass, statusform, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, ignorecaseslabel, XmNtopOffset, 0, XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET, XmNrightWidget, keytoplabel, XmNrightOffset, 0, XmNlabelString, label, NULL) ; XmStringFree(label) ; filterlabel = XtVaCreateManagedWidget("speed", xmLabelWidgetClass, statusform, XmNtopAttachment, XmATTACH_WIDGET, XmNtopOffset, 0, XmNtopWidget, ignorecaseslabel, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 0, NULL) ; /* the lower half, with the keyboard */ frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, outer, XmNtraversalOn, False, NULL) ; lower = XtVaCreateWidget("lower", xmRowColumnWidgetClass, frame, XmNtraversalOn, False, XmNorientation, XmVERTICAL, NULL) ; for (row = 0 ; row < NUMROWS ; row++) { if (row) separator = XtVaCreateManagedWidget("separator", xmSeparatorWidgetClass, lower, NULL) ; rowcol = XtVaCreateWidget("rowcol", xmRowColumnWidgetClass, lower, XmNnavigationType, XmNONE, XmNtraversalOn, False, XmNorientation, XmHORIZONTAL, NULL) ; /* space on the left */ button = XtVaCreateManagedWidget("", widgetClass, rowcol, XmNwidth, lspace, XmNborderWidth, 0, NULL) ; if (lpad[row]) { if (row != 4) { /* don't put a key next to the space bar */ frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, rowcol, XmNshadowType, XmSHADOW_OUT, XmNnavigationType, XmNONE, XmNshadowThickness, 3, XmNtraversalOn, False, NULL) ; } if (row == 3) shiftframes[0] = frame ; for (col = 0 ; col < lpad[row] ; col++) buf[col] = ' ' ; buf[col] = '\0' ; label = XmStringCreateSimple(buf) ; if (row != 4) button = XtVaCreateManagedWidget("button", xmLabelWidgetClass, frame, XmNlabelString, label, NULL) ; else button = XtVaCreateManagedWidget("button", xmLabelWidgetClass, rowcol, XmNlabelString, label, NULL) ; XmStringFree(label) ; if (row == 3) shifts[0] = button ; } for (col = 0 ; e_tab[0][row][col] ; col++) { if (col == lr_tab[row]) XtVaCreateManagedWidget("space", rectObjClass, rowcol, XmNwidth, 4, NULL) ; keyframes[row][col] = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, rowcol, XmNshadowType, XmSHADOW_OUT, XmNnavigationType, XmNONE, XmNshadowThickness, 3, XmNtraversalOn, False, NULL) ; keys[row][col] = XtVaCreateManagedWidget("key", xmLabelWidgetClass, keyframes[row][col], NULL) ; } if (rpad[row]) { frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, rowcol, XmNshadowType, XmSHADOW_OUT, XmNnavigationType, XmNONE, XmNshadowThickness, 3, XmNtraversalOn, False, NULL) ; if (row == 3) shiftframes[1] = frame ; for (col = 0 ; col < rpad[row] ; col++) buf[col] = ' ' ; buf[col] = '\0' ; label = XmStringCreateSimple(buf) ; button = XtVaCreateManagedWidget("button", xmLabelWidgetClass, frame, XmNlabelString, label, NULL) ; XmStringFree(label) ; if (row == 3) shifts[1] = button ; } XtManageChild(rowcol) ; } XtVaGetValues(keys[0][0], XmNforeground, &fgpix, NULL) ; XtVaGetValues(keys[0][0], XmNbackground, &bgpix, NULL) ; keytoppmbg = XmGetPixmap(XtScreen(toplevel), "background", bgpix, fgpix) ; keytoppm25 = XmGetPixmap(XtScreen(toplevel), "25_foreground", fgpix, bgpix) ; buildtranslatetables() ; PrepareSmileys(toplevel) ; XtManageChild(lower) ; XtManageChild(statusform) ; XtManageChild(outer) ; menubar = CreateMenus(mainwindow, scrolledtext) ; XtVaSetValues(mainwindow, XmNworkWindow, outer, NULL) ; return (scrolledtext) ; } void RedrawKeyboard(scrolledtext) Widget scrolledtext ; { int row, col ; XmString label ; char c ; for (row = 0 ; row < NUMROWS ; row++) for (col = 0 ; (c = e_tab[0][row][col]) ; col++) { if (isspace(c)) sprintf(buf, Spacebar) ; else sprintf(buf, " %c ", app_data.show_keytop ? c : ' ') ; label = XmStringCreateSimple(buf) ; if (IsHomePosition(row, col)) XtVaSetValues(keys[row][col], XmNlabelString, label, XmNbackgroundPixmap, keytoppm25, XmNforeground, fgpix, NULL) ; else XtVaSetValues(keys[row][col], XmNlabelString, label, XmNforeground, fgpix, XmNbackground, bgpix, NULL) ; XmStringFree(label) ; XtVaSetValues(keyframes[row][col], XmNshadowType, XmSHADOW_OUT, NULL) ; } for (col = 0 ; col < 2 ; col++) { XtVaSetValues(shiftframes[col], XmNshadowType, XmSHADOW_OUT, NULL) ; XtVaSetValues(shifts[col], XmNforeground, fgpix, XmNbackground, bgpix, NULL) ; } lastrow = lastcol = nextrow = nextcol = lastshift = nextshift = -1 ; inputc = '\0' ; UpdateKeyboard(scrolledtext, XmTextGetInsertionPosition(scrolledtext)) ; } Boolean IsHomePosition(row, col) int row, col ; { return (row == 2 && !(col == lr_tab[2]-1 || col == lr_tab[2] || col > lr_tab[2]+4)) ; } static void buildtranslatetables() { int row, col, i ; char c, sc ; /* char and shifted char */ for (i = 0 ; i < 0xff ; i++) { rowsd[i] = colsd[i] = ERROR ; rowsq[i] = colsq[i] = ERROR ; } for (row = 0 ; row < NUMROWS ; row++) for (col = 0 ; d_tab[0][row][col] ; col++) { c = d_tab[0][row][col] ; sc = d_tab[1][row][col] ; rowsd[(int)c] = row ; colsd[(int)c] = col ; rowsd[(int)sc] = row ; colsd[(int)sc] = col ; if (c == ' ') { rowsd['\r'] = row ; colsd['\r'] = col ; rowsd['\n'] = row ; colsd['\n'] = col ; rowsd['\t'] = row ; colsd['\t'] = col ; } c = q_tab[0][row][col] ; sc = q_tab[1][row][col] ; rowsq[(int)c] = row ; colsq[(int)c] = col ; rowsq[(int)sc] = row ; colsq[(int)sc] = col ; if (c == ' ') { rowsq['\r'] = row ; colsq['\r'] = col ; rowsq['\n'] = row ; colsq['\n'] = col ; rowsq['\t'] = row ; colsq['\t'] = col ; } } } static VendorID vendor(w) Widget w ; /* return index to vendor of keyboard */ { KeySym ks ; int v ; if (app_data.vendor && casecmp(app_data.vendor, "")) { if (!casecmp(app_data.vendor, "Sun")) return (SunKeyboard) ; else if (!casecmp(app_data.vendor, "HP")) return (HPKeyboard) ; else if (!casecmp(app_data.vendor, "DEC")) return (DECKeyboard) ; else if (!casecmp(app_data.vendor, "PC")) return (PCKeyboard) ; else if (!casecmp(app_data.vendor, "Kinesis")) return (KinesisKeyboard) ; else { printf("huh? %s\n", app_data.vendor) ; Usage() ; exit(1) ; } } for (v = 0 ; v < NumVendors ; v++) { ks = XKeycodeToKeysym(XtDisplay(w), v_tab[v][0], 0) & 0xff ; if (tolower(ks) == 'a') return (v) ; } return (ERROR) ; } static int isdvorak(w, v) Widget w ; VendorID v ; /* return true if keyboard is Dvorak */ { KeySym ks ; if (app_data.physical_keyboard && strcmp(app_data.physical_keyboard, "")) { if (!casecmp(app_data.physical_keyboard, "dvorak")) return (True) ; else if (!casecmp(app_data.physical_keyboard, "qwerty")) return (False) ; else { Usage() ; exit(1) ; } } ks = XKeycodeToKeysym(XtDisplay(w), v_tab[v][1], 0) & 0xff ; if (tolower(ks) == 'o') return (True) ; else return (False) ; }