Author Topic: Virtual Keyboard  (Read 6444 times)

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer. Press here if you would like to see it.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Virtual Keyboard
« on: February 05, 2021, 01:11:19 am »
And here's my newest little toy for folks to have fun and play around with -- a virtual keyboard creator!

Code: QB64: [Select]
  1. TYPE Keyboard_Internal_Type
  2.     AS LONG In_Use, Is_Hidden, Handle, Hardware_Handle, Xoffset, Yoffset, Xsize, Ysize
  3. DIM SHARED Virtual_KB(0 TO 10) AS Keyboard_Internal_Type
  4. DIM SHARED Keyboard_Values(0 TO 10, 0 TO 10, 0 TO 255) AS LONG '11 keyboards of up to 11 rows of 256 keys
  5.  
  6. SCREEN _NEWIMAGE(800, 600, 32)
  7.  
  8. DIM My_Keyboard(5) AS STRING
  9. My_Keyboard(0) = CHR$(0) + "27,ESC" + STRING$(2,0) + "15104,F1" + STRING$(2,0) + "15360,F2" + _
  10.     STRING$(2,0) + "15616,F3" + STRING$(2,0) + "15872,F4" + STRING$(2,0) + "16128,F5" + _
  11.     STRING$(2,0) + "16384,F6" + STRING$(2,0) + "16640,F7" + STRING$(2,0) + "16896,F8" + _
  12.     STRING$(2,0) + "17152,F9" + STRING$(2,0) + "17408,F10" + STRING$(2,0) + "34048,F11" + _
  13.     STRING$(2,0) + "34304,F12" + CHR$(0)
  14. My_Keyboard(1) = "`1234567890-=" + CHR$(0) + "19200,BKSP" + CHR$(0)
  15. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "QWERTYUIOP[]\"
  16. My_Keyboard(3) = CHR$(0) + "100301,TOGGLE" + CHR$(0) + "ASDFGHJKL;'" + CHR$(0) + "13,ENTER" + CHR$(0)
  17. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "ZXCVBNM,./" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  18. My_Keyboard(5) = CHR$(0) + "100306,CTRL" + STRING$(2,0) + "0,WIN" + STRING$(2,0) + "100308,ALT" + _
  19.     STRING$(2,0) + "32,SPACE" + STRING$(2,0) + "100307,ALT" + STRING$(2,0) + "0,WIN" + STRING$(2,0) + "0,MENU" + _
  20.     STRING$(2,0) + "100305,CTRL" +  CHR$(0)
  21.  
  22.  
  23. FullsizeKB1 = Create_KB(My_Keyboard(), 16, 50, 30)
  24.  
  25.  
  26.  
  27. My_Keyboard(1) = "~!@#$%^&*()_+" + CHR$(0) + "19200,BKSP" + CHR$(0)
  28. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "qwertyuiop{}|"
  29. My_Keyboard(3) = CHR$(0) + "100301,TOGGLE" + CHR$(0) + "asdfghjkl:" + CHR$(34) + CHR$(0) + "13,ENTER" + CHR$(0)
  30. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "zxcvbnm<>?" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  31. FullsizeKB2 = Create_KB(My_Keyboard(), 16, 50, 30)
  32.  
  33.  
  34. Keyboard_In_Use = FullsizeKB1
  35.     Display_KB Keyboard_In_Use, 10, 100, 1
  36.     WHILE _MOUSEINPUT: WEND 'must update mouse buffer before reading virtual keyboard
  37.     k = check_KB(Keyboard_In_Use)
  38.     SELECT CASE k
  39.         CASE 100301 'swap keyboards, rather than having a CAPS LOCK key
  40.             IF Keyboard_In_Use = FullsizeKB1 THEN
  41.                 Keyboard_In_Use = FullsizeKB2
  42.             ELSE
  43.                 Keyboard_In_Use = FullsizeKB1
  44.             END IF
  45.             _DELAY .2
  46.         CASE IS <> 0
  47.             PRINT k;
  48.             IF k > 0 AND k < 255 THEN PRINT CHR$(k);
  49.             PRINT ,
  50.             _DELAY .2 'delay is so we don't spam clicks from mousedown events
  51.     END SELECT
  52.  
  53.     _DISPLAY
  54.     _LIMIT 30
  55.  
  56. FUNCTION check_KB& (Which)
  57.     STATIC AS INTEGER x, y 'so as to not interfer with any global variables
  58.     x = _MOUSEX - Virtual_KB(Which).Xoffset
  59.     y = _MOUSEY - Virtual_KB(Which).Yoffset
  60.  
  61.     yon = x \ Virtual_KB(Which).Xsize
  62.     xon = y \ Virtual_KB(Which).Ysize
  63.     IF xon >= 0 AND xon <= 10 AND yon >= 0 AND yon <= 255 THEN
  64.         IF _MOUSEBUTTON(1) THEN check_KB& = Keyboard_Values(Which, xon, yon)
  65.     END IF
  66.  
  67.  
  68.  
  69.  
  70. SUB Display_KB (Which AS INTEGER, Xwhere AS INTEGER, Ywhere AS INTEGER, style AS INTEGER)
  71.     IF Virtual_KB(Which).In_Use = 0 THEN EXIT SUB
  72.     IF Virtual_KB(Which).Is_Hidden THEN EXIT SUB
  73.     Virtual_KB(Which).Xoffset = Xwhere
  74.     Virtual_KB(Which).Yoffset = Ywhere
  75.     IF style THEN 'we want a hardware image
  76.         _PUTIMAGE (Xwhere, Ywhere), Virtual_KB(Which).Hardware_Handle
  77.     ELSE
  78.         _PUTIMAGE (Xwhere, Ywhere), Virtual_KB(Which).Handle
  79.     END IF
  80.  
  81. FUNCTION Create_KB (KB() AS STRING, Font AS LONG, Xsize AS LONG, Ysize AS LONG)
  82.     STATIC AS LONG D, S 'stored as static so as to not interfer with any globals of the same name.
  83.     D = _DEST: S = _SOURCE
  84.  
  85.     FOR i = 0 TO 10
  86.         IF Virtual_KB(i).In_Use = 0 THEN
  87.             Virtual_KB(i).In_Use = -1
  88.             Virtual_KB(i).Xsize = Xsize
  89.             Virtual_KB(i).Ysize = Ysize
  90.  
  91.             Create_KB = i
  92.             EXIT FOR
  93.         END IF
  94.     NEXT
  95.     IF i = 11 THEN
  96.         CLS
  97.         PRINT "Too many keyboards registered in use at the same time!  Can not create a new one."
  98.         END
  99.     END IF
  100.     This_KB = i
  101.  
  102.     keyboard_image = _NEWIMAGE(4096, 4096, 32)
  103.     _DEST keyboard_image: _SOURCE keyboard_image
  104.     _FONT Font
  105.  
  106.     'now build the keyboard
  107.     FOR i = 0 TO UBOUND(KB)
  108.         top = (i - l) * Ysize + Ypos
  109.         count = 0
  110.         FOR j = 1 TO LEN(KB(i))
  111.             left = (count) * Xsize + Xpos
  112.             count = count + 1
  113.             repeat = 1
  114.             c = ASC(KB(i), j): out$ = ""
  115.             IF c = 0 THEN
  116.                 'look for the comma
  117.                 comma_position = INSTR(j, KB(i), ",")
  118.                 IF comma_position THEN 'we have a value, label
  119.                     value$ = MID$(KB(i), j + 1, comma_position - j - 1)
  120.                     c = VAL(value$)
  121.                     j = comma_position + 1
  122.                 ELSE 'cry loud and hard so we can sort it out while programming our keyboard layout
  123.                     scream_and_die:
  124.                     SLEEP
  125.                     CLS
  126.                     PRINT "You have an invalid keyboard layout!"
  127.                     END
  128.                 END IF
  129.  
  130.                 end_position = INSTR(j, KB(i), CHR$(0))
  131.                 IF end_position THEN 'we're extracting the label
  132.                     out$ = MID$(KB(i), j, end_position - j)
  133.                     repeat = ASC(out$, LEN(out$))
  134.                     IF repeat > 0 AND repeat < 9 THEN
  135.                         out$ = LEFT$(out$, LEN(out$) - 1)
  136.                     ELSE
  137.                         repeat = 1
  138.                     END IF
  139.                     j = end_position
  140.                 ELSE
  141.                     GOTO scream_and_die
  142.                 END IF
  143.             END IF
  144.             LINE (left, top)-STEP(Xsize * repeat, Ysize), -1, B
  145.             IF left + Xsize * repeat > max_width THEN max_width = left + Xsize * repeat
  146.             IF top + Ysize > max_height THEN max_height = top + Ysize
  147.             IF c < 256 AND out$ = "" THEN out$ = CHR$(c)
  148.             _PRINTSTRING (left + (Xsize * repeat - _FONTWIDTH * LEN(out$)) / 2, top + (Ysize - _FONTHEIGHT) / 2), out$
  149.  
  150.             DO UNTIL repeat = 1
  151.                 Keyboard_Values(This_KB, i, count - 1) = c
  152.                 count = count + 1
  153.                 repeat = repeat - 1
  154.             LOOP
  155.             Keyboard_Values(This_KB, i, count - 1) = c
  156.         NEXT
  157.     NEXT
  158.  
  159.     'resize to proper size to put upon the screen
  160.     Virtual_KB(This_KB).Handle = _NEWIMAGE(max_width + 1, max_height + 1, 32)
  161.     _PUTIMAGE (0, 0)-(max_width, max_height), keyboard_image, Virtual_KB(This_KB).Handle, (0, 0)-(max_width, max_height)
  162.     Virtual_KB(This_KB).Hardware_Handle = _COPYIMAGE(Virtual_KB(This_KB).Handle, 33)
  163.     _FREEIMAGE keyboard_image
  164.  
  165.     clean_exit:
  166.     _SOURCE S: _DEST D

I'll let the demo speak for itself, but if anyone has questions or ideas, I'd be more than happy to hear them.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Virtual Keyboard
« Reply #1 on: February 05, 2021, 01:33:36 am »
And here's a keyboard that can actually DO SOMETHING for you.  It's a simple  calculator!

