diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/wm/keyboard.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/wm/keyboard.b')
| -rw-r--r-- | appl/wm/keyboard.b | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/appl/wm/keyboard.b b/appl/wm/keyboard.b new file mode 100644 index 00000000..7e257826 --- /dev/null +++ b/appl/wm/keyboard.b @@ -0,0 +1,511 @@ +implement Keybd; + +# +# extensive revision of code originally by N. W. Knauft +# +# Copyright © 1997 Lucent Technologies Inc. All rights reserved. +# Revisions Copyright © 1998 Vita Nuova Limited. All rights reserved. +# Rewritten code Copyright © 2001 Vita Nuova Holdings Limited. All rights reserved. +# +# To do: +# input from file +# calculate size + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Rect, Point: import draw; + +include "tk.m"; + tk: Tk; + +include "tkclient.m"; + tkclient: Tkclient; + +include "arg.m"; + +include "keyboard.m"; + +Keybd: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +FONT: con "/fonts/lucidasans/boldlatin1.6.font"; +SPECFONT: con "/fonts/lucidasans/unicode.6.font"; + +# size in pixels +#KEYSIZE: con 16; +KEYSIZE: con 13; +KEYSPACE: con 2; +KEYBORDER: con 1; +KEYGAP: con KEYSPACE - (2 * KEYBORDER); +#ENDGAP: con 2 - KEYBORDER; +ENDGAP: con 0; + +Key: adt { + name: string; + val: int; + size: int; + x: list of int; + on: int; +}; + +background: con "#dddddd"; + +Backspace, Tab, Backslash, CapsLock, Return, Shift, Ctrl, Esc, Alt, Space: con iota; + +specials := array[] of { +Backspace => Key("<-", '\b', 28, nil, 0), +Tab => Key("Tab", '\t', 26, nil, 0), +Backslash => Key("\\\\", '\\', KEYSIZE, nil, 0), +CapsLock => Key("Caps", Keyboard->Caps, 40, nil, 0), +Return => Key("Enter", '\n', 36, nil, 0), +Shift => Key("Shift", Keyboard->LShift, 45, nil, 0), +Esc => Key("Esc", 8r33, 21, nil, 0), +Ctrl => Key("Ctrl", Keyboard->LCtrl, 36, nil, 0), +Alt => Key("Alt", Keyboard->LAlt, 22, nil, 0), +Space => Key(" ", ' ', 140, nil, 0), +Space+1 => Key("Return", '\n', 36, nil, 0), +}; + +keys:= array[] of { + # unshifted + array[] of { + "Esc", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\\\\", "`", nil, + "Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "<-", nil, + "Ctrl", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Enter", nil, + "Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Shift", nil, + "Caps", "Alt", " ", "Alt", nil, + }, + + # shifted + array[] of { + "Esc", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "|", "~", nil, + "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "\\{", "\\}", "<-", nil, + "Ctrl", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Return", nil, + "Shift", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "Shift", nil, + "Caps", "Alt", " ", "Alt", nil, + }, +}; + +keyvals: array of array of int; +noexit := 0; + +init(ctxt: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + if (ctxt == nil) { + sys->fprint(sys->fildes(2), "keyboard: no window context\n"); + raise "fail:bad context"; + } + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient = load Tkclient Tkclient->PATH; + arg := load Arg Arg->PATH; + + taskbar := 0; + winopts := Tkclient->Hide; + arg->init(args); + while ((opt := arg->opt()) != 0) { + case opt { + 't' => + taskbar = 1; + 'e' => + noexit = 1; + winopts = 0; + * => + sys->fprint(sys->fildes(2), "usage: keyboard [-et]\n"); + raise "fail:usage"; + } + } + + sys->pctl(Sys->NEWPGRP, nil); + tkclient->init(); + + keyvals = array[] of { + array[len keys[0]] of int, + array[len keys[1]] of int, + }; + setindex(keys[0], keyvals[0], specials); + setindex(keys[1], keyvals[1], specials); + + + (t, wcmd) := tkclient->toplevel(ctxt, "", "Kbd", winopts); + cmd(t, ". configure -bd 0 -relief flat"); + + for(i := 0; i < len keys[0]; i++) + if(keys[0][i] != nil) + cmd(t, sys->sprint("button .b%d -takefocus 0 -font %s -width %d -height %d -bd %d -activebackground %s -text {%s} -command 'send keypress %d", + i, FONT, KEYSIZE, KEYSIZE, KEYBORDER, background, keys[0][i], keyvals[0][i])); + + for(i = 0; i < len specials; i++) { + k := specials[i]; + for(xl := k.x; xl != nil; xl = tl xl) + cmd(t, sys->sprint(".b%d configure -font %s -width %d", hd xl, SPECFONT, k.size)); + } + + # pack buttons in rows + i = 0; + for(j:=0; i < len keys[0]; j++){ + rowf := sys->sprint(".f%d", j); + cmd(t, "frame "+rowf); + cmd(t, sys->sprint("frame .pad%d -height %d", j, KEYGAP)); + if(ENDGAP){ + cmd(t, rowf + ".pad -width " + string ENDGAP); + cmd(t, "pack " + rowf + ".pad -side left"); + } + for(; keys[0][i] != nil; i++){ + label := keys[0][i]; + expand := label != "\\\\" && len label > 1; + cmd(t, "pack .b" + string i + " -in "+ rowf + " -side left -fill x -expand "+string expand); + if(keys[0][i+1] != nil && KEYGAP > 0){ + padf := sys->sprint("%s.pad%d", rowf, i); + cmd(t, "frame " + padf + " -width " + string KEYGAP); + cmd(t, "pack " + padf + " -side left"); + } + } + if(ENDGAP){ + padf := sys->sprint("%s.pad%d", rowf, i); + cmd(t, "frame " + padf + " -width " + string ENDGAP); + cmd(t, "pack " + padf + " -side left"); + } + i++; + } + nrow := j; + + # pack rows in frame + for(j = 0; j < nrow; j++) + cmd(t, sys->sprint("pack .f%d .pad%d -fill x -in .", j, j)); + + (w, h) := (int cmd(t, ". cget -width"), int cmd(t, ". cget -height")); + r := t.screenr; + off := (r.dx()-w)/2; + cmd(t, sys->sprint(". configure -x %d -y %d", r.min.x+off, r.max.y-h)); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "ptr" :: nil); + + spawn handle_keyclicks(t, wcmd, taskbar); +} + +setindex(keys: array of string, keyvals: array of int, spec: array of Key) +{ + for(i := 0; i < len keys; i++){ + if(keys[i] == nil) + continue; + val := keys[i][0]; + if(len keys[i] > 1 && val == '\\') + val = keys[i][1]; + for(j := 0; j < len spec; j++) + if(spec[j].name == keys[i]){ + if(!inlist(i, spec[j].x)) + spec[j].x = i :: spec[j].x; + val = spec[j].val; + break; + } + keyvals[i] = val; + } +} + +inlist(i: int, l: list of int): int +{ + for(; l != nil; l = tl l) + if(hd l == i) + return 1; + return 0; +} + +handle_keyclicks(t: ref Tk->Toplevel, wcmd: chan of string, taskbar: int) +{ + keypress := chan of string; + tk->namechan(t, keypress, "keypress"); + + if(taskbar) + tkclient->wmctl(t, "task"); + + cmd(t,"update"); + + collecting := 0; + collected := ""; + for(;;)alt { + k := <-keypress => + c := int k; + case c { + Keyboard->Caps => + active(t, Ctrl, 0); + active(t, Shift, 0); + active(t, Alt, 0); + active(t, CapsLock, -1); + redraw(t); + Keyboard->LShift => + active(t, Shift, -1); + redraw(t); + Keyboard->LCtrl => + active(t, Alt, 0); + active(t, Ctrl, -1); + active(t, Shift, 0); + redraw(t); + Keyboard->LAlt => + active(t, Alt, -1); + active(t, Ctrl, 0); + active(t, Shift, 0); + redraw(t); + if(specials[Alt].on){ + collecting = 1; + collected = ""; + }else + collecting = 0; + * => + if(collecting){ + collected[len collected] = c; + c = latin1(collected); + if(c < -1) + continue; + collecting = 0; + if(c == -1){ + for(i := 0; i < len collected; i++) + sendkey(t, collected[i]); + continue; + } + } + show := specials[Ctrl].on | specials[Alt].on | specials[Shift].on; + if(specials[Ctrl].on) + c &= 16r1F; + active(t, Ctrl, 0); + active(t, Alt, 0); + active(t, Shift, 0); + if(show) + redraw(t); + sendkey(t, c); + } + m := <-t.ctxt.ptr => + tk->pointer(t, *m); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-wcmd => + if (s == "exit" && noexit) + s = "task"; + tkclient->wmctl(t, s); + } +} + +sendkey(t: ref Tk->Toplevel, c: int) +{ + sys->fprint(t.ctxt.connfd, "key %d", c); +} + +active(t: ref Tk->Toplevel, keyno: int, on: int) +{ + key := specials[keyno:]; + if(on < 0) + key[0].on ^= 1; + else + key[0].on = on; + for(xl := key[0].x; xl != nil; xl = tl xl){ + col := background; + if(key[0].on) + col = "white"; + cmd(t, ".b"+string hd xl+" configure -bg "+col+ " -activebackground "+col); + } +} + +redraw(t: ref Tk->Toplevel) +{ + shifted := specials[Shift].on; + bank := keys[shifted]; + vals := keyvals[shifted]; + for(i:=0; i<len bank; i++) { + key := bank[i]; + val := vals[i]; + if(key != nil){ + if(specials[CapsLock].on && len key == 1){ + if(key[0]>='A' && key[0]<='Z') # true if also shifted + key[0] += 'a'-'A'; + else if(key[0] >= 'a' && key[0]<='z') + key[0] += 'A'-'a'; + val = key[0]; + } + cmd(t, ".b" + string i + " configure -text {" + key + "} -command 'send keypress " + string val); + } + } + cmd(t, "update"); +} + +# +# The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a +# prefix of latintab[j].ld only when j<i. +# +Cvlist: adt +{ + ld: string; # must be seen before using this conversion + si: string; # options for last input characters + so: string; # the corresponding Rune for each si entry +}; +latintab: array of Cvlist = array[] of { + (" ", " i", "␣ı"), + ("!~", "-=~", "≄≇≉"), + ("!", "!<=>?bmp", "¡≮≠≯‽⊄∉⊅"), + ("\"*", "IUiu", "ΪΫϊϋ"), + ("\"", "\"AEIOUYaeiouy", "¨ÄËÏÖÜŸäëïöüÿ"), + ("$*", "fhk", "ϕϑϰ"), + ("$", "BEFHILMRVaefglopv", "ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ"), + ("\'\"", "Uu", "Ǘǘ"), + ("\'", "\'ACEILNORSUYZacegilnorsuyz", "´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź"), + ("*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz", "∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ"), + ("+", "-O", "±⊕"), + (",", ",ACEGIKLNORSTUacegiklnorstu", "¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų"), + ("-*", "l", "ƛ"), + ("-", "+-2:>DGHILOTZbdghiltuz~", "∓ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂"), + (".", ".CEGILOZceglz", "·ĊĖĠİĿ⊙Żċėġŀż"), + ("/", "Oo", "Øø"), + ("1", "234568", "½⅓¼⅕⅙⅛"), + ("2", "-35", "ƻ⅔⅖"), + ("3", "458", "¾⅗⅜"), + ("4", "5", "⅘"), + ("5", "68", "⅚⅝"), + ("7", "8", "⅞"), + (":", ")-=", "☺÷≔"), + ("<!", "=~", "≨⋦"), + ("<", "-<=>~", "←«≤≶≲"), + ("=", ":<=>OV", "≕⋜≡⋝⊜⇒"), + (">!", "=~", "≩⋧"), + (">", "<=>~", "≷≥»≳"), + ("?", "!?", "‽¿"), + ("@\'", "\'", "ъ"), + ("@@", "\'EKSTYZekstyz", "ьЕКСТЫЗекстыз"), + ("@C", "Hh", "ЧЧ"), + ("@E", "Hh", "ЭЭ"), + ("@K", "Hh", "ХХ"), + ("@S", "CHch", "ЩШЩШ"), + ("@T", "Ss", "ЦЦ"), + ("@Y", "AEOUaeou", "ЯЕЁЮЯЕЁЮ"), + ("@Z", "Hh", "ЖЖ"), + ("@c", "h", "ч"), + ("@e", "h", "э"), + ("@k", "h", "х"), + ("@s", "ch", "щш"), + ("@t", "s", "ц"), + ("@y", "aeou", "яеёю"), + ("@z", "h", "ж"), + ("@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx", "АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх"), + ("A", "E", "Æ"), + ("C", "ACU", "⋂ℂ⋃"), + ("Dv", "Zz", "DŽDž"), + ("D", "-e", "Ð∆"), + ("G", "-", "Ǥ"), + ("H", "-H", "Ħℍ"), + ("I", "-J", "ƗIJ"), + ("L", "&-Jj|", "⋀ŁLJLj⋁"), + ("N", "JNj", "NJℕNj"), + ("O", "*+-./=EIcoprx", "⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗"), + ("P", "P", "ℙ"), + ("Q", "Q", "ℚ"), + ("R", "R", "ℝ"), + ("S", "123S", "¹²³§"), + ("T", "-u", "Ŧ⊨"), + ("V", "=", "⇐"), + ("Y", "R", "Ʀ"), + ("Z", "-ACSZ", "Ƶℤ"), + ("^", "ACEGHIJOSUWYaceghijosuwy", "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ"), + ("_\"", "AUau", "ǞǕǟǖ"), + ("_,", "Oo", "Ǭǭ"), + ("_.", "Aa", "Ǡǡ"), + ("_", "AEIOU_aeiou", "ĀĒĪŌŪ¯āēīōū"), + ("`\"", "Uu", "Ǜǜ"), + ("`", "AEIOUaeiou", "ÀÈÌÒÙàèìòù"), + ("a", "ben", "↔æ∠"), + ("b", "()+-0123456789=bknpqru", "₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•"), + ("c", "$Oagu", "¢©∩≅∪"), + ("dv", "z", "dž"), + ("d", "-adegz", "ð↓‡°†ʣ"), + ("e", "$lmns", "€⋯—–∅"), + ("f", "a", "∀"), + ("g", "$-r", "¤ǥ∇"), + ("h", "-v", "ℏƕ"), + ("i", "-bfjps", "ɨ⊆∞ij⊇∫"), + ("l", "\"$&\'-jz|", "“£∧‘łlj⋄∨"), + ("m", "iou", "µ∈×"), + ("n", "jo", "nj¬"), + ("o", "AOUaeiu", "Å⊚Ůåœƣů"), + ("p", "Odgrt", "℗∂¶∏∝"), + ("r", "\"\'O", "”’®"), + ("s", "()+-0123456789=abnoprstu", "⁽⁾⁺⁻⁰ⁱ⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑"), + ("t", "-efmsu", "ŧ∃∴™ς⊢"), + ("u", "-AEGIOUaegiou", "ʉĂĔĞĬŎŬ↑ĕğĭŏŭ"), + ("v\"", "Uu", "Ǚǚ"), + ("v", "ACDEGIKLNORSTUZacdegijklnorstuz", "ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž"), + ("w", "bknpqr", "♗♔♘♙♕♖"), + ("x", "O", "⊗"), + ("y", "$", "¥"), + ("z", "-", "ƶ"), + ("|", "Pp|", "Þþ¦"), + ("~!", "=", "≆"), + ("~", "-=AINOUainou~", "≃≅ÃĨÑÕŨãĩñõũ≈"), +}; + +# +# Given 5 characters k[0]..k[4], find the rune or return -1 for failure. +# +unicode(k: string): int +{ + c := 0; + for(i:=1; i<5; i++){ + r := k[i]; + c <<= 4; + if('0'<=r && r<='9') + c += r-'0'; + else if('a'<=r && r<='f') + c += 10 + r-'a'; + else if('A'<=r && r<='F') + c += 10 + r-'A'; + else + return -1; + } + return c; +} + +# +# Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for +# failure, or something < -1 if n is too small. In the latter case, the result +# is minus the required n. +# +latin1(k: string): int +{ + n := len k; + if(k[0] == 'X' || n>1 && k[0] == 'x' && k[1]!='O') # 'x' to avoid having to Shift as well + if(n>=5) + return unicode(k); + else + return -5; + for(i := 0; i < len latintab; i++){ + l := latintab[i]; + if(k[0] == l.ld[0]){ + if(n == 1) + return -2; + c := 0; + if(len l.ld == 1) + c = k[1]; + else if(l.ld[1] != k[1]) + continue; + else if(n == 2) + return -3; + else + c = k[2]; + for(p:=0; p < len l.si; p++) + if(l.si[p] == c && p < len l.so) + return l.so[p]; + return -1; + } + } + return -1; +} + +cmd(top: ref Tk->Toplevel, c: string): string +{ + e := tk->cmd(top, c); + if (e != nil && e[0] == '!') + sys->fprint(sys->fildes(2), "keyboard: tk error on '%s': %s\n", c, e); + return e; +} |
