5

I have been trying to find a way to remap my keyboard and send 5-digit hex unicode chars, the method described here: ahk Send only supports 4-digit hex codes {U+nnnn}, I know that in the past, autohotkey didnt support unicode natively so it was needed some functions in order to do that, maybe thats the solution for me.

Example:

#If GetKeyState("CapsLock","T")
+u::Send {U+1D4B0}

The results from that is 풰 instead of , and the code for 풰 is {U+D4B0}, meaning AHK is reading only the last 4 digits. How can I fix it even if I need to make new functions to achieve that?

Thanks

-Mark

2501
  • 25,460
  • 4
  • 47
  • 87
markymark
  • 53
  • 4

4 Answers4

4

Unicode values larger than 0xFFFF must be encoded as two surrogate pairs:

+u:: SendInput ,{U+D835}{U+DCB0}

Here is the algorithm to convert a Unicode code point that is in range 0x10000 to 0x10FFFF, to surrogate pairs, paraphrased from wikipedia:

First subtract 0x10000 from the code point, to get a number in range 0xFFFFF.

Then right shift the number by 10 bits and add 0xD800 to it to get the high surrogate.

Take the lowest ten bits of the number and add 0xDC00 to it to get the low surrogate

2501
  • 25,460
  • 4
  • 47
  • 87
  • Thanks! This one works, now I need to learn about surrogate pairs, could you show me where I can learn more about that? Or even a table with all the codes would be very helpful. – markymark Apr 12 '16 at 15:21
  • Wow. I spent HOURS trying to figure this out over the course of a few months. Tried a lot of clever things. Then I realized the problem was only with 5 digit unicode points, googled it, and found this answer. And it works great! – KANJICODER Aug 01 '17 at 19:20
0

I took 2501's solution from above and turned it into working a working ahk script. I have been searching on and off for this solution for months!

+u:: FUNC_SEND_UNICODE( 0x1D4B0 )

FUNC_SEND_UNICODE(func_args)
{

    /*
    ;commented out proof of concept code:
    str := ""
    u1  := Chr( 0xD835 )
    u2  := Chr( 0xDCB0 )
    str := u1 . u2

    bk := clipboard
    clipboard := str
    send, ^v

    sleep,200
    clipboard := bk
    */

    ;bk == "clipboard [B]ac[K]up
    bk := clipboard


    ;chunk of data that needs to be cut into
    ;two chunks.
    ful := func_args + 0


    /*  commented out testing code, using original
        sample input of stack overflow post
    ;msgbox,ful:[%ful%]
    ;for testing. Expecting input to be equal to
    ;the value in the post.
    if(ful != 0x1D4B0 ){
        msgbox,"[WHATTHEHECK]"
    }
    */


    ;Subtract 0x10000 from ful, gets number 
    ;in range(rng) 0x0 to 0xFFFFFF inclusive
    rng := ful - 0x10000
    if(rng > 0xFFFFF)
    {
        msgBox,[Step1 Resulted In Out Of Range]
    }

    ;Do shifting and masking, then check to make
    ;sure the value is in the expected range:
    big_shif := (rng >>   10)
    lit_mask := (rng & 0x3FF)
    if(big_shif > 0x3FF)
    {
        msgBox,[MATH_ERROR:big_shif]
    }
    if(lit_mask > 0x3FF)
    {
        msgBox,[MATH_ERROR:lit_mask]
    }

    big := big_shif + 0xD800
    lit := lit_mask + 0xDC00
    if(big < 0xD800 || big >= 0xDBFF){
        msgBox, [HIGH_SURROGATE_OUT_OF_BOUNDS]
    }
    if(lit < 0xDC00 || lit >= 0xDFFF){
        msgBox, [LOW_SURROGATE_OUT_OF_BOUNDS]
    }

    ;Convert code points to actual characters:
    u1 := Chr( big )
    u2 := Chr( lit )

    ;concatentate those two characters to
    ;create our final surrogate output:
    str := u1 . u2

    ;set it equal to clipboard, and send
    ;the clipboard. This is a hack.
    ;send,%str% works fine in google chrome,
    ;but fails in notepad++ for some reason.
    ;tried appending EOF, STX, ETX control codes
    ;along with output, but to no effect.
    clipboard := str
    send, ^v

    ;Must sleep before restoring clipboard,
    ;otherwise, the clipboard will get
    ;overwritten before ctrl+v has the chance
    ;to output the character. You'll end up just
    ;pasting whatever was originally on the
    ;clipboard.
    sleep,200
    clipboard := bk

    return
}
KANJICODER
  • 3,611
  • 30
  • 17
0

An implementation of 2501's answer.

  1. Listens for ;u
  2. Followed by an ending character (e.g. ; or Return)
  3. Waits for a sequence of keypresses like 1D4B0
  4. Input ends when terminated by a ;
  5. Sends the desired Unicode character directly if below 65536, calculates surrogate pairs otherwise
  6. Inserts the desired Unicode character

:?:`;u::
    Input, keys, , `;
    if (StrLen(keys) < 5)
        Send {U+%keys%}
    else {
        keys := "0x" + keys
        num := keys - 0x10000
        w1 := Format("{:x}", (num >> 10) + 0xD800)
        w2 := Format("{:x}", (num & 1023) + 0xDC00)
        Send {U+%w1%}{U+%w2%}
    }
    return

For reference:

TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97
-1

From somewhere on the internet: sendunicodechar(0x1D4B0)

SendUnicodeChar(charCode)
{
    VarSetCapacity(ki, 28 * 2, 0)
    EncodeInteger(&ki + 0, 1)
    EncodeInteger(&ki + 6, charCode)
    EncodeInteger(&ki + 8, 4)
    EncodeInteger(&ki +28, 1)
    EncodeInteger(&ki +34, charCode)
    EncodeInteger(&ki +36, 4|2)

    DllCall("SendInput", "UInt", 2, "UInt", &ki, "Int", 28)
}

EncodeInteger(ref, val)
{
    DllCall("ntdll\RtlFillMemoryUlong", "Uint", ref, "Uint", 4, "Uint", val)
}

edit. probably got downvoted bc of missing source. I simply dont know where I got it from anymore. But when I used it a few years ago, it worked flawlessly.

phil294
  • 10,038
  • 8
  • 65
  • 98