Code: QB64: [Select]
  1. TYPE Keyboard_Internal_Type
  2.     AS LONG In_Use, Is_Hidden, Handle, Hardware_Handle, Xoffset, Yoffset, Xsize, Ysize
  3. DIM SHARED Virtual_KB(0 TO 10) AS Keyboard_Internal_Type
  4. DIM SHARED Keyboard_Values(0 TO 10, 0 TO 10, 0 TO 255) AS LONG '11 keyboards of up to 11 rows of 256 keys
  5. REDIM SHARED OName(0) AS STRING 'Operation Name
  6. REDIM SHARED PL(0) AS INTEGER 'Priority Level
  7. REDIM SHARED PP_TypeMod(0) AS STRING, PP_ConvertedMod(0) AS STRING 'Prepass Name Conversion variables.
  8.  
  9.  
  10. SCREEN _NEWIMAGE(640, 480, 32)
  11.  
  12. DIM My_Keyboard(4) AS STRING
  13. My_Keyboard(0) = "^/*-"
  14. My_Keyboard(1) = "789+"
  15. My_Keyboard(2) = "456" + CHR$(0) + "27,End" + CHR$(0)
  16. My_Keyboard(3) = "123" + CHR$(0) + "8,Del" + CHR$(0)
  17. My_Keyboard(4) = "0." + CHR$(0) + "13,Enter" + CHR$(0)
  18.  
  19.  
  20. Numpad = Create_KB(My_Keyboard(), 16, 30, 30)
  21.  
  22.  
  23.  
  24.  
  25.  
  26. ypos = 1
  27.     Display_KB Numpad, 250, 150, 1
  28.     WHILE _MOUSEINPUT: WEND 'must update mouse buffer before reading virtual keyboard
  29.     k = check_KB(Numpad)
  30.  
  31.     SELECT CASE k
  32.         CASE 8
  33.             out$ = LEFT$(out$, LEN(out$) - 1)
  34.             _DELAY .2
  35.         CASE 13
  36.             LOCATE ypos, 1: PRINT out$; " = "; Evaluate_Expression(out$)
  37.             out$ = ""
  38.             ypos = ypos + 1
  39.             _DELAY .2
  40.         CASE 27
  41.             SYSTEM
  42.         CASE IS <> 0
  43.             out$ = out$ + CHR$(k)
  44.             _DELAY .2
  45.     END SELECT
  46.     LOCATE ypos, 1: PRINT out$; " "; answer$
  47.     answer$ = ""
  48.  
  49.     _DISPLAY
  50.     _LIMIT 30
  51.  
  52. FUNCTION check_KB& (Which)
  53.     STATIC AS INTEGER x, y 'so as to not interfer with any global variables
  54.     x = _MOUSEX - Virtual_KB(Which).Xoffset
  55.     y = _MOUSEY - Virtual_KB(Which).Yoffset
  56.  
  57.     yon = x \ Virtual_KB(Which).Xsize
  58.     xon = y \ Virtual_KB(Which).Ysize
  59.     IF xon >= 0 AND xon <= 10 AND yon >= 0 AND yon <= 255 THEN
  60.         IF _MOUSEBUTTON(1) THEN check_KB& = Keyboard_Values(Which, xon, yon)
  61.     END IF
  62.  
  63.  
  64.  
  65.  
  66. SUB Display_KB (Which AS INTEGER, Xwhere AS INTEGER, Ywhere AS INTEGER, style AS INTEGER)
  67.     IF Virtual_KB(Which).In_Use = 0 THEN EXIT SUB
  68.     IF Virtual_KB(Which).Is_Hidden THEN EXIT SUB
  69.     Virtual_KB(Which).Xoffset = Xwhere
  70.     Virtual_KB(Which).Yoffset = Ywhere
  71.     IF style THEN 'we want a hardware image
  72.         _PUTIMAGE (Xwhere, Ywhere), Virtual_KB(Which).Hardware_Handle
  73.     ELSE
  74.         _PUTIMAGE (Xwhere, Ywhere), Virtual_KB(Which).Handle
  75.     END IF
  76.  
  77. FUNCTION Create_KB (KB() AS STRING, Font AS LONG, Xsize AS LONG, Ysize AS LONG)
  78.     STATIC AS LONG D, S 'stored as static so as to not interfer with any globals of the same name.
  79.     D = _DEST: S = _SOURCE
  80.  
  81.     FOR i = 0 TO 10
  82.         IF Virtual_KB(i).In_Use = 0 THEN
  83.             Virtual_KB(i).In_Use = -1
  84.             Virtual_KB(i).Xsize = Xsize
  85.             Virtual_KB(i).Ysize = Ysize
  86.  
  87.             Create_KB = i
  88.             EXIT FOR
  89.         END IF
  90.     NEXT
  91.     IF i = 11 THEN
  92.         CLS
  93.         PRINT "Too many keyboards registered in use at the same time!  Can not create a new one."
  94.         END
  95.     END IF
  96.     This_KB = i
  97.  
  98.     keyboard_image = _NEWIMAGE(4096, 4096, 32)
  99.     _DEST keyboard_image: _SOURCE keyboard_image
  100.     _FONT Font
  101.  
  102.     'now build the keyboard
  103.     FOR i = 0 TO UBOUND(KB)
  104.         top = (i - l) * Ysize + Ypos
  105.         count = 0
  106.         FOR j = 1 TO LEN(KB(i))
  107.             left = (count) * Xsize + Xpos
  108.             count = count + 1
  109.             repeat = 1
  110.             c = ASC(KB(i), j): out$ = ""
  111.             IF c = 0 THEN
  112.                 'look for the comma
  113.                 comma_position = INSTR(j, KB(i), ",")
  114.                 IF comma_position THEN 'we have a value, label
  115.                     value$ = MID$(KB(i), j + 1, comma_position - j - 1)
  116.                     c = VAL(value$)
  117.                     j = comma_position + 1
  118.                 ELSE 'cry loud and hard so we can sort it out while programming our keyboard layout
  119.                     scream_and_die:
  120.                     SLEEP
  121.                     CLS
  122.                     PRINT "You have an invalid keyboard layout!"
  123.                     END
  124.                 END IF
  125.  
  126.                 end_position = INSTR(j, KB(i), CHR$(0))
  127.                 IF end_position THEN 'we're extracting the label
  128.                     out$ = MID$(KB(i), j, end_position - j)
  129.                     repeat = ASC(out$, LEN(out$))
  130.                     IF repeat > 0 AND repeat < 9 THEN
  131.                         out$ = LEFT$(out$, LEN(out$) - 1)
  132.                     ELSE
  133.                         repeat = 1
  134.                     END IF
  135.                     j = end_position
  136.                 ELSE
  137.                     GOTO scream_and_die
  138.                 END IF
  139.             END IF
  140.             LINE (left, top)-STEP(Xsize * repeat, Ysize), -1, B
  141.             IF left + Xsize * repeat > max_width THEN max_width = left + Xsize * repeat
  142.             IF top + Ysize > max_height THEN max_height = top + Ysize
  143.             IF c < 256 AND out$ = "" THEN out$ = CHR$(c)
  144.             _PRINTSTRING (left + (Xsize * repeat - _FONTWIDTH * LEN(out$)) / 2, top + (Ysize - _FONTHEIGHT) / 2), out$
  145.  
  146.             DO UNTIL repeat = 1
  147.                 Keyboard_Values(This_KB, i, count - 1) = c
  148.                 count = count + 1
  149.                 repeat = repeat - 1
  150.             LOOP
  151.             Keyboard_Values(This_KB, i, count - 1) = c
  152.         NEXT
  153.     NEXT
  154.  
  155.     'resize to proper size to put upon the screen
  156.     Virtual_KB(This_KB).Handle = _NEWIMAGE(max_width + 1, max_height + 1, 32)
  157.     _PUTIMAGE (0, 0)-(max_width, max_height), keyboard_image, Virtual_KB(This_KB).Handle, (0, 0)-(max_width, max_height)
  158.     Virtual_KB(This_KB).Hardware_Handle = _COPYIMAGE(Virtual_KB(This_KB).Handle, 33)
  159.     _FREEIMAGE keyboard_image
  160.  
  161.     clean_exit:
  162.     _SOURCE S: _DEST D
  163.  
  164. 'Steve Subs/Functins for _MATH support with CONST
  165. FUNCTION Evaluate_Expression$ (e$)
  166.     t$ = e$ 'So we preserve our original data, we parse a temp copy of it
  167.     PreParse t$
  168.  
  169.  
  170.     IF LEFT$(t$, 5) = "ERROR" THEN Evaluate_Expression$ = t$: EXIT FUNCTION
  171.  
  172.     'Deal with brackets first
  173.     EXP$ = "(" + t$ + ")" 'Starting and finishing brackets for our parse routine.
  174.  
  175.     DO
  176.         Eval_E = INSTR(EXP$, ")")
  177.         IF Eval_E > 0 THEN
  178.             c = 0
  179.             DO UNTIL Eval_E - c <= 0
  180.                 c = c + 1
  181.                 IF Eval_E THEN
  182.                     IF MID$(EXP$, Eval_E - c, 1) = "(" THEN EXIT DO
  183.                 END IF
  184.             LOOP
  185.             s = Eval_E - c + 1
  186.             IF s < 1 THEN Evaluate_Expression$ = "ERROR -- BAD () Count": EXIT SUB
  187.             eval$ = " " + MID$(EXP$, s, Eval_E - s) + " " 'pad with a space before and after so the parser can pick up the values properly.
  188.  
  189.             'PRINT "Before ParseExpression: "; eval$
  190.             ParseExpression eval$
  191.             'PRINT "After ParseExpression: "; eval$
  192.             eval$ = LTRIM$(RTRIM$(eval$))
  193.             IF LEFT$(eval$, 5) = "ERROR" THEN Evaluate_Expression$ = eval$: EXIT SUB
  194.             EXP$ = DWD(LEFT$(EXP$, s - 2) + eval$ + MID$(EXP$, Eval_E + 1))
  195.             'PRINT exp$
  196.             IF MID$(EXP$, 1, 1) = "N" THEN MID$(EXP$, 1) = "-"
  197.  
  198.             'temppp$ = DWD(LEFT$(exp$, s - 2) + " ## " + eval$ + " ## " + MID$(exp$, e + 1))
  199.         END IF
  200.     LOOP UNTIL Eval_E = 0
  201.     c = 0
  202.     DO
  203.         c = c + 1
  204.         SELECT CASE MID$(EXP$, c, 1)
  205.             CASE "0" TO "9", ".", "-" 'At this point, we should only have number values left.
  206.             CASE ELSE: Evaluate_Expression$ = "ERROR - Unknown Diagnosis: (" + EXP$ + ") ": EXIT SUB
  207.         END SELECT
  208.     LOOP UNTIL c >= LEN(EXP$)
  209.  
  210.     Evaluate_Expression$ = EXP$
  211.  
  212.  
  213.  
  214. SUB ParseExpression (EXP$)
  215.     DIM num(10) AS STRING
  216.     'PRINT exp$
  217.     EXP$ = DWD(EXP$)
  218.     'We should now have an expression with no () to deal with
  219.     'IF MID$(exp$, 2, 1) = "-" THEN exp$ = "0+" + MID$(exp$, 2)
  220.     FOR J = 1 TO 250
  221.         lowest = 0
  222.         DO UNTIL lowest = LEN(EXP$)
  223.             lowest = LEN(EXP$): OpOn = 0
  224.             FOR P = 1 TO UBOUND(OName)
  225.                 'Look for first valid operator
  226.                 IF J = PL(P) THEN 'Priority levels match
  227.                     IF LEFT$(EXP$, 1) = "-" THEN op = INSTR(2, EXP$, OName(P)) ELSE op = INSTR(EXP$, OName(P))
  228.                     IF op > 0 AND op < lowest THEN lowest = op: OpOn = P
  229.                 END IF
  230.             NEXT
  231.             IF OpOn = 0 THEN EXIT DO 'We haven't gotten to the proper PL for this OP to be processed yet.
  232.             IF LEFT$(EXP$, 1) = "-" THEN op = INSTR(2, EXP$, OName(OpOn)) ELSE op = INSTR(EXP$, OName(OpOn))
  233.             numset = 0
  234.  
  235.             '*** SPECIAL OPERATION RULESETS
  236.             IF OName(OpOn) = "-" THEN 'check for BOOLEAN operators before the -
  237.                 SELECT CASE MID$(EXP$, op - 3, 3)
  238.                     CASE "NOT", "XOR", "AND", "EQV", "IMP"
  239.                         EXIT DO 'Not an operator, it's a negative
  240.                 END SELECT
  241.                 IF MID$(EXP$, op - 3, 2) = "OR" THEN EXIT DO 'Not an operator, it's a negative
  242.             END IF
  243.  
  244.             IF op THEN
  245.                 c = LEN(OName(OpOn)) - 1
  246.                 DO
  247.                     SELECT CASE MID$(EXP$, op + c + 1, 1)
  248.                         CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N": numset = -1 'Valid digit
  249.                         CASE "-" 'We need to check if it's a minus or a negative
  250.                             IF OName(OpOn) = "_PI" OR numset THEN EXIT DO
  251.                         CASE ",": numset = 0
  252.                         CASE ELSE 'Not a valid digit, we found our separator
  253.                             EXIT DO
  254.                     END SELECT
  255.                     c = c + 1
  256.                 LOOP UNTIL op + c >= LEN(EXP$)
  257.                 e = op + c
  258.  
  259.                 c = 0
  260.                 DO
  261.                     c = c + 1
  262.                     SELECT CASE MID$(EXP$, op - c, 1)
  263.                         CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "N" 'Valid digit
  264.                         CASE "-" 'We need to check if it's a minus or a negative
  265.                             c1 = c
  266.                             bad = 0
  267.                             DO
  268.                                 c1 = c1 + 1
  269.                                 SELECT CASE MID$(EXP$, op - c1, 1)
  270.                                     CASE "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."
  271.                                         bad = -1
  272.                                         EXIT DO 'It's a minus sign
  273.                                     CASE ELSE
  274.                                         'It's a negative sign and needs to count as part of our numbers
  275.                                 END SELECT
  276.                             LOOP UNTIL op - c1 <= 0
  277.                             IF bad THEN EXIT DO 'We found our seperator
  278.                         CASE ELSE 'Not a valid digit, we found our separator
  279.                             EXIT DO
  280.                     END SELECT
  281.                 LOOP UNTIL op - c <= 0
  282.                 s = op - c
  283.                 num(1) = MID$(EXP$, s + 1, op - s - 1) 'Get our first number
  284.                 num(2) = MID$(EXP$, op + LEN(OName(OpOn)), e - op - LEN(OName(OpOn)) + 1) 'Get our second number
  285.                 IF MID$(num(1), 1, 1) = "N" THEN MID$(num(1), 1) = "-"
  286.                 IF MID$(num(2), 1, 1) = "N" THEN MID$(num(2), 1) = "-"
  287.                 IF num(1) = "-" THEN
  288.                     num(3) = "N" + EvaluateNumbers(OpOn, num())
  289.                 ELSE
  290.                     num(3) = EvaluateNumbers(OpOn, num())
  291.                 END IF
  292.                 IF MID$(num(3), 1, 1) = "-" THEN MID$(num(3), 1) = "N"
  293.                 'PRINT "*************"
  294.                 'PRINT num(1), OName(OpOn), num(2), num(3), exp$
  295.                 IF LEFT$(num(3), 5) = "ERROR" THEN EXP$ = num(3): EXIT SUB
  296.                 EXP$ = LTRIM$(N2S(DWD(LEFT$(EXP$, s) + RTRIM$(LTRIM$(num(3))) + MID$(EXP$, e + 1))))
  297.                 'PRINT exp$
  298.             END IF
  299.             op = 0
  300.         LOOP
  301.     NEXT
  302.  
  303.  
  304.  
  305.  
  306. SUB Set_OrderOfOperations
  307.     'PL sets our priortity level. 1 is highest to 65535 for the lowest.
  308.     'I used a range here so I could add in new priority levels as needed.
  309.     'OName ended up becoming the name of our commands, as I modified things.... Go figure!  LOL!
  310.     REDIM OName(10000) AS STRING, PL(10000) AS INTEGER
  311.     'Constants get evaluated first, with a Priority Level of 1
  312.  
  313.     i = i + 1: OName(i) = "C_UOF": PL(i) = 5 'convert to unsigned offset
  314.     i = i + 1: OName(i) = "C_OF": PL(i) = 5 'convert to offset
  315.     i = i + 1: OName(i) = "C_UBY": PL(i) = 5 'convert to unsigned byte
  316.     i = i + 1: OName(i) = "C_BY": PL(i) = 5 'convert to byte
  317.     i = i + 1: OName(i) = "C_UIN": PL(i) = 5 'convert to unsigned integer
  318.     i = i + 1: OName(i) = "C_IN": PL(i) = 5 'convert to integer
  319.     i = i + 1: OName(i) = "C_UIF": PL(i) = 5 'convert to unsigned int64
  320.     i = i + 1: OName(i) = "C_IF": PL(i) = 5 'convert to int64
  321.     i = i + 1: OName(i) = "C_ULO": PL(i) = 5 'convert to unsigned long
  322.     i = i + 1: OName(i) = "C_LO": PL(i) = 5 'convert to long
  323.     i = i + 1: OName(i) = "C_SI": PL(i) = 5 'convert to single
  324.     i = i + 1: OName(i) = "C_FL": PL(i) = 5 'convert to float
  325.     i = i + 1: OName(i) = "C_DO": PL(i) = 5 'convert to double
  326.     i = i + 1: OName(i) = "C_UBI": PL(i) = 5 'convert to unsigned bit
  327.     i = i + 1: OName(i) = "C_BI": PL(i) = 5 'convert to bit
  328.  
  329.     'Then Functions with PL 10
  330.     i = i + 1:: OName(i) = "_PI": PL(i) = 10
  331.     i = i + 1: OName(i) = "_ACOS": PL(i) = 10
  332.     i = i + 1: OName(i) = "_ASIN": PL(i) = 10
  333.     i = i + 1: OName(i) = "_ARCSEC": PL(i) = 10
  334.     i = i + 1: OName(i) = "_ARCCSC": PL(i) = 10
  335.     i = i + 1: OName(i) = "_ARCCOT": PL(i) = 10
  336.     i = i + 1: OName(i) = "_SECH": PL(i) = 10
  337.     i = i + 1: OName(i) = "_CSCH": PL(i) = 10
  338.     i = i + 1: OName(i) = "_COTH": PL(i) = 10
  339.     i = i + 1: OName(i) = "COS": PL(i) = 10
  340.     i = i + 1: OName(i) = "SIN": PL(i) = 10
  341.     i = i + 1: OName(i) = "TAN": PL(i) = 10
  342.     i = i + 1: OName(i) = "LOG": PL(i) = 10
  343.     i = i + 1: OName(i) = "EXP": PL(i) = 10
  344.     i = i + 1: OName(i) = "ATN": PL(i) = 10
  345.     i = i + 1: OName(i) = "_D2R": PL(i) = 10
  346.     i = i + 1: OName(i) = "_D2G": PL(i) = 10
  347.     i = i + 1: OName(i) = "_R2D": PL(i) = 10
  348.     i = i + 1: OName(i) = "_R2G": PL(i) = 10
  349.     i = i + 1: OName(i) = "_G2D": PL(i) = 10
  350.     i = i + 1: OName(i) = "_G2R": PL(i) = 10
  351.     i = i + 1: OName(i) = "ABS": PL(i) = 10
  352.     i = i + 1: OName(i) = "SGN": PL(i) = 10
  353.     i = i + 1: OName(i) = "INT": PL(i) = 10
  354.     i = i + 1: OName(i) = "_ROUND": PL(i) = 10
  355.     i = i + 1: OName(i) = "_CEIL": PL(i) = 10
  356.     i = i + 1: OName(i) = "FIX": PL(i) = 10
  357.     i = i + 1: OName(i) = "_SEC": PL(i) = 10
  358.     i = i + 1: OName(i) = "_CSC": PL(i) = 10
  359.     i = i + 1: OName(i) = "_COT": PL(i) = 10
  360.     i = i + 1: OName(i) = "ASC": PL(i) = 10
  361.     i = i + 1: OName(i) = "CHR$": PL(i) = 10
  362.     i = i + 1: OName(i) = "C_RG": PL(i) = 10 '_RGB32 converted
  363.     i = i + 1: OName(i) = "C_RA": PL(i) = 10 '_RGBA32 converted
  364.     i = i + 1: OName(i) = "_RGB": PL(i) = 10
  365.     i = i + 1: OName(i) = "_RGBA": PL(i) = 10
  366.     i = i + 1: OName(i) = "C_RX": PL(i) = 10 '_RED32 converted
  367.     i = i + 1: OName(i) = "C_GR": PL(i) = 10 ' _GREEN32 converted
  368.     i = i + 1: OName(i) = "C_BL": PL(i) = 10 '_BLUE32 converted
  369.     i = i + 1: OName(i) = "C_AL": PL(i) = 10 '_ALPHA32 converted
  370.     i = i + 1: OName(i) = "_RED": PL(i) = 10
  371.     i = i + 1: OName(i) = "_GREEN": PL(i) = 10
  372.     i = i + 1: OName(i) = "_BLUE": PL(i) = 10
  373.     i = i + 1: OName(i) = "_ALPHA": PL(i) = 10
  374.  
  375.     'Exponents with PL 20
  376.     i = i + 1: OName(i) = "^": PL(i) = 20
  377.     i = i + 1: OName(i) = "SQR": PL(i) = 20
  378.     i = i + 1: OName(i) = "ROOT": PL(i) = 20
  379.     'Multiplication and Division PL 30
  380.     i = i + 1: OName(i) = "*": PL(i) = 30
  381.     i = i + 1: OName(i) = "/": PL(i) = 30
  382.     'Integer Division PL 40
  383.     i = i + 1: OName(i) = "\": PL(i) = 40
  384.     'MOD PL 50
  385.     i = i + 1: OName(i) = "MOD": PL(i) = 50
  386.     'Addition and Subtraction PL 60
  387.     i = i + 1: OName(i) = "+": PL(i) = 60
  388.     i = i + 1: OName(i) = "-": PL(i) = 60
  389.  
  390.     'Relational Operators =, >, <, <>, <=, >=   PL 70
  391.     i = i + 1: OName(i) = "<>": PL(i) = 70 'These next three are just reversed symbols as an attempt to help process a common typo
  392.     i = i + 1: OName(i) = "><": PL(i) = 70
  393.     i = i + 1: OName(i) = "<=": PL(i) = 70
  394.     i = i + 1: OName(i) = ">=": PL(i) = 70
  395.     i = i + 1: OName(i) = "=<": PL(i) = 70 'I personally can never keep these things straight.  Is it < = or = <...
  396.     i = i + 1: OName(i) = "=>": PL(i) = 70 'Who knows, check both!
  397.     i = i + 1: OName(i) = ">": PL(i) = 70
  398.     i = i + 1: OName(i) = "<": PL(i) = 70
  399.     i = i + 1: OName(i) = "=": PL(i) = 70
  400.     'Logical Operations PL 80+
  401.     i = i + 1: OName(i) = "NOT": PL(i) = 80
  402.     i = i + 1: OName(i) = "AND": PL(i) = 90
  403.     i = i + 1: OName(i) = "OR": PL(i) = 100
  404.     i = i + 1: OName(i) = "XOR": PL(i) = 110
  405.     i = i + 1: OName(i) = "EQV": PL(i) = 120
  406.     i = i + 1: OName(i) = "IMP": PL(i) = 130
  407.     i = i + 1: OName(i) = ",": PL(i) = 1000
  408.  
  409.     REDIM _PRESERVE OName(i) AS STRING, PL(i) AS INTEGER
  410.  
  411. FUNCTION EvaluateNumbers$ (p, num() AS STRING)
  412.     DIM n1 AS _FLOAT, n2 AS _FLOAT, n3 AS _FLOAT
  413.     'PRINT "EVALNUM:"; OName(p), num(1), num(2)
  414.     IF INSTR(num(1), ",") THEN
  415.         EvaluateNumbers$ = "ERROR - Invalid comma (" + num(1) + ")": EXIT FUNCTION
  416.     END IF
  417.     l2 = INSTR(num(2), ",")
  418.     IF l2 THEN
  419.         SELECT CASE OName(p) 'only certain commands should pass a comma value
  420.             CASE "C_RG", "C_RA", "_RGB", "_RGBA", "_RED", "_GREEN", "C_BL", "_ALPHA"
  421.             CASE ELSE
  422.                 C$ = MID$(num(2), l2)
  423.                 num(2) = LEFT$(num(2), l2 - 1)
  424.         END SELECT
  425.     END IF
  426.  
  427.     SELECT CASE PL(p) 'divide up the work so we want do as much case checking
  428.         CASE 5 'Type conversions
  429.             'Note, these are special cases and work with the number BEFORE the command and not after
  430.             SELECT CASE OName(p) 'Depending on our operator..
  431.                 CASE "C_UOF": n1~%& = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1~%&)))
  432.                 CASE "C_ULO": n1%& = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1%&)))
  433.                 CASE "C_UBY": n1~%% = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1~%%)))
  434.                 CASE "C_UIN": n1~% = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1~%)))
  435.                 CASE "C_BY": n1%% = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1%%)))
  436.                 CASE "C_IN": n1% = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1%)))
  437.                 CASE "C_UIF": n1~&& = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1~&&)))
  438.                 CASE "C_OF": n1~& = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1~&)))
  439.                 CASE "C_IF": n1&& = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1&&)))
  440.                 CASE "C_LO": n1& = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1&)))
  441.                 CASE "C_UBI": n1~` = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1~`)))
  442.                 CASE "C_BI": n1` = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1`)))
  443.                 CASE "C_FL": n1## = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1##)))
  444.                 CASE "C_DO": n1# = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1#)))
  445.                 CASE "C_SI": n1! = VAL(num(1)): EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1!)))
  446.             END SELECT
  447.             EXIT FUNCTION
  448.         CASE 10 'functions
  449.             SELECT CASE OName(p) 'Depending on our operator..
  450.                 CASE "_PI"
  451.                     n1 = 3.14159265358979323846264338327950288## 'Future compatable in case something ever stores extra digits for PI
  452.                     IF num(2) <> "" THEN n1 = n1 * VAL(num(2))
  453.                 CASE "_ACOS": n1 = _ACOS(VAL(num(2)))
  454.                 CASE "_ASIN": n1 = _ASIN(VAL(num(2)))
  455.                 CASE "_ARCSEC": n1 = _ARCSEC(VAL(num(2)))
  456.                 CASE "_ARCCSC": n1 = _ARCCSC(VAL(num(2)))
  457.                 CASE "_ARCCOT": n1 = _ARCCOT(VAL(num(2)))
  458.                 CASE "_SECH": n1 = _SECH(VAL(num(2)))
  459.                 CASE "_CSCH": n1 = _CSCH(VAL(num(2)))
  460.                 CASE "_COTH": n1 = _COTH(VAL(num(2)))
  461.                 CASE "C_RG"
  462.                     n$ = num(2)
  463.                     IF n$ = "" THEN EvaluateNumbers$ = "ERROR - Invalid null _RGB32": EXIT FUNCTION
  464.                     c1 = INSTR(n$, ",")
  465.                     IF c1 THEN c2 = INSTR(c1 + 1, n$, ",")
  466.                     IF c2 THEN c3 = INSTR(c2 + 1, n$, ",")
  467.                     IF c3 THEN c4 = INSTR(c3 + 1, n$, ",")
  468.                     IF c1 = 0 THEN 'there's no comma in the command to parse.  It's a grayscale value
  469.                         n = VAL(num(2))
  470.                         n1 = _RGB32(n, n, n)
  471.                     ELSEIF c2 = 0 THEN 'there's one comma and not 2.  It's grayscale with alpha.
  472.                         n = VAL(LEFT$(num(2), c1))
  473.                         n2 = VAL(MID$(num(2), c1 + 1))
  474.                         n1 = _RGBA32(n, n, n, n2)
  475.                     ELSEIF c3 = 0 THEN 'there's two commas.  It's _RGB values
  476.                         n = VAL(LEFT$(num(2), c1))
  477.                         n2 = VAL(MID$(num(2), c1 + 1))
  478.                         n3 = VAL(MID$(num(2), c2 + 1))
  479.                         n1 = _RGB32(n, n2, n3)
  480.                     ELSEIF c4 = 0 THEN 'there's three commas.  It's _RGBA values
  481.                         n = VAL(LEFT$(num(2), c1))
  482.                         n2 = VAL(MID$(num(2), c1 + 1))
  483.                         n3 = VAL(MID$(num(2), c2 + 1))
  484.                         n4 = VAL(MID$(num(2), c3 + 1))
  485.                         n1 = _RGBA32(n, n2, n3, n4)
  486.                     ELSE 'we have more than three commas.  I have no idea WTH type of values got passed here!
  487.                         EvaluateNumbers$ = "ERROR - Invalid comma count (" + num(2) + ")": EXIT FUNCTION
  488.                     END IF
  489.                 CASE "C_RA"
  490.                     n$ = num(2)
  491.                     IF n$ = "" THEN EvaluateNumbers$ = "ERROR - Invalid null _RGBA32": EXIT FUNCTION
  492.                     c1 = INSTR(n$, ",")
  493.                     IF c1 THEN c2 = INSTR(c1 + 1, n$, ",")
  494.                     IF c2 THEN c3 = INSTR(c2 + 1, n$, ",")
  495.                     IF c3 THEN c4 = INSTR(c3 + 1, n$, ",")
  496.                     IF c3 = 0 OR c4 <> 0 THEN EvaluateNumbers$ = "ERROR - Invalid comma count (" + num(2) + ")": EXIT FUNCTION
  497.                     'we have to have 3 commas; not more, not less.
  498.                     n = VAL(LEFT$(num(2), c1))
  499.                     n2 = VAL(MID$(num(2), c1 + 1))
  500.                     n3 = VAL(MID$(num(2), c2 + 1))
  501.                     n4 = VAL(MID$(num(2), c3 + 1))
  502.                     n1 = _RGBA32(n, n2, n3, n4)
  503.                 CASE "_RGB"
  504.                     n$ = num(2)
  505.                     IF n$ = "" THEN EvaluateNumbers$ = "ERROR - Invalid null _RGB": EXIT FUNCTION
  506.                     c1 = INSTR(n$, ",")
  507.                     IF c1 THEN c2 = INSTR(c1 + 1, n$, ",")
  508.                     IF c2 THEN c3 = INSTR(c2 + 1, n$, ",")
  509.                     IF c3 THEN c4 = INSTR(c3 + 1, n$, ",")
  510.                     IF c3 = 0 OR c4 <> 0 THEN EvaluateNumbers$ = "ERROR - Invalid comma count (" + num(2) + "). _RGB requires 4 parameters for Red, Green, Blue, ScreenMode.": EXIT FUNCTION
  511.                     'we have to have 3 commas; not more, not less.
  512.                     n = VAL(LEFT$(num(2), c1))
  513.                     n2 = VAL(MID$(num(2), c1 + 1))
  514.                     n3 = VAL(MID$(num(2), c2 + 1))
  515.                     n4 = VAL(MID$(num(2), c3 + 1))
  516.                     SELECT CASE n4
  517.                         CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values
  518.                         CASE ELSE
  519.                             EvaluateNumbers$ = "ERROR - Invalid Screen Mode (" + STR$(n4) + ")": EXIT FUNCTION
  520.                     END SELECT
  521.                     t = _NEWIMAGE(1, 1, n4)
  522.                     n1 = _RGB(n, n2, n3, t)
  523.                     _FREEIMAGE t
  524.                 CASE "_RGBA"
  525.                     n$ = num(2)
  526.                     IF n$ = "" THEN EvaluateNumbers$ = "ERROR - Invalid null _RGBA": EXIT FUNCTION
  527.                     c1 = INSTR(n$, ",")
  528.                     IF c1 THEN c2 = INSTR(c1 + 1, n$, ",")
  529.                     IF c2 THEN c3 = INSTR(c2 + 1, n$, ",")
  530.                     IF c3 THEN c4 = INSTR(c3 + 1, n$, ",")
  531.                     IF c4 THEN c5 = INSTR(c4 + 1, n$, ",")
  532.                     IF c4 = 0 OR c5 <> 0 THEN EvaluateNumbers$ = "ERROR - Invalid comma count (" + num(2) + "). _RGBA requires 5 parameters for Red, Green, Blue, Alpha, ScreenMode.": EXIT FUNCTION
  533.                     'we have to have 4 commas; not more, not less.
  534.                     n = VAL(LEFT$(num(2), c1))
  535.                     n2 = VAL(MID$(num(2), c1 + 1))
  536.                     n3 = VAL(MID$(num(2), c2 + 1))
  537.                     n4 = VAL(MID$(num(2), c3 + 1))
  538.                     n5 = VAL(MID$(num(2), c4 + 1))
  539.                     SELECT CASE n5
  540.                         CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values
  541.                         CASE ELSE
  542.                             EvaluateNumbers$ = "ERROR - Invalid Screen Mode (" + STR$(n5) + ")": EXIT FUNCTION
  543.                     END SELECT
  544.                     t = _NEWIMAGE(1, 1, n5)
  545.                     n1 = _RGBA(n, n2, n3, n4, t)
  546.                     _FREEIMAGE t
  547.                 CASE "_RED", "_GREEN", "_BLUE", "_ALPHA"
  548.                     n$ = num(2)
  549.                     IF n$ = "" THEN EvaluateNumbers$ = "ERROR - Invalid null " + OName(p): EXIT FUNCTION
  550.                     c1 = INSTR(n$, ",")
  551.                     IF c1 = 0 THEN EvaluateNumbers$ = "ERROR - " + OName(p) + " requires 2 parameters for Color, ScreenMode.": EXIT FUNCTION
  552.                     IF c1 THEN c2 = INSTR(c1 + 1, n$, ",")
  553.                     IF c2 THEN EvaluateNumbers$ = "ERROR - " + OName(p) + " requires 2 parameters for Color, ScreenMode.": EXIT FUNCTION
  554.                     n = VAL(LEFT$(num(2), c1))
  555.                     n2 = VAL(MID$(num(2), c1 + 1))
  556.                     SELECT CASE n2
  557.                         CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values
  558.                         CASE ELSE
  559.                             EvaluateNumbers$ = "ERROR - Invalid Screen Mode (" + STR$(n2) + ")": EXIT FUNCTION
  560.                     END SELECT
  561.                     t = _NEWIMAGE(1, 1, n4)
  562.                     SELECT CASE OName(p)
  563.                         CASE "_RED": n1 = _RED(n, t)
  564.                         CASE "_BLUE": n1 = _BLUE(n, t)
  565.                         CASE "_GREEN": n1 = _GREEN(n, t)
  566.                         CASE "_ALPHA": n1 = _ALPHA(n, t)
  567.                     END SELECT
  568.                     _FREEIMAGE t
  569.                 CASE "C_RX", "C_GR", "C_BL", "C_AL"
  570.                     n$ = num(2)
  571.                     IF n$ = "" THEN EvaluateNumbers$ = "ERROR - Invalid null " + OName(p): EXIT FUNCTION
  572.                     n = VAL(num(2))
  573.                     SELECT CASE OName(p)
  574.                         CASE "C_RX": n1 = _RED32(n)
  575.                         CASE "C_BL": n1 = _BLUE32(n)
  576.                         CASE "C_GR": n1 = _GREEN32(n)
  577.                         CASE "C_AL": n1 = _ALPHA32(n)
  578.                     END SELECT
  579.                 CASE "COS": n1 = COS(VAL(num(2)))
  580.                 CASE "SIN": n1 = SIN(VAL(num(2)))
  581.                 CASE "TAN": n1 = TAN(VAL(num(2)))
  582.                 CASE "LOG": n1 = LOG(VAL(num(2)))
  583.                 CASE "EXP": n1 = EXP(VAL(num(2)))
  584.                 CASE "ATN": n1 = ATN(VAL(num(2)))
  585.                 CASE "_D2R": n1 = 0.0174532925 * (VAL(num(2)))
  586.                 CASE "_D2G": n1 = 1.1111111111 * (VAL(num(2)))
  587.                 CASE "_R2D": n1 = 57.2957795 * (VAL(num(2)))
  588.                 CASE "_R2G": n1 = 0.015707963 * (VAL(num(2)))
  589.                 CASE "_G2D": n1 = 0.9 * (VAL(num(2)))
  590.                 CASE "_G2R": n1 = 63.661977237 * (VAL(num(2)))
  591.                 CASE "ABS": n1 = ABS(VAL(num(2)))
  592.                 CASE "SGN": n1 = SGN(VAL(num(2)))
  593.                 CASE "INT": n1 = INT(VAL(num(2)))
  594.                 CASE "_ROUND": n1 = _ROUND(VAL(num(2)))
  595.                 CASE "_CEIL": n1 = _CEIL(VAL(num(2)))
  596.                 CASE "FIX": n1 = FIX(VAL(num(2)))
  597.                 CASE "_SEC": n1 = _SEC(VAL(num(2)))
  598.                 CASE "_CSC": n1 = _CSC(VAL(num(2)))
  599.                 CASE "_COT": n1 = _COT(VAL(num(2)))
  600.             END SELECT
  601.         CASE 20 TO 60 'Math Operators
  602.             SELECT CASE OName(p) 'Depending on our operator..
  603.                 CASE "^": n1 = VAL(num(1)) ^ VAL(num(2))
  604.                 CASE "SQR": n1 = SQR(VAL(num(2)))
  605.                 CASE "ROOT"
  606.                     n1 = VAL(num(1)): n2 = VAL(num(2))
  607.                     IF n2 = 1 THEN EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1))): EXIT FUNCTION
  608.                     IF n1 < 0 AND n2 >= 1 THEN sign = -1: n1 = -n1 ELSE sign = 1
  609.                     n3 = 1## / n2
  610.                     IF n3 <> INT(n3) AND n2 < 1 THEN sign = SGN(n1): n1 = ABS(n1)
  611.                     n1 = sign * (n1 ^ n3)
  612.                 CASE "*": n1 = VAL(num(1)) * VAL(num(2))
  613.                 CASE "/": n1 = VAL(num(1)) / VAL(num(2))
  614.                 CASE "\"
  615.                     IF VAL(num(2)) <> 0 THEN
  616.                         n1 = VAL(num(1)) \ VAL(num(2))
  617.                     ELSE
  618.                         EvaluateNumbers$ = "ERROR - Bad operation (We shouldn't see this)"
  619.                         EXIT FUNCTION
  620.                     END IF
  621.                 CASE "MOD": n1 = VAL(num(1)) MOD VAL(num(2))
  622.                 CASE "+": n1 = VAL(num(1)) + VAL(num(2))
  623.                 CASE "-":
  624.                     n1 = VAL(num(1)) - VAL(num(2))
  625.             END SELECT
  626.         CASE 70 'Relational Operators =, >, <, <>, <=, >=
  627.             SELECT CASE OName(p) 'Depending on our operator..
  628.                 CASE "=": n1 = VAL(num(1)) = VAL(num(2))
  629.                 CASE ">": n1 = VAL(num(1)) > VAL(num(2))
  630.                 CASE "<": n1 = VAL(num(1)) < VAL(num(2))
  631.                 CASE "<>", "><": n1 = VAL(num(1)) <> VAL(num(2))
  632.                 CASE "<=", "=<": n1 = VAL(num(1)) <= VAL(num(2))
  633.                 CASE ">=", "=>": n1 = VAL(num(1)) >= VAL(num(2))
  634.             END SELECT
  635.         CASE ELSE 'a value we haven't processed elsewhere
  636.             SELECT CASE OName(p) 'Depending on our operator..
  637.                 CASE "NOT": n1 = NOT VAL(num(2))
  638.                 CASE "AND": n1 = VAL(num(1)) AND VAL(num(2))
  639.                 CASE "OR": n1 = VAL(num(1)) OR VAL(num(2))
  640.                 CASE "XOR": n1 = VAL(num(1)) XOR VAL(num(2))
  641.                 CASE "EQV": n1 = VAL(num(1)) EQV VAL(num(2))
  642.                 CASE "IMP": n1 = VAL(num(1)) IMP VAL(num(2))
  643.             END SELECT
  644.     END SELECT
  645.  
  646.     EvaluateNumbers$ = RTRIM$(LTRIM$(STR$(n1))) + C$
  647.  
  648.     'PRINT "AFTEREVN:"; EvaluateNumbers$
  649.  
  650. FUNCTION DWD$ (EXP$) 'Deal With Duplicates
  651.     'To deal with duplicate operators in our code.
  652.     'Such as --  becomes a +
  653.     '++ becomes a +
  654.     '+- becomes a -
  655.     '-+ becomes a -
  656.     t$ = EXP$
  657.     DO
  658.         bad = 0
  659.         DO
  660.             l = INSTR(t$, "++")
  661.             IF l THEN t$ = LEFT$(t$, l - 1) + "+" + MID$(t$, l + 2): bad = -1
  662.         LOOP UNTIL l = 0
  663.         DO
  664.             l = INSTR(t$, "+-")
  665.             IF l THEN t$ = LEFT$(t$, l - 1) + "-" + MID$(t$, l + 2): bad = -1
  666.         LOOP UNTIL l = 0
  667.         DO
  668.             l = INSTR(t$, "-+")
  669.             IF l THEN t$ = LEFT$(t$, l - 1) + "-" + MID$(t$, l + 2): bad = -1
  670.         LOOP UNTIL l = 0
  671.         DO
  672.             l = INSTR(t$, "--")
  673.             IF l THEN t$ = LEFT$(t$, l - 1) + "+" + MID$(t$, l + 2): bad = -1
  674.         LOOP UNTIL l = 0
  675.         'PRINT "FIXING: "; t$
  676.     LOOP UNTIL NOT bad
  677.     DWD$ = t$
  678.  
  679. SUB PreParse (e$)
  680.     DIM f AS _FLOAT
  681.  
  682.     IF PP_TypeMod(0) = "" THEN
  683.         REDIM PP_TypeMod(100) AS STRING, PP_ConvertedMod(100) AS STRING 'Large enough to hold all values to begin with
  684.         PP_TypeMod(0) = "Initialized" 'Set so we don't do this section over and over, as we keep the values in shared memory.
  685.         Set_OrderOfOperations 'Call this once to set up our proper order of operations and variable list
  686.         'and the below is a conversion list so symbols don't get cross confused.
  687.         i = i + 1: PP_TypeMod(i) = "~`": PP_ConvertedMod(i) = "C_UBI" 'unsigned bit
  688.         i = i + 1: PP_TypeMod(i) = "~%%": PP_ConvertedMod(i) = "C_UBY" 'unsigned byte
  689.         i = i + 1: PP_TypeMod(i) = "~%&": PP_ConvertedMod(i) = "C_UOF" 'unsigned offset
  690.         i = i + 1: PP_TypeMod(i) = "~%": PP_ConvertedMod(i) = "C_UIN" 'unsigned integer
  691.         i = i + 1: PP_TypeMod(i) = "~&&": PP_ConvertedMod(i) = "C_UIF" 'unsigned integer64
  692.         i = i + 1: PP_TypeMod(i) = "~&": PP_ConvertedMod(i) = "C_ULO" 'unsigned long
  693.         i = i + 1: PP_TypeMod(i) = "`": PP_ConvertedMod(i) = "C_BI" 'bit
  694.         i = i + 1: PP_TypeMod(i) = "%%": PP_ConvertedMod(i) = "C_BY" 'byte
  695.         i = i + 1: PP_TypeMod(i) = "%&": PP_ConvertedMod(i) = "C_OF" 'offset
  696.         i = i + 1: PP_TypeMod(i) = "%": PP_ConvertedMod(i) = "C_IN" 'integer
  697.         i = i + 1: PP_TypeMod(i) = "&&": PP_ConvertedMod(i) = "C_IF" 'integer64
  698.         i = i + 1: PP_TypeMod(i) = "&": PP_ConvertedMod(i) = "C_LO" 'long
  699.         i = i + 1: PP_TypeMod(i) = "!": PP_ConvertedMod(i) = "C_SI" 'single
  700.         i = i + 1: PP_TypeMod(i) = "##": PP_ConvertedMod(i) = "C_FL" 'float
  701.         i = i + 1: PP_TypeMod(i) = "#": PP_ConvertedMod(i) = "C_DO" 'double
  702.         i = i + 1: PP_TypeMod(i) = "_RGB32": PP_ConvertedMod(i) = "C_RG" 'rgb32
  703.         i = i + 1: PP_TypeMod(i) = "_RGBA32": PP_ConvertedMod(i) = "C_RA" 'rgba32
  704.         i = i + 1: PP_TypeMod(i) = "_RED32": PP_ConvertedMod(i) = "C_RX" 'red32
  705.         i = i + 1: PP_TypeMod(i) = "_GREEN32": PP_ConvertedMod(i) = "C_GR" 'green32
  706.         i = i + 1: PP_TypeMod(i) = "_BLUE32": PP_ConvertedMod(i) = "C_BL" 'blue32
  707.         i = i + 1: PP_TypeMod(i) = "_ALPHA32": PP_ConvertedMod(i) = "C_AL" 'alpha32
  708.         REDIM _PRESERVE PP_TypeMod(i) AS STRING, PP_ConvertedMod(i) AS STRING 'And then resized to just contain the necessary space in memory
  709.     END IF
  710.     t$ = e$
  711.  
  712.     'First strip all spaces
  713.     t$ = ""
  714.     FOR i = 1 TO LEN(e$)
  715.         IF MID$(e$, i, 1) <> " " THEN t$ = t$ + MID$(e$, i, 1)
  716.     NEXT
  717.  
  718.     t$ = UCASE$(t$)
  719.     IF t$ = "" THEN e$ = "ERROR -- NULL string; nothing to evaluate": EXIT SUB
  720.  
  721.     'ERROR CHECK by counting our brackets
  722.     l = 0
  723.     DO
  724.         l = INSTR(l + 1, t$, "("): IF l THEN c = c + 1
  725.     LOOP UNTIL l = 0
  726.     l = 0
  727.     DO
  728.         l = INSTR(l + 1, t$, ")"): IF l THEN c1 = c1 + 1
  729.     LOOP UNTIL l = 0
  730.     IF c <> c1 THEN e$ = "ERROR -- Bad Parenthesis:" + STR$(c) + "( vs" + STR$(c1) + ")": EXIT SUB
  731.  
  732.     'Modify so that NOT will process properly
  733.     l = 0
  734.     DO
  735.         l = INSTR(l + 1, t$, "NOT")
  736.         IF l THEN
  737.             'We need to work magic on the statement so it looks pretty.
  738.             ' 1 + NOT 2 + 1 is actually processed as 1 + (NOT 2 + 1)
  739.             'Look for something not proper
  740.             l1 = INSTR(l + 1, t$, "AND")
  741.             IF l1 = 0 OR (INSTR(l + 1, t$, "OR") > 0 AND INSTR(l + 1, t$, "OR") < l1) THEN l1 = INSTR(l + 1, t$, "OR")
  742.             IF l1 = 0 OR (INSTR(l + 1, t$, "XOR") > 0 AND INSTR(l + 1, t$, "XOR") < l1) THEN l1 = INSTR(l + 1, t$, "XOR")
  743.             IF l1 = 0 OR (INSTR(l + 1, t$, "EQV") > 0 AND INSTR(l + 1, t$, "EQV") < l1) THEN l1 = INSTR(l + 1, t$, "EQV")
  744.             IF l1 = 0 OR (INSTR(l + 1, t$, "IMP") > 0 AND INSTR(l + 1, t$, "IMP") < l1) THEN l1 = INSTR(l + 1, t$, "IMP")
  745.             IF l1 = 0 THEN l1 = LEN(t$) + 1
  746.             t$ = LEFT$(t$, l - 1) + "(" + MID$(t$, l, l1 - l) + ")" + MID$(t$, l + l1 - l)
  747.             l = l + 3
  748.             'PRINT t$
  749.         END IF
  750.     LOOP UNTIL l = 0
  751.  
  752.     FOR j = 1 TO UBOUND(PP_TypeMod)
  753.         l = 0
  754.         DO
  755.             l = INSTR(l + 1, t$, PP_TypeMod(j))
  756.             IF l = 0 THEN EXIT DO
  757.             i = 0: l1 = 0: l2 = 0: lo = LEN(PP_TypeMod(j))
  758.             DO
  759.                 IF PL(i) > 10 THEN
  760.                     l2 = _INSTRREV(l, t$, OName$(i))
  761.                     IF l2 > 0 AND l2 > l1 THEN l1 = l2
  762.                 END IF
  763.                 i = i + lo
  764.             LOOP UNTIL i > UBOUND(PL)
  765.             'PRINT "L1:"; l1; "L"; l
  766.             l$ = LEFT$(t$, l1)
  767.             m$ = MID$(t$, l1 + 1, l - l1 - 1)
  768.             r$ = PP_ConvertedMod(j) + MID$(t$, l + lo)
  769.             'PRINT "Y$: "; TypeMod(j)
  770.             'PRINT "L$: "; l$
  771.             'PRINT "M$: "; m$
  772.             'PRINT "R$: "; r$
  773.             IF j > 15 THEN
  774.                 t$ = l$ + m$ + r$ 'replacement routine for commands which might get confused with others, like _RGB and _RGB32
  775.             ELSE
  776.                 'the first 15 commands need to properly place the parenthesis around the value we want to convert.
  777.                 t$ = l$ + "(" + m$ + ")" + r$
  778.             END IF
  779.             'PRINT "T$: "; t$
  780.             l = l + 2 + LEN(PP_TypeMod(j)) 'move forward from the length of the symbol we checked + the new "(" and  ")"
  781.         LOOP
  782.     NEXT
  783.     '    PRINT "HERE: "; t$
  784.  
  785.  
  786.  
  787.     'Check for bad operators before a ( bracket
  788.     l = 0
  789.     DO
  790.         l = INSTR(l + 1, t$, "(")
  791.         IF l AND l > 2 THEN 'Don't check the starting bracket; there's nothing before it.
  792.             good = 0
  793.             'PRINT "BEFORE: "; t$; l
  794.             FOR i = 1 TO UBOUND(OName)
  795.                 m$ = MID$(t$, l - LEN(OName(i)), LEN(OName(i)))
  796.                 'IF OName(i) = "C_SI" THEN PRINT "CONVERT TO SINGLE", m$
  797.                 IF m$ = OName(i) THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)
  798.             NEXT
  799.             'PRINT t$; l
  800.             IF NOT good THEN e$ = "ERROR - Improper operations before (.": EXIT SUB
  801.             l = l + 1
  802.         END IF
  803.     LOOP UNTIL l = 0
  804.  
  805.     'Check for bad operators after a ) bracket
  806.     l = 0
  807.     DO
  808.         l = INSTR(l + 1, t$, ")")
  809.         IF l AND l < LEN(t$) THEN
  810.             good = 0
  811.             FOR i = 1 TO UBOUND(oname)
  812.                 m$ = MID$(t$, l + 1, LEN(OName(i)))
  813.                 IF m$ = OName(i) THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI
  814.             NEXT
  815.             IF MID$(t$, l + 1, 1) = ")" THEN good = -1
  816.             IF NOT good THEN e$ = "ERROR - Improper operations after ).": EXIT SUB
  817.             l = l + 1
  818.         END IF
  819.     LOOP UNTIL l = 0 OR l = LEN(t$) 'last symbol is a bracket
  820.  
  821.     'Turn all &H (hex) numbers into decimal values for the program to process properly
  822.     l = 0
  823.     DO
  824.         l = INSTR(t$, "&H")
  825.         IF l THEN
  826.             E = l + 1: finished = 0
  827.             DO
  828.                 E = E + 1
  829.                 comp$ = MID$(t$, E, 1)
  830.                 SELECT CASE comp$
  831.                     CASE "0" TO "9", "A" TO "F" 'All is good, our next digit is a number, continue to add to the hex$
  832.                     CASE ELSE
  833.                         good = 0
  834.                         FOR i = 1 TO UBOUND(oname)
  835.                             IF MID$(t$, E, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)
  836.                         NEXT
  837.                         IF NOT good THEN e$ = "ERROR - Improper &H value. (" + comp$ + ")": EXIT SUB
  838.                         E = E - 1
  839.                         finished = -1
  840.                 END SELECT
  841.             LOOP UNTIL finished OR E = LEN(t$)
  842.             t$ = LEFT$(t$, l - 1) + LTRIM$(RTRIM$(STR$(VAL(MID$(t$, l, E - l + 1))))) + MID$(t$, E + 1)
  843.         END IF
  844.     LOOP UNTIL l = 0
  845.  
  846.     'Turn all &B (binary) numbers into decimal values for the program to process properly
  847.     l = 0
  848.     DO
  849.         l = INSTR(t$, "&B")
  850.         IF l THEN
  851.             E = l + 1: finished = 0
  852.             DO
  853.                 E = E + 1
  854.                 comp$ = MID$(t$, E, 1)
  855.                 SELECT CASE comp$
  856.                     CASE "0", "1" 'All is good, our next digit is a number, continue to add to the hex$
  857.                     CASE ELSE
  858.                         good = 0
  859.                         FOR i = 1 TO UBOUND(oname)
  860.                             IF MID$(t$, E, LEN(OName(i))) = OName(i) AND PL(i) > 1 AND PL(i) <= 250 THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)
  861.                         NEXT
  862.                         IF NOT good THEN e$ = "ERROR - Improper &B value. (" + comp$ + ")": EXIT SUB
  863.                         E = E - 1
  864.                         finished = -1
  865.                 END SELECT
  866.             LOOP UNTIL finished OR E = LEN(t$)
  867.             bin$ = MID$(t$, l + 2, E - l - 1)
  868.             FOR i = 1 TO LEN(bin$)
  869.                 IF MID$(bin$, i, 1) = "1" THEN f = f + 2 ^ (LEN(bin$) - i)
  870.             NEXT
  871.             t$ = LEFT$(t$, l - 1) + LTRIM$(RTRIM$(STR$(f))) + MID$(t$, E + 1)
  872.         END IF
  873.     LOOP UNTIL l = 0
  874.  
  875.     'PRINT "ALMOST:"; t$
  876.  
  877.     t$ = N2S(t$)
  878.     'PRINT "ALMOST2:"; t$
  879.     VerifyString t$
  880.     'PRINT "Out of PreParse: "; e$
  881.     e$ = t$
  882.  
  883.  
  884.  
  885. SUB VerifyString (t$)
  886.     'ERROR CHECK for unrecognized operations
  887.     j = 1
  888.     DO
  889.         comp$ = MID$(t$, j, 1)
  890.         SELECT CASE comp$
  891.             CASE "0" TO "9", ".", "(", ")", ",": j = j + 1
  892.             CASE ELSE
  893.                 good = 0
  894.                 FOR i = 1 TO UBOUND(OName)
  895.                     IF MID$(t$, j, LEN(OName(i))) = OName(i) THEN good = -1: EXIT FOR 'We found an operator after our ), and it's not a CONST (like PI)
  896.                 NEXT
  897.                 IF NOT good THEN t$ = "ERROR - Bad Operational value. (" + comp$ + ")": EXIT SUB
  898.                 j = j + LEN(OName(i))
  899.         END SELECT
  900.     LOOP UNTIL j > LEN(t$)
  901.  
  902. FUNCTION N2S$ (EXP$) 'scientific Notation to String
  903.  
  904.     'PRINT "Before notation:"; exp$
  905.  
  906.     t$ = LTRIM$(RTRIM$(EXP$))
  907.     IF LEFT$(t$, 1) = "-" OR LEFT$(t$, 1) = "N" THEN sign$ = "-": t$ = MID$(t$, 2)
  908.  
  909.     dp = INSTR(t$, "D+"): dm = INSTR(t$, "D-")
  910.     ep = INSTR(t$, "E+"): em = INSTR(t$, "E-")
  911.     check1 = SGN(dp) + SGN(dm) + SGN(ep) + SGN(em)
  912.     IF check1 < 1 OR check1 > 1 THEN N2S = EXP$: EXIT SUB 'If no scientic notation is found, or if we find more than 1 type, it's not SN!
  913.  
  914.     SELECT CASE l 'l now tells us where the SN starts at.
  915.         CASE IS < dp: l = dp
  916.         CASE IS < dm: l = dm
  917.         CASE IS < ep: l = ep
  918.         CASE IS < em: l = em
  919.     END SELECT
  920.  
  921.     l$ = LEFT$(t$, l - 1) 'The left of the SN
  922.     r$ = MID$(t$, l + 1): r&& = VAL(r$) 'The right of the SN, turned into a workable long
  923.  
  924.  
  925.     IF INSTR(l$, ".") THEN 'Location of the decimal, if any
  926.         IF r&& > 0 THEN
  927.             r&& = r&& - LEN(l$) + 2
  928.         ELSE
  929.             r&& = r&& + 1
  930.         END IF
  931.         l$ = LEFT$(l$, 1) + MID$(l$, 3)
  932.     END IF
  933.  
  934.     SELECT CASE r&&
  935.         CASE 0 'what the heck? We solved it already?
  936.             'l$ = l$
  937.         CASE IS < 0
  938.             FOR i = 1 TO -r&&
  939.                 l$ = "0" + l$
  940.             NEXT
  941.             l$ = "0." + l$
  942.         CASE ELSE
  943.             FOR i = 1 TO r&&
  944.                 l$ = l$ + "0"
  945.             NEXT
  946.     END SELECT
  947.  
  948.     N2S$ = sign$ + l$
  949.     'PRINT "After notation:"; N2S$
  950.  

