Skip to content

Commit

Permalink
Support typing of Unicode Characters (similar to keyTap)
Browse files Browse the repository at this point in the history
Tested on Windows and Mac.
Unit test is added.

Closes octalmage#351
  • Loading branch information
MariaDima committed Jan 2, 2018
1 parent d021733 commit 94ed67c
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 10 deletions.
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface Screen {
export function setKeyboardDelay(ms: number) : void
export function keyTap(key: string, modifier?: string | string[]) : void
export function keyToggle(key: string, down: string, modifier?: string | string[]) : void
export function utf32Tap(utf32dec: number) : void
export function typeString(string: string) : void
export function typeStringDelayed(string: string, cpm: number) : void
export function setMouseDelay(delay: number) : void
Expand Down
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"typings": "index.d.ts",
"scripts": {
"test": "node test/all.js",
"install": "prebuild-install || node-gyp rebuild"
"test-keyboard": "node test/keyboard.js",
"install": "prebuild-install || node-gyp rebuild",
"install-debug": "prebuild-install || node-gyp rebuild --debug"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -37,11 +39,11 @@
},
"homepage": "https://github.com/octalmage/robotjs",
"dependencies": {
"nan": "^2.2.1",
"prebuild-install": "^2.1.1"
"nan": "^2.8.0",
"prebuild-install": "^2.4.1"
},
"devDependencies": {
"tape": "^3.5.0",
"prebuild": "v6.1.0"
"tape": "^4.8.0",
"prebuild": "v7.0.0"
}
}
46 changes: 44 additions & 2 deletions src/keypress.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,15 +219,57 @@ void toggleUnicodeKey(unsigned long ch, const bool down)
CGEventPost(kCGSessionEventTap, keyEvent);
CFRelease(keyEvent);
}
void toggleUniChar(UniChar ch, const bool down)
{
/* This function relies on the convenient
* CGEventKeyboardSetUnicodeString(), which allows us to not have to
* convert characters to a keycode, but does not support adding modifier
* flags. It is therefore only used in typeString() and typeStringDelayed()
* -- if you need modifier keys, use the above functions instead. */
CGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL, 0, down);
if (keyEvent == NULL) {
fputs("Could not create keyboard event.\n", stderr);
return;
}

CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch);

CGEventPost(kCGSessionEventTap, keyEvent);
CFRelease(keyEvent);
}
void toggleUniKey(char c, const bool down)
{
toggleUnicodeKey(c, down);
UniChar ch = (UniChar)c; // Convert to unsigned char

toggleUniChar(ch, down);
}
#else
#define toggleUniKey(c, down) toggleKey(c, down, MOD_NONE)
#endif

void tapUtf32(const unsigned utf32dec)
{
#if defined(IS_MACOSX)
UniChar ch = (UniChar)utf32dec; // Convert to unsigned char

toggleUniChar(ch, true);
toggleUniChar(ch, false);
#elif defined(IS_WINDOWS)
INPUT ip;

// Set up a generic keyboard event.
ip.type = INPUT_KEYBOARD;
ip.ki.wVk = 0; // Virtual-key code
ip.ki.wScan = utf32dec; // Hardware scan code for key
// ip.ki.wScan = 0x03B1;
ip.ki.time = 0; // System will provide its own time stamp.
ip.ki.dwExtraInfo = 0; // No extra info. Use the GetMessageExtraInfo function to obtain this information if needed.
ip.ki.dwFlags = KEYEVENTF_UNICODE; // KEYEVENTF_KEYUP for key release.

SendInput(1, &ip, sizeof(INPUT));
#endif
}

static void tapUniKey(char c)
{
toggleUniKey(c, true);
Expand Down Expand Up @@ -290,4 +332,4 @@ void typeStringDelayed(const char *str, const unsigned cpm)
tapUniKey(*str++);
microsleep(mspc + (DEADBEEF_UNIFORM(0.0, 62.5)));
}
}
}
3 changes: 3 additions & 0 deletions src/keypress.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ void tapKeyCode(MMKeyCode code, MMKeyFlags flags);
void toggleKey(char c, const bool down, MMKeyFlags flags);
void tapKey(char c, MMKeyFlags flags);

/* Sends a UTF-32 character without modifiers. */
void tapUtf32(const unsigned utf32dec);

/* Sends a UTF-8 string without modifiers. */
void typeString(const char *str);

Expand Down
20 changes: 18 additions & 2 deletions src/robotjs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ NAN_METHOD(keyToggle)
}
}

//Get the acutal key.
//Get the actual key.
switch(CheckKeyCodes(k, &key))
{
case -1:
Expand All @@ -561,12 +561,25 @@ NAN_METHOD(keyToggle)
break;
default:
toggleKeyCode(key, down, flags);
microsleep(keyboardDelay);
microsleep(keyboardDelay);
}

info.GetReturnValue().Set(Nan::New(1));
}

NAN_METHOD(utf32Tap)
{
size_t utf32dec = info[0]->Int32Value();

if (utf32dec != 0) {
tapUtf32(utf32dec);

info.GetReturnValue().Set(Nan::New(1));
} else {
return Nan::ThrowError("Invalid character typed.");
}
}

NAN_METHOD(typeString)
{
char *str;
Expand Down Expand Up @@ -844,6 +857,9 @@ NAN_MODULE_INIT(InitAll)
Nan::Set(target, Nan::New("keyToggle").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(keyToggle)).ToLocalChecked());

Nan::Set(target, Nan::New("utf32Tap").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(utf32Tap)).ToLocalChecked());

Nan::Set(target, Nan::New("typeString").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(typeString)).ToLocalChecked());

Expand Down
18 changes: 17 additions & 1 deletion test/keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ var os = require('os');

test('Tap a key.', function(t)
{
t.plan(4);
t.plan(5);
t.ok(robot.keyTap("a") === 1, 'successfully tapped "a".');
t.ok(robot.keyTap("control") === 1, 'successfully tapped "ctrl".');
t.ok(robot.keyTap("a", "control") === 1, 'successfully tapped "ctrl+a".');

t.throws(function()
Expand Down Expand Up @@ -57,3 +58,18 @@ test('Tap all numpad keys.', function(t)

t.end();
});

test('Tap a UTF32 character.', function(t)
{
t.plan(6);
t.ok(robot.utf32Tap("r".charCodeAt(0)) === 1, 'successfully tapped "r".');
t.ok(robot.utf32Tap("ά".charCodeAt(0)) === 1, 'successfully tapped "ά".');
t.ok(robot.utf32Tap("ö".charCodeAt(0)) === 1, 'successfully tapped "ö".');
t.ok(robot.utf32Tap("ち".charCodeAt(0)) === 1, 'successfully tapped "ち".');
t.ok(robot.utf32Tap("嗨".charCodeAt(0)) === 1, 'successfully tapped "嗨".');

t.throws(function()
{
robot.utf32Tap();
}, /Invalid character typed./, 'tap nothing.');
});

0 comments on commit 94ed67c

Please sign in to comment.