_osd_keyboard.tcl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. namespace eval osd_keyboard {
  2. # KNOWN ISSUES/TODO:
  3. # * Shouldn't use the keymatrix command, but the 'type' command for all keys
  4. # that are not on the same position in the matrix for all machines
  5. # * lots more? :P
  6. variable is_dingux [string match dingux "[openmsx_info platform]"]
  7. #init vars
  8. variable mouse1_pressed false
  9. variable key_pressed -1
  10. variable key_selected -1
  11. variable keys_held
  12. variable row_starts
  13. #init colors
  14. variable key_color "0x999999c0 0xbbbbbbc0 0xddddddc0 0xffffffc0"
  15. variable key_pressed_color "0x994400c0 0xbb5500c0 0xdd6600c0 0xff8800c0"
  16. variable key_background_color 0x00000080
  17. variable key_hold_color "0x009933f0 0x00bb44f0 0x00dd66f0 0x00ff88ff"
  18. variable key_select_color "0x999933f0 0xbbbb44f0 0xdddd66f0 0xffff88f0"
  19. variable key_edge_color 0xaaaaaaa0
  20. variable key_edge_color_select 0xaaaa00a0
  21. variable key_edge_color_hold 0x00aa44a0
  22. variable key_edge_color_pressed 0xaa4444a0
  23. # Keyboard layout constants.
  24. variable key_height 16
  25. variable key_hspace 2
  26. variable key_vspace 2
  27. variable board_hborder 4
  28. variable board_vborder 4
  29. proc toggle_osd_keyboard {} {
  30. if {[osd exists kb]} {
  31. disable_osd_keyboard
  32. } else {
  33. enable_osd_keyboard
  34. }
  35. }
  36. proc disable_osd_keyboard {} {
  37. osd destroy kb
  38. deactivate_input_layer osd_keyboard
  39. #reset keyboard matrix
  40. for {set i 0} {$i <= 8} {incr i} {
  41. keymatrixup $i 255
  42. }
  43. namespace eval ::osd_control {unset close}
  44. }
  45. proc enable_osd_keyboard {} {
  46. variable is_dingux
  47. variable mouse1_pressed false
  48. variable keys_held [list]
  49. variable row_starts [list]
  50. variable key_color
  51. variable key_background_color
  52. variable key_edge_color
  53. # first remove other OSD controlled widgets (like the osd menu)
  54. if {[info exists ::osd_control::close]} {
  55. eval $::osd_control::close
  56. }
  57. # and tell how to close this widget
  58. namespace eval ::osd_control {set close ::osd_keyboard::disable_osd_keyboard}
  59. #bind stuff
  60. bind -layer osd_keyboard "mouse button1 down" {osd_keyboard::key_handler true}
  61. bind -layer osd_keyboard "mouse button1 up" {osd_keyboard::key_handler false}
  62. bind -layer osd_keyboard "mouse button3 down" {osd_keyboard::key_hold_toggle false}
  63. bind -layer osd_keyboard "OSDcontrol UP PRESS" -repeat {osd_keyboard::selection_row -1}
  64. bind -layer osd_keyboard "OSDcontrol DOWN PRESS" -repeat {osd_keyboard::selection_row +1}
  65. bind -layer osd_keyboard "OSDcontrol LEFT PRESS" -repeat {osd_keyboard::selection_col -1}
  66. bind -layer osd_keyboard "OSDcontrol RIGHT PRESS" -repeat {osd_keyboard::selection_col +1}
  67. if {$is_dingux} {
  68. bind -layer osd_keyboard "keyb LCTRL,PRESS" {osd_keyboard::selection_press }
  69. bind -layer osd_keyboard "keyb LCTRL,RELEASE" {osd_keyboard::selection_release}
  70. bind -layer osd_keyboard "keyb LALT" {osd_keyboard::key_hold_toggle true }
  71. } else {
  72. bind -layer osd_keyboard "OSDcontrol A PRESS" {osd_keyboard::selection_press }
  73. bind -layer osd_keyboard "OSDcontrol A RELEASE" {osd_keyboard::selection_release}
  74. bind -layer osd_keyboard "OSDcontrol B PRESS" {osd_keyboard::key_hold_toggle true }
  75. }
  76. activate_input_layer osd_keyboard -blocking
  77. #Define Keyboard (how do we handle the shift/ctrl/graph command?)
  78. set key_basewidth 18
  79. set rows {
  80. "F1*26|F2*26|F3*26|F4*26|F5*26|null*8|Select*26|Stop*26|null*8|Home*26|Ins*26|Del*26" \
  81. "Esc|1|2|3|4|5|6|7|8|9|0|-|=|\\|BS" \
  82. "Tab*28|Q|W|E|R|T|Y|U|I|O|P|\[|]|Return*28" \
  83. "Ctrl*32|A|S|D|F|G|H|J|K|L|;|'|`|<--*24" \
  84. "Shift*40|Z|X|C|V|B|N|M|,|.|/|Acc|Shift*36" \
  85. "null*40|Cap|Grp|Space*158|Cod"
  86. }
  87. # Keyboard layout constants.
  88. variable key_height
  89. variable key_hspace
  90. variable key_vspace
  91. variable board_hborder
  92. variable board_vborder
  93. # Create widgets.
  94. set board_width \
  95. [expr {15 * $key_basewidth + 14 * $key_hspace + 2 * $board_hborder}]
  96. set board_height \
  97. [expr {6 * $key_height + 5 * $key_vspace + 2 * $board_vborder}]
  98. osd create rectangle kb \
  99. -x [expr {(320 - $board_width) / 2}] \
  100. -y 4 \
  101. -w $board_width \
  102. -h $board_height -scaled true -rgba $key_background_color
  103. set keycount 0
  104. for {set y 0} {$y <= [llength $rows]} {incr y} {
  105. set y_base [expr {$board_vborder + $y * ($key_height + $key_vspace)}]
  106. lappend row_starts $keycount
  107. set x $board_hborder
  108. foreach {keys} [split [lindex $rows $y] "|"] {
  109. lassign [split $keys "*"] key_text key_width
  110. if {$key_width < 1} {set key_width $key_basewidth}
  111. if {$key_text ne "null"} {
  112. set key_y $y_base
  113. set key_h $key_height
  114. set bordersize 1
  115. if {$key_text eq "Return"} {
  116. set bordersize 0
  117. } elseif {$key_text eq "<--"} {
  118. set bordersize 0
  119. incr key_y -$key_vspace
  120. incr key_h $key_vspace
  121. }
  122. osd create rectangle kb.$keycount \
  123. -x $x -y $key_y \
  124. -w $key_width -h $key_h \
  125. -rgba $key_color \
  126. -bordersize $bordersize \
  127. -borderrgba $key_edge_color
  128. osd create text kb.$keycount.text \
  129. -x 1.1 \
  130. -y 0.1 \
  131. -text $key_text \
  132. -size 8
  133. incr keycount
  134. }
  135. set x [expr {$x + $key_width + $key_hspace}]
  136. }
  137. }
  138. variable key_selected
  139. if {$key_selected == -1} {
  140. # Select the key in the middle of the keyboard.
  141. key_select [key_at_coord \
  142. [expr {$board_width / 2}] \
  143. [expr {$board_vborder + 3.5 * ($key_height + $key_vspace)}]]
  144. } else {
  145. update_key_color $key_selected
  146. }
  147. return ""
  148. }
  149. proc selection_row {delta} {
  150. variable row_starts
  151. variable key_selected
  152. # Note: Delta as bias makes sure that if a key is exactly in the middle
  153. # above/below two other keys, an up/down or down/up sequence will
  154. # end on the same key it started on.
  155. set x [expr {\
  156. [osd info kb.$key_selected -x] + [osd info kb.$key_selected -w] / 2 \
  157. + $delta}]
  158. set num_rows [expr {[llength $row_starts] - 1}]
  159. set row [row_for_key $key_selected]
  160. while {1} {
  161. # Determine new row.
  162. incr row $delta
  163. if {$row < 0} {
  164. set row [expr {$num_rows - 1}]
  165. } elseif {$row >= $num_rows} {
  166. set row 0
  167. }
  168. # Get key at new coordinates.
  169. set first_key [lindex $row_starts $row]
  170. set y [expr {\
  171. [osd info kb.$first_key -y] + [osd info kb.$first_key -h] / 2}]
  172. set new_selection [key_at_coord $x $y]
  173. if {$new_selection >= 0} {
  174. break
  175. }
  176. }
  177. key_select $new_selection
  178. }
  179. proc selection_col {delta} {
  180. variable row_starts
  181. variable key_selected
  182. # Figure out first and last key of current row.
  183. set row [row_for_key $key_selected]
  184. set row_start [lindex $row_starts $row]
  185. set row_end [lindex $row_starts [expr {$row + 1}]]
  186. # Move left or right.
  187. set new_selection [expr {$key_selected + $delta}]
  188. if {$new_selection < $row_start} {
  189. set new_selection [expr {$row_end - 1}]
  190. } elseif {$new_selection >= $row_end} {
  191. set new_selection $row_start
  192. }
  193. key_select $new_selection
  194. }
  195. proc selection_press {} {
  196. variable key_selected
  197. key_press $key_selected
  198. }
  199. proc selection_release {} {
  200. key_release
  201. }
  202. proc key_press {key_id} {
  203. variable key_pressed
  204. set key_pressed $key_id
  205. key_matrix $key_id down
  206. update_key_color $key_id
  207. }
  208. proc key_release {} {
  209. variable key_pressed
  210. variable keys_held
  211. if {$key_pressed == -1} {
  212. return
  213. }
  214. set key_id $key_pressed
  215. set key_pressed -1
  216. set index [lsearch -exact $keys_held $key_id]
  217. if {$index != -1} {
  218. set keys_held [lreplace $keys_held $index $index]
  219. }
  220. key_matrix $key_id up
  221. update_key_color $key_id
  222. }
  223. proc key_select {key_id} {
  224. variable key_selected
  225. set old_selected $key_selected
  226. set key_selected $key_id
  227. update_key_color $key_selected
  228. update_key_color $old_selected
  229. }
  230. proc row_for_key {key_id} {
  231. variable row_starts
  232. for {set row 0} {$row < [llength $row_starts] - 1} {incr row} {
  233. set row_start [lindex $row_starts $row]
  234. set row_end [lindex $row_starts [expr {$row + 1}]]
  235. if {$row_start <= $key_id && $key_id < $row_end} {
  236. return $row
  237. }
  238. }
  239. return -1
  240. }
  241. proc update_key_color {key_id} {
  242. variable key_selected
  243. variable key_pressed
  244. variable keys_held
  245. variable key_color
  246. variable key_select_color
  247. variable key_pressed_color
  248. variable key_hold_color
  249. variable key_edge_color
  250. variable key_edge_color_select
  251. variable key_edge_color_hold
  252. variable key_edge_color_pressed
  253. if {$key_id < 0} {
  254. return
  255. } elseif {$key_id == $key_pressed} {
  256. set color $key_pressed_color
  257. set edge_color $key_edge_color_pressed
  258. } elseif {$key_id == $key_selected} {
  259. set color $key_select_color
  260. set edge_color $key_edge_color_select
  261. } elseif {$key_id in $keys_held} {
  262. set color $key_hold_color
  263. set edge_color $key_edge_color_hold
  264. } else {
  265. set color $key_color
  266. set edge_color $key_edge_color
  267. }
  268. osd configure kb.$key_id -rgba $color -borderrgba $edge_color
  269. }
  270. proc key_at_coord {x y} {
  271. variable key_hspace
  272. variable key_height
  273. variable key_vspace
  274. variable board_vborder
  275. variable row_starts
  276. set row [expr {int(floor( \
  277. ($y - $board_vborder + $key_vspace / 2) / ($key_height + $key_vspace) \
  278. ))}]
  279. if {$row >= 0 && $row < [llength $row_starts] - 1} {
  280. set row_start [lindex $row_starts $row]
  281. set row_end [lindex $row_starts [expr {$row + 1}]]
  282. for {set key_id $row_start} {$key_id < $row_end} {incr key_id} {
  283. set relx [expr {$x - [osd info kb.$key_id -x] + $key_hspace / 2}]
  284. if {$relx >= 0 && $relx < [osd info kb.$key_id -w] + $key_hspace} {
  285. return $key_id
  286. }
  287. }
  288. }
  289. return -1
  290. }
  291. proc key_at_mouse {} {
  292. lassign [osd info kb -mousecoord] x y
  293. key_at_coord [expr {$x * [osd info kb -w]}] \
  294. [expr {$y * [osd info kb -h]}]
  295. }
  296. proc key_hold_toggle {at_selection} {
  297. variable keys_held
  298. variable key_selected
  299. if {$at_selection} {
  300. set key_id $key_selected
  301. } else {
  302. set key_id [key_at_mouse]
  303. }
  304. if {$key_id >= 0} {
  305. set index [lsearch -exact $keys_held $key_id]
  306. if {$index == -1} {
  307. key_matrix $key_id down
  308. lappend keys_held $key_id
  309. } else {
  310. key_matrix $key_id up
  311. set keys_held [lreplace $keys_held $index $index]
  312. }
  313. update_key_color $key_id
  314. }
  315. }
  316. proc key_handler {mouse_state} {
  317. if {$mouse_state} {
  318. set key_id [key_at_mouse]
  319. if {$key_id >= 0} {
  320. key_press $key_id
  321. key_select $key_id
  322. }
  323. } else {
  324. key_release
  325. }
  326. }
  327. proc key_matrix {keynum state} {
  328. set key [string trim "[osd info kb.$keynum.text -text]"]
  329. set km keymatrix$state
  330. #info from http://map.grauw.nl/articles/keymatrix.php (thanks Grauw)
  331. switch -- $key {
  332. "0" {$km 0 1}
  333. "1" {$km 0 2}
  334. "2" {$km 0 4}
  335. "3" {$km 0 8}
  336. "4" {$km 0 16}
  337. "5" {$km 0 32}
  338. "6" {$km 0 64}
  339. "7" {$km 0 128}
  340. "8" {$km 1 1}
  341. "9" {$km 1 2}
  342. "-" {$km 1 4}
  343. "=" {$km 1 8}
  344. "\\" {$km 1 16}
  345. "\[" {$km 1 32}
  346. "\]" {$km 1 64}
  347. ";" {$km 1 128}
  348. "'" {$km 2 1}
  349. "`" {$km 2 2}
  350. "," {$km 2 4}
  351. "." {$km 2 8}
  352. "/" {$km 2 16}
  353. "Acc" {$km 2 32}
  354. "A" {$km 2 64}
  355. "B" {$km 2 128}
  356. "C" {$km 3 1}
  357. "D" {$km 3 2}
  358. "E" {$km 3 4}
  359. "F" {$km 3 8}
  360. "G" {$km 3 16}
  361. "H" {$km 3 32}
  362. "I" {$km 3 64}
  363. "J" {$km 3 128}
  364. "K" {$km 4 1}
  365. "L" {$km 4 2}
  366. "M" {$km 4 4}
  367. "N" {$km 4 8}
  368. "O" {$km 4 16}
  369. "P" {$km 4 32}
  370. "Q" {$km 4 64}
  371. "R" {$km 4 128}
  372. "S" {$km 5 1}
  373. "T" {$km 5 2}
  374. "U" {$km 5 4}
  375. "V" {$km 5 8}
  376. "W" {$km 5 16}
  377. "X" {$km 5 32}
  378. "Y" {$km 5 64}
  379. "Z" {$km 5 128}
  380. "Shift" {$km 6 1}
  381. "Ctrl" {$km 6 2}
  382. "Grp" {$km 6 4}
  383. "Cap" {$km 6 8}
  384. "Cod" {$km 6 16}
  385. "F1" {$km 6 32}
  386. "F2" {$km 6 64}
  387. "F3" {$km 6 128}
  388. "F4" {$km 7 1}
  389. "F5" {$km 7 2}
  390. "Esc" {$km 7 4}
  391. "Tab" {$km 7 8}
  392. "Stop" {$km 7 16}
  393. "BS" {$km 7 32}
  394. "Select" {$km 7 64}
  395. "Return" {$km 7 128}
  396. "<--" {$km 7 128}
  397. "Space" {$km 8 1}
  398. "Home" {$km 8 2}
  399. "Ins" {$km 8 4}
  400. "Del" {$km 8 8}
  401. }
  402. #cursor keys etc (not implemented... should we?)
  403. #numeric keyboard?
  404. }
  405. namespace export toggle_osd_keyboard
  406. };# namespace osd_keyboard
  407. namespace import osd_keyboard::*