Now, I know there's generally easier and shorter ways to write a calculator than this one, but this is making use of some pretty complex libraries of mine, such as the math evaluator.  A better way to think of this program would be:

Code: QB64: [Select]
  1. '$INCLUDE:'Math.BI'
  2. '$INCLUDE:'Virtual Keyboard.BI'
  3.  
  4. SCREEN _NEWIMAGE(640, 480, 32)
  5.  
  6. DIM My_Keyboard(4) AS STRING
  7. My_Keyboard(0) = "^/*-"
  8. My_Keyboard(1) = "789+"
  9. My_Keyboard(2) = "456" + CHR$(0) + "27,End" + CHR$(0)
  10. My_Keyboard(3) = "123" + CHR$(0) + "8,Del" + CHR$(0)
  11. My_Keyboard(4) = "0." + CHR$(0) + "13,Enter" + CHR$(0)
  12.  
  13.  
  14. Numpad = Create_KB(My_Keyboard(), 16, 30, 30)
  15.  
  16. ypos = 1
  17.     Display_KB Numpad, 250, 150, 1
  18.     WHILE _MOUSEINPUT: WEND 'must update mouse buffer before reading virtual keyboard
  19.     k = check_KB(Numpad)
  20.  
  21.     SELECT CASE k
  22.         CASE 8
  23.             out$ = LEFT$(out$, LEN(out$) - 1)
  24.             _DELAY .2
  25.         CASE 13
  26.             LOCATE ypos, 1: PRINT out$; " = "; Evaluate_Expression(out$)
  27.             out$ = ""
  28.             ypos = ypos + 1
  29.             _DELAY .2
  30.         CASE 27
  31.             SYSTEM
  32.         CASE IS <> 0
  33.             out$ = out$ + CHR$(k)
  34.             _DELAY .2
  35.     END SELECT
  36.     LOCATE ypos, 1: PRINT out$; " "; answer$
  37.     answer$ = ""
  38.  
  39.     _DISPLAY
  40.     _LIMIT 30
  41.  
  42. '$INCLUDE:'Virtual Keyboard.BM'
  43. '$INCLUDE:'Math.BM'

Now looking at it like that, it suddenly became a whole lot less complicated, as the majority of our code is actually from the libraries which we'd make use of.

Not terribly shabby, if I do say so myself!
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Virtual Keyboard
« Reply #2 on: February 05, 2021, 01:40:26 am »
And a few images, for the folks who are curious, but too lazy to test things out themselves:

 
KB1.png


 
KB2.png


 
KB3.png
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Virtual Keyboard
« Reply #3 on: February 05, 2021, 11:58:41 am »
And here's my on-screen keyboard jazzed up a bit with the new button routine which I shared in a different post.

Code: QB64: [Select]
  1. TYPE Keyboard_Internal_Type
  2.     AS LONG In_Use, Is_Hidden, Handle, Hardware_Handle, Xoffset, Yoffset, Xsize, Ysize
  3. DIM SHARED Virtual_KB(0 TO 10) AS Keyboard_Internal_Type
  4. DIM SHARED Keyboard_Values(0 TO 10, 0 TO 10, 0 TO 255) AS LONG '11 keyboards of up to 11 rows of 256 keys
  5.  
  6. SCREEN _NEWIMAGE(800, 600, 32)
  7.  
  8. DIM My_Keyboard(5) AS STRING
  9. My_Keyboard(0) = CHR$(0) + "27,ESC" + STRING$(2,0) + "15104,F1" + STRING$(2,0) + "15360,F2" + _
  10.     STRING$(2,0) + "15616,F3" + STRING$(2,0) + "15872,F4" + STRING$(2,0) + "16128,F5" + _
  11.     STRING$(2,0) + "16384,F6" + STRING$(2,0) + "16640,F7" + STRING$(2,0) + "16896,F8" + _
  12.     STRING$(2,0) + "17152,F9" + STRING$(2,0) + "17408,F10" + STRING$(2,0) + "34048,F11" + _
  13.     STRING$(2,0) + "34304,F12" + CHR$(0)
  14. My_Keyboard(1) = "`1234567890-=" + CHR$(0) + "19200,BKSP" + CHR$(0)
  15. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "QWERTYUIOP[]\"
  16. My_Keyboard(3) = CHR$(0) + "100301,KB2" + CHR$(0) + "ASDFGHJKL;'" + CHR$(0) + "13,ENTER" + CHR$(0)
  17. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "ZXCVBNM,./" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  18. My_Keyboard(5) = CHR$(0) + "100306,CTRL" + STRING$(2,0) + "0,WIN" + STRING$(2,0) + "100308,ALT" + _
  19.     STRING$(2,0) + "32,SPACE" + STRING$(2,0) + "100307,ALT" + STRING$(2,0) + "0,WIN" + STRING$(2,0) + "0,MENU" + _
  20.     STRING$(2,0) + "100305,CTRL" +  CHR$(0)
  21.  
  22. font = _LOADFONT("Courbd.ttf", 14, "monospace")
  23. FullsizeKB1 = Create_KB(My_Keyboard(), font, 55, 35)
  24.  
  25.  
  26.  
  27. My_Keyboard(1) = "~!@#$%^&*()_+" + CHR$(0) + "19200,BKSP" + CHR$(0)
  28. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "qwertyuiop{}|"
  29. My_Keyboard(3) = CHR$(0) + "100301,KB1" + CHR$(0) + "asdfghjkl:" + CHR$(34) + CHR$(0) + "13,ENTER" + CHR$(0)
  30. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "zxcvbnm<>?" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  31. FullsizeKB2 = Create_KB(My_Keyboard(), font, 55, 35)
  32.  
  33.  
  34. Keyboard_In_Use = FullsizeKB1
  35.     Display_KB Keyboard_In_Use, 10, 100, 1
  36.     WHILE _MOUSEINPUT: WEND 'must update mouse buffer before reading virtual keyboard
  37.     k = check_KB(Keyboard_In_Use)
  38.     SELECT CASE k
  39.         CASE 100301 'swap keyboards, rather than having a CAPS LOCK key
  40.             IF Keyboard_In_Use = FullsizeKB1 THEN
  41.                 Keyboard_In_Use = FullsizeKB2
  42.             ELSE
  43.                 Keyboard_In_Use = FullsizeKB1
  44.             END IF
  45.             _DELAY .2
  46.         CASE IS <> 0
  47.             PRINT k;
  48.             IF k > 0 AND k < 255 THEN PRINT CHR$(k);
  49.             PRINT ,
  50.             _DELAY .2 'delay is so we don't spam clicks from mousedown events
  51.     END SELECT
  52.  
  53.     _DISPLAY
  54.     _LIMIT 30
  55.  
  56. FUNCTION check_KB& (Which)
  57.     STATIC AS INTEGER x, y 'so as to not interfer with any global variables
  58.     x = _MOUSEX - Virtual_KB(Which).Xoffset
  59.     y = _MOUSEY - Virtual_KB(Which).Yoffset
  60.  
  61.     yon = x \ Virtual_KB(Which).Xsize
  62.     xon = y \ Virtual_KB(Which).Ysize
  63.     IF xon >= 0 AND xon <= 10 AND yon >= 0 AND yon <= 255 THEN
  64.         IF _MOUSEBUTTON(1) THEN check_KB& = Keyboard_Values(Which, xon, yon)
  65.     END IF
  66.  
  67.  
  68.  
  69.  
  70. SUB Display_KB (Which AS INTEGER, Xwhere AS INTEGER, Ywhere AS INTEGER, style AS INTEGER)
  71.     IF Virtual_KB(Which).In_Use = 0 THEN EXIT SUB
  72.     IF Virtual_KB(Which).Is_Hidden THEN EXIT SUB
  73.     Virtual_KB(Which).Xoffset = Xwhere
  74.     Virtual_KB(Which).Yoffset = Ywhere
  75.     IF style THEN 'we want a hardware image
  76.         _PUTIMAGE (Xwhere, Ywhere), Virtual_KB(Which).Hardware_Handle
  77.     ELSE
  78.         _PUTIMAGE (Xwhere, Ywhere), Virtual_KB(Which).Handle
  79.     END IF
  80.  
  81. FUNCTION Create_KB (KB() AS STRING, Font AS LONG, Xsize AS LONG, Ysize AS LONG)
  82.     STATIC AS LONG D, S 'stored as static so as to not interfer with any globals of the same name.
  83.     D = _DEST: S = _SOURCE
  84.  
  85.     FOR i = 0 TO 10
  86.         IF Virtual_KB(i).In_Use = 0 THEN
  87.             Virtual_KB(i).In_Use = -1
  88.             Virtual_KB(i).Xsize = Xsize
  89.             Virtual_KB(i).Ysize = Ysize
  90.  
  91.             Create_KB = i
  92.             EXIT FOR
  93.         END IF
  94.     NEXT
  95.     IF i = 11 THEN
  96.         CLS
  97.         PRINT "Too many keyboards registered in use at the same time!  Can not create a new one."
  98.         END
  99.     END IF
  100.     This_KB = i
  101.  
  102.     keyboard_image = _NEWIMAGE(4096, 4096, 32)
  103.     _DEST keyboard_image: _SOURCE keyboard_image
  104.     _FONT Font
  105.  
  106.     'now build the keyboard
  107.     FOR i = 0 TO UBOUND(KB)
  108.         top = i * Ysize
  109.         count = 0
  110.         FOR j = 1 TO LEN(KB(i))
  111.             left = (count) * Xsize
  112.             count = count + 1
  113.             repeat = 1
  114.             c = ASC(KB(i), j): out$ = ""
  115.             IF c = 0 THEN
  116.                 'look for the comma
  117.                 comma_position = INSTR(j, KB(i), ",")
  118.                 IF comma_position THEN 'we have a value, label
  119.                     value$ = MID$(KB(i), j + 1, comma_position - j - 1)
  120.                     c = VAL(value$)
  121.                     j = comma_position + 1
  122.                 ELSE 'cry loud and hard so we can sort it out while programming our keyboard layout
  123.                     scream_and_die:
  124.                     SLEEP
  125.                     CLS
  126.                     PRINT "You have an invalid keyboard layout!"
  127.                     END
  128.                 END IF
  129.  
  130.                 end_position = INSTR(j, KB(i), CHR$(0))
  131.                 IF end_position THEN 'we're extracting the label
  132.                     out$ = MID$(KB(i), j, end_position - j)
  133.                     repeat = ASC(out$, LEN(out$))
  134.                     IF repeat > 0 AND repeat < 9 THEN
  135.                         out$ = LEFT$(out$, LEN(out$) - 1)
  136.                     ELSE
  137.                         repeat = 1
  138.                     END IF
  139.                     j = end_position
  140.                 ELSE
  141.                     GOTO scream_and_die
  142.                 END IF
  143.             END IF
  144.             '           LINE (left, top)-STEP(Xsize * repeat, Ysize), -1, B
  145.  
  146.             IF left + Xsize * repeat > max_width THEN max_width = left + Xsize * repeat
  147.             IF top + Ysize > max_height THEN max_height = top + Ysize
  148.             IF c < 256 AND out$ = "" THEN out$ = CHR$(c)
  149.             '            _PRINTSTRING (left + (Xsize * repeat - _FONTWIDTH * LEN(out$)) / 2, top + (Ysize - _FONTHEIGHT) / 2), out$
  150.             Button left, top, Xsize * repeat, Ysize, 50, 50, 50, 10, 10, 10, out$
  151.             DO UNTIL repeat = 1
  152.                 Keyboard_Values(This_KB, i, count - 1) = c
  153.                 count = count + 1
  154.                 repeat = repeat - 1
  155.             LOOP
  156.             Keyboard_Values(This_KB, i, count - 1) = c
  157.         NEXT
  158.     NEXT
  159.  
  160.     'resize to proper size to put upon the screen
  161.     Virtual_KB(This_KB).Handle = _NEWIMAGE(max_width + 1, max_height + 1, 32)
  162.     _PUTIMAGE (0, 0)-(max_width, max_height), keyboard_image, Virtual_KB(This_KB).Handle, (0, 0)-(max_width, max_height)
  163.     Virtual_KB(This_KB).Hardware_Handle = _COPYIMAGE(Virtual_KB(This_KB).Handle, 33)
  164.     _FREEIMAGE keyboard_image
  165.  
  166.     clean_exit:
  167.     _SOURCE S: _DEST D
  168.  
  169. SUB Button (x, y, wide, tall, r, g, b, rc, gc, bc, caption$)
  170.     DIM AS _UNSIGNED LONG k, d, bg
  171.     d = _DEFAULTCOLOR
  172.     bg = _BACKGROUNDCOLOR
  173.     FOR i = 0 TO 10
  174.         rm = rm + rc
  175.         gm = gm + gc
  176.         bm = bm + bc
  177.         k = _RGB32(r + rm, g + gm, b + bm)
  178.         LINE (x + i, y + i)-(x + wide - i, y + tall - i), k, B
  179.     NEXT
  180.     PAINT (x + i, y + i), k
  181.     COLOR _RGB32(r, g, b), 0
  182.     COLOR -1, 0
  183.     _PRINTSTRING (x + (wide - _PRINTWIDTH(caption$)) / 2, y + (tall - _FONTHEIGHT) / 2 + 2), caption$
  184.  
  185.     COLOR d, bg
  186.  

 
Sexy Keyboard.png
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Virtual Keyboard
« Reply #4 on: February 05, 2021, 12:11:52 pm »
OOOOH I like how that looks.
Shuwatch!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Virtual Keyboard
« Reply #5 on: February 05, 2021, 12:15:15 pm »
Agreed. Getting better. Keep going for realism!
You're not done when it works, you're done when it's right.

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: Virtual Keyboard
« Reply #6 on: February 05, 2021, 12:31:50 pm »
Looking good! 

- Dav

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Virtual Keyboard
« Reply #7 on: February 05, 2021, 12:33:28 pm »
Might need a slightly smaller font, or a slightly larger set of keys.  The "MENU" button is wider than the center of its button now that the shading is taking up a good bit of real estate.

(Or perhaps smaller levels of shading between keys, in this specific case?  It may be the button routine which gets a little more tweaking here.)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Virtual Keyboard
« Reply #8 on: February 06, 2021, 02:13:56 am »
Okies guys, if you love me at all, check this out!  This has to be one of my proudest little accomplishments to date, I think.  I *REALLY* love the way this is looking and performing on my machine for me!

Code: QB64: [Select]
  1. TYPE Keyboard_Internal_Type
  2.     AS LONG In_Use, Is_Hidden, Handle, Hardware_Handle, Xoffset, Yoffset, Xsize, Ysize, Font
  3. TYPE Keyboard_Value_Type
  4.     AS LONG Value, State
  5.     AS STRING Caption
  6. DIM SHARED Virtual_KB(0 TO 10) AS Keyboard_Internal_Type
  7. DIM SHARED Keyboard_Values(0 TO 10, 0 TO 10, 0 TO 255) AS Keyboard_Value_Type '11 keyboards of up to 11 rows of 256 keys
  8. TYPE Button_Report
  9.     Time AS _FLOAT
  10.     Value AS LONG
  11.     caption AS STRING
  12. DIM SHARED Buttons(10) AS Button_Report
  13. CONST Repeat_Speed = 0.1
  14.  
  15.  
  16. SCREEN _NEWIMAGE(800, 600, 32)
  17.  
  18. DIM My_Keyboard(5) AS STRING
  19. My_Keyboard(0) = CHR$(0) + "27,ESC" + STRING$(2,0) + "15104,F1" + STRING$(2,0) + "15360,F2" + _
  20.     STRING$(2,0) + "15616,F3" + STRING$(2,0) + "15872,F4" + STRING$(2,0) + "16128,F5" + _
  21.     STRING$(2,0) + "16384,F6" + STRING$(2,0) + "16640,F7" + STRING$(2,0) + "16896,F8" + _
  22.     STRING$(2,0) + "17152,F9" + STRING$(2,0) + "17408,F10" + STRING$(2,0) + "34048,F11" + _
  23.     STRING$(2,0) + "34304,F12" + CHR$(0)
  24. My_Keyboard(1) = "`1234567890-=" + CHR$(0) + "19200,BKSP" + CHR$(0)
  25. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "QWERTYUIOP[]\"
  26. My_Keyboard(3) = CHR$(0) + "100301,KB2" + CHR$(0) + "ASDFGHJKL;'" + CHR$(0) + "13,ENTER" + CHR$(0)
  27. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "ZXCVBNM,./" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  28. My_Keyboard(5) = CHR$(0) + "100306,CTRL" + STRING$(2,0) + "0,WIN" + STRING$(2,0) + "100308,ALT" + _
  29.     STRING$(2,0) + "32,SPACE" + STRING$(2,0) + "100307,ALT" + STRING$(2,0) + "0,WIN" + STRING$(2,0) + "0,MENU" + _
  30.     STRING$(2,0) + "100305,CTRL" +  CHR$(0)
  31.  
  32. font = _LOADFONT("Courbd.ttf", 14, "monospace")
  33. FullsizeKB1 = Create_KB(My_Keyboard(), font, 55, 35)
  34.  
  35. My_Keyboard(1) = "~!@#$%^&*()_+" + CHR$(0) + "19200,BKSP" + CHR$(0)
  36. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "qwertyuiop{}|"
  37. My_Keyboard(3) = CHR$(0) + "100301,KB1" + CHR$(0) + "asdfghjkl:" + CHR$(34) + CHR$(0) + "13,ENTER" + CHR$(0)
  38. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "zxcvbnm<>?" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  39. FullsizeKB2 = Create_KB(My_Keyboard(), font, 55, 35)
  40.  
  41.  
  42. Keyboard_In_Use = FullsizeKB1
  43.     Display_KB Keyboard_In_Use, 10, 100, 0
  44.     WHILE _MOUSEINPUT: WEND 'must update mouse buffer before reading virtual keyboard
  45.     k = check_KB(Keyboard_In_Use)
  46.     SELECT CASE k
  47.         CASE 100301 'swap keyboards, rather than having a CAPS LOCK key
  48.             IF Keyboard_In_Use = FullsizeKB1 THEN
  49.                 Keyboard_In_Use = FullsizeKB2
  50.             ELSE
  51.                 Keyboard_In_Use = FullsizeKB1
  52.             END IF
  53.         CASE IS <> 0
  54.             PRINT k;
  55.             IF k > 0 AND k < 255 THEN PRINT CHR$(k);
  56.             PRINT ,
  57.     END SELECT
  58.  
  59.     _DISPLAY
  60.     _LIMIT 30
  61.  
  62. FUNCTION check_KB& (Which)
  63.     STATIC AS INTEGER x, y 'so as to not interfer with any global variables
  64.  
  65.  
  66.     x = _MOUSEX - Virtual_KB(Which).Xoffset
  67.     y = _MOUSEY - Virtual_KB(Which).Yoffset
  68.  
  69.     xsize = Virtual_KB(Which).Xsize
  70.     ysize = Virtual_KB(Which).Ysize
  71.     yon = x \ Virtual_KB(Which).Xsize
  72.     xon = y \ Virtual_KB(Which).Ysize
  73.     first_zero = 0
  74.     IF xon >= 0 AND xon <= 10 AND yon >= 0 AND yon <= 255 THEN
  75.         IF _MOUSEBUTTON(1) THEN
  76.             IF Keyboard_Values(Which, xon, yon).Value THEN
  77.                 FOR i = 0 TO 10
  78.                     IF Buttons(i).Value = Keyboard_Values(Which, xon, yon).Value THEN GOTO safe_exit:
  79.                     IF Buttons(i).Value = 0 AND first_zero = 0 THEN first_zero = i
  80.                 NEXT
  81.                 IF first_zero = 0 THEN GOTO safe_exit:
  82.                 Buttons(first_zero).Value = Keyboard_Values(Which, xon, yon).Value
  83.                 Buttons(first_zero).Time = ExtendedTimer + Repeat_Speed
  84.                 Buttons(first_zero).caption = Keyboard_Values(Which, xon, yon).Caption
  85.  
  86.                 check_KB& = Keyboard_Values(Which, xon, yon).Value
  87.                 firston = 0
  88.                 out$ = Keyboard_Values(Which, xon, yon).Caption
  89.                 FOR z = 0 TO 255
  90.                     IF Keyboard_Values(Which, xon, z).Caption = out$ THEN
  91.                         Keyboard_Values(Which, xon, z).State = -1 'down
  92.                         IF firston = 0 THEN firston = z
  93.                         Repeat = ASC(out$, LEN(out$))
  94.                         IF Repeat < 1 OR Repeat > 9 THEN Repeat = 1
  95.                     END IF
  96.                 NEXT
  97.                 IF firston THEN
  98.                     d = _DEST
  99.                     _DEST Virtual_KB(Which).Handle
  100.                     IF Repeat > 1 THEN out$ = LEFT$(out$, LEN(out$) - 1)
  101.                     Button firston * xsize, xon * ysize, xsize * Repeat, ysize, 150, 150, 150, -5, -5, -5, out$
  102.                     Virtual_KB(Which).Hardware_Handle = _COPYIMAGE(Virtual_KB(Which).Handle, 33)
  103.                     _DEST d
  104.                 END IF
  105.  
  106.             END IF
  107.         END IF
  108.     END IF
  109.     safe_exit:
  110.     FOR i = 0 TO 10
  111.         IF Buttons(i).Value <> 0 AND Buttons(i).Time <> 0 AND Buttons(i).caption <> "" THEN
  112.             IF ExtendedTimer > Buttons(i).Time THEN
  113.                 'Refresh_KB = -1
  114.                 out$ = Buttons(i).caption
  115.                 firston = 0
  116.                 FOR j = 0 TO 10
  117.                     FOR z = 0 TO 255
  118.                         IF Keyboard_Values(Which, j, z).Value = Buttons(i).Value OR Keyboard_Values(Which, j, z).Caption = out$ THEN
  119.                             Keyboard_Values(Which, j, z).State = 0 'up
  120.                             IF firston = 0 THEN firston = z
  121.                             Repeat = ASC(out$, LEN(out$))
  122.                             IF Repeat < 1 OR Repeat > 9 THEN Repeat = 1
  123.                         END IF
  124.                     NEXT
  125.                 NEXT
  126.                 Buttons(i).Value = 0
  127.                 Buttons(i).Time = 0
  128.                 Buttons(i).caption = ""
  129.  
  130.                 IF firston THEN
  131.                     d = _DEST
  132.                     _DEST Virtual_KB(Which).Handle
  133.                     IF Repeat > 1 THEN out$ = LEFT$(out$, LEN(out$) - 1)
  134.                     Button firston * xsize, xon * ysize, xsize * Repeat, ysize, 50, 50, 50, 10, 10, 10, out$
  135.                     Virtual_KB(Which).Hardware_Handle = _COPYIMAGE(Virtual_KB(Which).Handle, 33)
  136.                     _DEST d
  137.                 END IF
  138.             END IF
  139.         END IF
  140.     NEXT
  141.  
  142.  
  143.  
  144.  
  145. SUB Display_KB (Which AS INTEGER, Xwhere AS INTEGER, Ywhere AS INTEGER, style AS INTEGER)
  146.     IF Virtual_KB(Which).In_Use = 0 THEN EXIT SUB
  147.     IF Virtual_KB(Which).Is_Hidden THEN EXIT SUB
  148.     Virtual_KB(Which).Xoffset = Xwhere
  149.     Virtual_KB(Which).Yoffset = Ywhere
  150.     IF style THEN 'we want a hardware image
  151.         _PUTIMAGE (Xwhere, Ywhere), Virtual_KB(Which).Hardware_Handle
  152.     ELSE
  153.         _PUTIMAGE (Xwhere, Ywhere), Virtual_KB(Which).Handle
  154.     END IF
  155.  
  156. FUNCTION Create_KB (KB() AS STRING, Font AS LONG, Xsize AS LONG, Ysize AS LONG)
  157.     STATIC AS LONG D, S 'stored as static so as to not interfer with any globals of the same name.
  158.     D = _DEST: S = _SOURCE
  159.  
  160.     FOR i = 0 TO 10
  161.         IF Virtual_KB(i).In_Use = 0 THEN
  162.             Virtual_KB(i).In_Use = -1
  163.             Virtual_KB(i).Xsize = Xsize
  164.             Virtual_KB(i).Ysize = Ysize
  165.             Virtual_KB(i).Font = Font
  166.             Create_KB = i
  167.             EXIT FOR
  168.         END IF
  169.     NEXT
  170.     IF i = 11 THEN
  171.         CLS
  172.         PRINT "Too many keyboards registered in use at the same time!  Can not create a new one."
  173.         END
  174.     END IF
  175.     This_KB = i
  176.  
  177.     keyboard_image = _NEWIMAGE(4096, 4096, 32)
  178.     _DEST keyboard_image: _SOURCE keyboard_image
  179.     _FONT Font
  180.  
  181.     'now build the keyboard
  182.     FOR i = 0 TO UBOUND(KB)
  183.         top = i * Ysize
  184.         count = 0
  185.         FOR j = 1 TO LEN(KB(i))
  186.             left = (count) * Xsize
  187.             count = count + 1
  188.             repeat = 1
  189.             c = ASC(KB(i), j): out$ = ""
  190.             IF c = 0 THEN
  191.                 'look for the comma
  192.                 comma_position = INSTR(j, KB(i), ",")
  193.                 IF comma_position THEN 'we have a value, label
  194.                     value$ = MID$(KB(i), j + 1, comma_position - j - 1)
  195.                     c = VAL(value$)
  196.  
  197.                     j = comma_position + 1
  198.                 ELSE 'cry loud and hard so we can sort it out while programming our keyboard layout
  199.                     scream_and_die:
  200.                     SLEEP
  201.                     CLS
  202.                     PRINT "You have an invalid keyboard layout!"
  203.                     END
  204.                 END IF
  205.  
  206.                 end_position = INSTR(j, KB(i), CHR$(0))
  207.                 IF end_position THEN 'we're extracting the label
  208.                     out$ = MID$(KB(i), j, end_position - j)
  209.                     repeat = ASC(out$, LEN(out$))
  210.                     IF repeat > 0 AND repeat < 9 THEN
  211.                         r$ = CHR$(repeat)
  212.                         out$ = LEFT$(out$, LEN(out$) - 1)
  213.                     ELSE
  214.                         repeat = 1
  215.                     END IF
  216.                     Keyboard_Values(This_KB, i, count - 1).Caption = out$ + r$
  217.                     j = end_position
  218.                 ELSE
  219.                     GOTO scream_and_die
  220.                 END IF
  221.             END IF
  222.  
  223.  
  224.             IF left + Xsize * repeat > max_width THEN max_width = left + Xsize * repeat
  225.             IF top + Ysize > max_height THEN max_height = top + Ysize
  226.             IF c < 256 AND out$ = "" THEN out$ = CHR$(c)
  227.  
  228.             Keyboard_Values(This_KB, i, count - 1).Caption = out$ + "r"
  229.             Button left, top, Xsize * repeat, Ysize, 50, 50, 50, 10, 10, 10, out$
  230.             DO UNTIL repeat = 1
  231.                 Keyboard_Values(This_KB, i, count - 1).Value = c
  232.                 Keyboard_Values(This_KB, i, count - 1).Caption = out$ + r$
  233.                 count = count + 1
  234.                 repeat = repeat - 1
  235.             LOOP
  236.             Keyboard_Values(This_KB, i, count - 1).Value = c
  237.             Keyboard_Values(This_KB, i, count - 1).Caption = out$ + r$
  238.             r$ = ""
  239.         NEXT
  240.     NEXT
  241.  
  242.     'resize to proper size to put upon the screen
  243.     Virtual_KB(This_KB).Handle = _NEWIMAGE(max_width + 1, max_height + 1, 32)
  244.     _PUTIMAGE (0, 0)-(max_width, max_height), keyboard_image, Virtual_KB(This_KB).Handle, (0, 0)-(max_width, max_height)
  245.     Virtual_KB(This_KB).Hardware_Handle = _COPYIMAGE(Virtual_KB(This_KB).Handle, 33)
  246.     _FREEIMAGE keyboard_image
  247.  
  248.     clean_exit:
  249.     _SOURCE S: _DEST D
  250.  
  251. SUB Button (x, y, wide, tall, r, g, b, rc, gc, bc, caption$)
  252.     DIM AS _UNSIGNED LONG k, d, bg
  253.     d = _DEFAULTCOLOR
  254.     bg = _BACKGROUNDCOLOR
  255.  
  256.     FOR i = 0 TO 8
  257.         rm = rm + rc
  258.         gm = gm + gc
  259.         bm = bm + bc
  260.         k = _RGB32(r + rm, g + gm, b + bm)
  261.         LINE (x + i * .75, y + i)-(x + wide - i * .75, y + tall - i), k, B
  262.     NEXT
  263.     PAINT (x + i, y + i), k
  264.     COLOR _RGB32(r, g, b), 0
  265.     COLOR -1, 0
  266.     _PRINTSTRING (x + (wide - _PRINTWIDTH(caption$)) / 2, y + (tall - _FONTHEIGHT) / 2 + 2), caption$
  267.  
  268.     COLOR d, bg
  269.  
  270. FUNCTION ExtendedTimer##
  271.     d$ = DATE$
  272.     l = INSTR(d$, "-")
  273.     l1 = INSTR(l + 1, d$, "-")
  274.     m = VAL(LEFT$(d$, l))
  275.     d = VAL(MID$(d$, l + 1))
  276.     y = VAL(MID$(d$, l1 + 1)) - 1970
  277.     FOR i = 1 TO m
  278.         SELECT CASE i 'Add the number of days for each previous month passed
  279.             CASE 1: d = d 'January doestn't have any carry over days.
  280.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  281.             CASE 3: d = d + 28
  282.             CASE 5, 7, 10, 12: d = d + 30
  283.         END SELECT
  284.     NEXT
  285.     FOR i = 1 TO y
  286.         d = d + 365
  287.     NEXT
  288.     FOR i = 2 TO y STEP 4
  289.         IF m > 2 THEN d = d + 1 'add an extra day for leap year every 4 years, starting in 1970
  290.     NEXT
  291.     d = d - 1 'for year 2000
  292.     s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  293.     ExtendedTimer## = (s~&& + TIMER)
  294.  

Repeat keys are slower on this than on a traditional keyboard (0.5 seconds), but that's on purpose since I figure folks are much slower moving and interacting with a mouse than they are with a physical keyboard.

This is getting close to being what I'd call a completed library.  The one thing which I'm still wanting to add here is the ability to HOLD a key down, with a double click event.  Usage would be to let someone double-click ALT, have it stay down, and then click T, to produce an ALT-T event with the virtual keyboard.

Test this out.  Play around with it.  Tell me how responsive (or sluggish) it acts on other systems.  I'd love to hear back from you guys, after you've had a chance to give this a test run once.   Let me know what you think about it, and if it's a virtual keyboard that you'd enjoy interacting with in the future.  (I'm looking at plugging this into my game that I've been making since FOREVER ago, and the end goal is to have it interact with a cursor which you can move with your either a joystick, or a mouse.)
« Last Edit: February 06, 2021, 03:29:20 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Virtual Keyboard
« Reply #9 on: February 06, 2021, 03:48:15 am »
 more contrast on the keys

I get an error on line 2

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Virtual Keyboard
« Reply #10 on: February 06, 2021, 03:53:36 am »
more contrast on the keys

I get an error on line 2

You need the latest version of QB64 to compile it.  We’re pushing for a new version (1.5), and I’m trying to help test things while working on my code.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Virtual Keyboard
« Reply #11 on: February 06, 2021, 04:05:21 am »
Steve

I like the calculator idea!



Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Virtual Keyboard
« Reply #12 on: February 06, 2021, 10:15:19 am »
So this has been turned into a library type system, and I've returned the TYPEs to v1.4 and earlier compatibility.

Program looks like the following:
Code: QB64: [Select]
  1. '$INCLUDE:'Keyboard Library.BI'
  2. '$INCLUDE:'Virtual Keyboard.BI'
  3. Repeat_Speed = 0.1 'Global variable in the Virtual Keyboard library which a user can change for repeat speed
  4.  
  5.  
  6. SCREEN _NEWIMAGE(800, 600, 32)
  7.  
  8.  
  9. 'CREATE YOUR CUSTOM KEYBOARD LAYOUT HERE
  10. DIM My_Keyboard(5) AS STRING
  11. My_Keyboard(0) = CHR$(0) + "27,ESC" + STRING$(2,0) + "15104,F1" + STRING$(2,0) + "15360,F2" + _
  12.     STRING$(2,0) + "15616,F3" + STRING$(2,0) + "15872,F4" + STRING$(2,0) + "16128,F5" + _
  13.     STRING$(2,0) + "16384,F6" + STRING$(2,0) + "16640,F7" + STRING$(2,0) + "16896,F8" + _
  14.     STRING$(2,0) + "17152,F9" + STRING$(2,0) + "17408,F10" + STRING$(2,0) + "34048,F11" + _
  15.     STRING$(2,0) + "34304,F12" + CHR$(0)
  16. My_Keyboard(1) = "`1234567890-=" + CHR$(0) + "19200,BKSP" + CHR$(0)
  17. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "QWERTYUIOP[]\"
  18. My_Keyboard(3) = CHR$(0) + "100301,KB2" + CHR$(0) + "ASDFGHJKL;'" + CHR$(0) + "13,ENTER" + CHR$(0)
  19. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "ZXCVBNM,./" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  20. My_Keyboard(5) = CHR$(0) + "100306,CTRL" + STRING$(2,0) + "100311,WIN" + STRING$(2,0) + "100308,ALT" + _
  21.     STRING$(2,0) + "32,SPACE" + STRING$(2,0) + "100307,ALT" + STRING$(2,0) + "100312,WIN" + STRING$(2,0) + "100319,MENU" + _
  22.     STRING$(2,0) + "100305,CTRL" +  CHR$(0)
  23.  
  24. font = _LOADFONT("Courbd.ttf", 14, "monospace") 'IF USED ON A KEYBOARD, DON'T FREE THIS FONT
  25. '                                                IT GETS REUSED WHEN DRAWING KEYS UP/DOWN STATE
  26.  
  27. '     Honestly, I like the look with the standard font 16 here better, but I wanted to load a custom font
  28. '     just so folks could see how to make use of it.
  29. FullsizeKB1 = Create_KB(My_Keyboard(), font, 55, 35)
  30.  
  31. '     And here's where I'm redefining my keys to toggle for my second keyboard
  32. My_Keyboard(1) = "~!@#$%^&*()_+" + CHR$(0) + "19200,BKSP" + CHR$(0)
  33. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "qwertyuiop{}|"
  34. My_Keyboard(3) = CHR$(0) + "100301,KB1" + CHR$(0) + "asdfghjkl:" + CHR$(34) + CHR$(0) + "13,ENTER" + CHR$(0)
  35. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "zxcvbnm<>?" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  36. FullsizeKB2 = Create_KB(My_Keyboard(), font, 55, 35)
  37.  
  38.  
  39. Keyboard_In_Use = FullsizeKB1 'Set the keyboard I'm currently using
  40.     Display_KB Keyboard_In_Use, 10, 100, 0
  41.     WHILE _MOUSEINPUT: WEND 'must update mouse buffer before reading virtual keyboard
  42.     k = KeyHit 'The library version which reads all the keys for us, not the qb64 _KEYHIT version
  43.     '(ONLY FOR WINDOWS. LINUX/MAC USERS STILL GET THE SAME OLE BUGGY _KEYHIT FOR NOW.  SORRY.)
  44.     IF k = 0 THEN k = check_KB(Keyboard_In_Use) 'this checks the virtual keyboard
  45.     SELECT CASE k
  46.         CASE 100301 'swap keyboards, rather than having a CAPS LOCK key
  47.             IF Keyboard_In_Use = FullsizeKB1 THEN
  48.                 Keyboard_In_Use = FullsizeKB2
  49.             ELSE
  50.                 Keyboard_In_Use = FullsizeKB1
  51.             END IF
  52.         CASE 1 TO 900000 '900001 are mouse buttons, which I don't want to print to the screen and add to any
  53.             '             confusion.
  54.             PRINT k;
  55.             IF k > 0 AND k < 255 THEN PRINT CHR$(k);
  56.             PRINT ,
  57.     END SELECT
  58.     _DISPLAY
  59.     _LIMIT 30
  60. LOOP UNTIL k = 27
  61.  
  62.  
  63. '$INCLUDE:'Keyboard Library.BM'
  64. '$INCLUDE:'Virtual Keyboard.BM'
  65.  

Library Files are below.
« Last Edit: February 08, 2021, 03:00:58 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Virtual Keyboard
« Reply #13 on: February 07, 2021, 07:15:58 am »
And now I think I can finally say that I have the core of this library worked out nicely for me.

Included in the 7z archive is now the compiled EXE for folks on windows to use, who may not be up to the latest moment with the development build and get compile errors.

We can now click on keys and get the codes back for those keys with VK_Keyhit.   (Jazzy hard name to remember right?  We want to get Keyhiy codes from our Virtual Keyboard...  VK_Keyhit!  Don't I have amazingly spectacular naming sense?)

If you hold a key down, it'll now repeat itself over and over for you.  (Wish my dang phone would do that so I wouldn't have to tappy tap tappy tap on double letters and all!)

If you double click on a button, it'll now hold that button down forever more for you!  (It won't spam you with VK_KeyHit codes, though.  I took inspiration from how SHIFT and ALT act with _KEYHIT -- they only report the down and up event, but not the hold time.)  If you want to check to see if a button is held down, or not, just use the VK_KeyDown command!!

We have both virtual keuhit events and virtual keydown events, so make use of both of them, if you're going to use the library.  ;)

There's still a few things I want to play around with and tweak later, but the root of this library is now about done.  I want to go in and tweak so we can highlight buttons, or give letters a little backlighting (like a LED backlit keyboard), as well as tweak the shadowing and such to make it more customizable -- but that's all cosmetic stuff.  Unless unforeseen glitches pop up, the main part of this library is now up and functional -- and it plays quite nicely with the Extended Keyboard library of mine, so folks can custom map their own keyboards to whatever input they want in Windows.  (Everywhere else, we just wrap around back to a standard _KEYHIT instead of the modified Keyhit. :P )
« Last Edit: February 08, 2021, 03:00:44 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Marked as best answer by SMcNeill on February 07, 2021, 10:01:04 pm

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Virtual Keyboard
« Reply #14 on: February 08, 2021, 02:59:39 am »
As far as I can tell, I'd call this done -- at least until other folks try it out and report any sort of errors or user requests with it.  It's to the point now, that I'm willing to stick a 1.0 version label on the library, as everything appears to be working as intended with the tests I've been doing with it during development.

Here's a screenshot of two full-sized keyboards which we make with just a few quick lines of code:

Code: QB64: [Select]
  1. '$INCLUDE:'Keyboard Library.BI'
  2. '$INCLUDE:'Virtual Keyboard.BI'
  3. Repeat_Speed = 0.2 'Global variable in the Virtual Keyboard library which a user can change for repeat speed
  4.  
  5. SCREEN _NEWIMAGE(800, 600, 32)
  6.  
  7. 'CREATE YOUR CUSTOM KEYBOARD LAYOUT HERE
  8. DIM My_Keyboard(5) AS STRING
  9. My_Keyboard(0) = CHR$(0) + "27,ESC" + STRING$(2,0) + "15104,F1" + STRING$(2,0) + "15360,F2" + _
  10.     STRING$(2,0) + "15616,F3" + STRING$(2,0) + "15872,F4" + STRING$(2,0) + "16128,F5" + _
  11.     STRING$(2,0) + "16384,F6" + STRING$(2,0) + "16640,F7" + STRING$(2,0) + "16896,F8" + _
  12.     STRING$(2,0) + "17152,F9" + STRING$(2,0) + "17408,F10" + STRING$(2,0) + "34048,F11" + _
  13.     STRING$(2,0) + "34304,F12" + CHR$(0)
  14. My_Keyboard(1) = "`1234567890-=" + CHR$(0) + "19200,BKSP" + CHR$(0)
  15. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "QWERTYUIOP[]\"
  16. My_Keyboard(3) = CHR$(0) + "100301,KB2" + CHR$(0) + "ASDFGHJKL;'" + CHR$(0) + "13,ENTER" + CHR$(0)
  17. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "ZXCVBNM,./" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  18. My_Keyboard(5) = CHR$(0) + "100306,CTRL" + STRING$(2,0) + "100311,WIN" + STRING$(2,0) + "100308,ALT" + _
  19.     STRING$(2,0) + "32,SPACE" + STRING$(2,0) + "100307,ALT" + STRING$(2,0) + "100312,WIN" + STRING$(2,0) + "100319,MENU" + _
  20.     STRING$(2,0) + "100305,CTRL" +  CHR$(0)
  21.  
  22. font = _LOADFONT("Courbd.ttf", 14, "monospace") 'IF USED ON BUTTONS, DON'T FREE THIS FONT AS IT GETS REUSED
  23.  
  24. '     Honestly, I like the look with the standard font 16 here better, but I wanted to load a custom font
  25. '     just so folks could see how to make use of it.
  26.  
  27. 'DEFINING MY BUTTON STYLES FOR SHADING AND EFFECT
  28. Button_Style_Up = Register_Button(50, 50, 50, 150, 150, 150, 8, font, &HFFFFFF00, 0)
  29. Button_Style_Down = Register_Button(150, 150, 150, 110, 110, 110, 8, font, &HFFFFFF00, 0)
  30. FullsizeKB1 = Create_KB(My_Keyboard(), 55, 35, Button_Style_Up, Button_Style_Down)
  31.  
  32. Button_Style_Up = Register_Button(50, 50, 150, 150, 150, 250, 8, 16, &HFFFFFF00, 0)
  33. Button_Style_Down = Register_Button(150, 150, 250, 110, 110, 210, 8, 16, &HFFFFFF00, 0)
  34. My_Keyboard(3) = CHR$(0) + "100301,KB3" + CHR$(0) + "ASDFGHJKL;'" + CHR$(0) + "13,ENTER" + CHR$(0)
  35. FullsizeKB2 = Create_KB(My_Keyboard(), 55, 35, Button_Style_Up, Button_Style_Down)
  36.  
  37. '     And here's where I'm redefining my keys to toggle for my second keyboard
  38. My_Keyboard(1) = "~!@#$%^&*()_+" + CHR$(0) + "19200,BKSP" + CHR$(0)
  39. My_Keyboard(2) = CHR$(0) + "9,TAB" + CHR$(0) + "qwertyuiop{}|"
  40. My_Keyboard(3) = CHR$(0) + "100301,KB4" + CHR$(0) + "asdfghjkl:" + CHR$(34) + CHR$(0) + "13,ENTER" + CHR$(0)
  41. My_Keyboard(4) = CHR$(0) + "100304,SHIFT" + CHR$(0) + "zxcvbnm<>?" + CHR$(0) + "100303,SHIFT" + CHR$(0)
  42.  
  43. Button_Style_Up = Register_Button(50, 50, 50, 150, 150, 150, 8, font, &HFFFFFF00, 0)
  44. Button_Style_Down = Register_Button(150, 150, 150, 110, 110, 110, 8, font, &HFFFFFF00, 0)
  45. FullsizeKB3 = Create_KB(My_Keyboard(), 55, 35, Button_Style_Up, Button_Style_Down)
  46.  
  47. Button_Style_Up = Register_Button(50, 50, 150, 150, 150, 250, 8, 16, &HFFFFFF00, 0)
  48. Button_Style_Down = Register_Button(150, 150, 250, 110, 110, 210, 8, 16, &HFFFFFF00, 0)
  49. My_Keyboard(3) = CHR$(0) + "100301,KB1" + CHR$(0) + "asdfghjkl:" + CHR$(34) + CHR$(0) + "13,ENTER" + CHR$(0)
  50. FullsizeKB4 = Create_KB(My_Keyboard(), 55, 35, Button_Style_Up, Button_Style_Down)
  51.  
  52. Keyboard_In_Use = FullsizeKB1 'Set the keyboard I'm currently using
  53.     Display_KB Keyboard_In_Use, 10, 380, -1
  54.     WHILE _MOUSEINPUT: WEND 'must update mouse buffer before reading virtual keyboard
  55.     k = KeyHit 'The library version which reads all the keys for us, not the qb64 _KEYHIT version
  56.     '(ONLY FOR WINDOWS. LINUX/MAC USERS STILL GET THE SAME OLE BUGGY _KEYHIT FOR NOW.  SORRY.)
  57.     IF k = 0 OR k > 900000 THEN k = VK_Keyhit(Keyboard_In_Use) 'this checks the virtual keyboard
  58.     IF VK_Keydown(32) THEN PRINT "Space held"; 'and here we can check for virtual keys being held down.
  59.     SELECT CASE k
  60.         CASE 100301 'swap keyboards, rather than having a CAPS LOCK key
  61.             Keyboard_In_Use = (Keyboard_In_Use + 1) MOD 4
  62.             _DELAY .2 'we need a delay here, as we haven't actually pressed any key on the new keyboard
  63.             'so the keys aren't going to have a down timer to stop repeats.. We'd probably change keyboards
  64.             'multiple times quickly without it, before we lifted our finger up off the mouse button.
  65.         CASE 27
  66.             SYSTEM
  67.         CASE 1 TO 900000 '900001 are mouse buttons, which I don't want to print to the screen and add to any
  68.             '             confusion.
  69.             PRINT k;
  70.             IF k > 0 AND k < 255 THEN PRINT CHR$(k);
  71.             PRINT ,
  72.     END SELECT
  73.     _DISPLAY
  74.     _LIMIT 30
  75. '$INCLUDE:'Keyboard Library.BM'
  76. '$INCLUDE:'Virtual Keyboard.BM'

80 lines of code -- and most of those are comments, I think -- and here's what the keyboards look like that we display and interact with:
 
multi-keyboards.png


I haven't went into much description on how to make use of this library yet, but so far, nobody has really seemed that interested in it.  If it's only going to be for my personal use, then there's no need for me to spend a lot of time writing up a nice tutorial for everyone else to use.  Just a few little crib notes to refresh my own memory when I plug it into my projects should be more than enough for me, personally....

...So if anyone is interested in making use of this library, and can't figure it out even after studying over the demo code (personally, I think the layout, setup, and use is rather simple overall), speak up and ask questions.  The more interest there is in this work, the more time I'll spend in making certain that I write up and clarify any common issues that others have utilizing it.
* Virtual Keyboard and Custom Keyhit Library v1.0.7z (Filesize: 680.88 KB, Downloads: 163)
« Last Edit: February 08, 2021, 03:29:16 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!