본문으로 건너뛰기

예제

구문과 기본 제공 함수 영역을 다루는 실제 동작하는 스크립트입니다. 주제별로 필터링하거나 코드와 텍스트를 검색할 수 있습니다. Context* 기본 제공 함수를 사용하는 스크립트에는 제스처 또는 단축키 트리거가 필요합니다. 진단 콘솔에서는 이러한 기본 제공 함수가 빈 기본값을 반환합니다.

Hello, 콘솔​

가장 작은 스크립트입니다. UtilityPrint는 진단 콘솔에 출력하며, 콘솔에서 실행하지 않은 스크립트라면 출력 로그에 기록합니다.

UtilityPrint("Hello from Input.Observer");

다섯 가지 값 형식​

각 형식의 변수를 하나씩 만들어 StringFormat으로 출력합니다. StringFormat은 Text가 아닌 값을 Text로 바꾸는 유일한 방법입니다.

count = 3; // Integer
ratio = 0.75; // Real
name = "Notepad"; // Text
enabled = true; // Bool
w = WindowGetActive(); // Window

UtilityPrint(StringFormat("count={0} ratio={1}", count, ratio)); // count=3 ratio=0.750000
UtilityPrint(StringFormat("name={0} enabled={1}", name, enabled)); // name=Notepad enabled=true
UtilityPrint(StringFormat("window handle={0}", w, ""));

모든 형식의 참/거짓 판정​

if가 각 형식에서 참으로 보는 값: 0이 아닌 숫자, 비어 있지 않은 Text, null이 아닌 핸들.

if (0) UtilityPrint("never");
if (-1) UtilityPrint("-1 is true");
if (0.0) UtilityPrint("never");
if ("") UtilityPrint("never");
if (" ") UtilityPrint("a single space is true");
if ("false") UtilityPrint("the text \"false\" is true -- it's not empty");
if (!WindowFindByTitle("no window has this title zzz")) UtilityPrint("null handle is false");

else-if 연쇄​

커서의 가로 위치가 커서 아래 모니터의 세 구역 중 어디에 있는지 구분합니다.

x = MouseGetCursorX();
y = MouseGetCursorY();
left = DisplayMonitorGetRectXFromPoint(x, y, false);
width = DisplayMonitorGetRectWidthFromPoint(x, y, false);

offset = x - left;
if (offset < width / 3) {
zone = "left";
} else if (offset < width * 2 / 3) {
zone = "center";
} else {
zone = "right";
}
UtilityPrint("Cursor is in the " + zone + " third");

카운트 루프: 증가, 감소, 일정 간격​

for의 세 부분은 모두 원하는 할당 또는 식으로 작성할 수 있습니다.

sum = 0;
for (i = 1; i <= 100; i = i + 1) sum = sum + i;
UtilityPrint(StringFormat("1..100 = {0}", sum, "")); // 5050

for (i = 5; i > 0; i = i - 1) UtilityPrint(StringFormat("{0}...", i, ""));

for (p = 1; p <= 1024; p = p * 2) UtilityPrint(StringFormat("{0}", p, "")); // powers of two

중첩 루프: 구구단​

중첩된 for 루프로 행마다 Text 한 줄을 만듭니다.

for (r = 1; r <= 5; r = r + 1) {
line = "";
for (c = 1; c <= 5; c = c + 1) {
line = line + StringFormat("{0}\t", r * c, "");
}
UtilityPrint(line);
}

while 루프: 시간 제한을 두고 창 기다리기​

조건이 "while" 역할을 하고, 카운터가 최대 5초로 제한합니다.

ShellRun("notepad.exe");
w = WindowFindByTitle("Notepad");
for (tries = 0; !w && tries < 50; tries = tries + 1) {
UtilityWait(100);
w = WindowFindByTitle("Notepad");
}
if (w) {
WindowCenterToScreen(w);
} else {
UtilityPrint("Notepad didn't appear within 5 seconds");
}

종료 플래그가 있는 while (true)​

자기 자신에 할당하는 증감식(go = go)으로, 본문에서 플래그를 해제할 때만 멈추는 루프를 만듭니다. 모든 작업 중지 단축키로도 항상 종료할 수 있습니다.

rolls = 0;
for (go = true; go; go = go) {
rolls = rolls + 1;
if (MathRandom(1, 6) == 6) go = false;
}
UtilityPrint(StringFormat("Rolled a six after {0} tries", rolls, ""));

break와 continue​

break하려면 카운터를 한도 너머로 설정합니다. continue하려면 본문의 나머지를 if 안에 넣습니다.

n = StringSplit("keep,,skip-me,keep too,STOP,never reached", ",");
for (i = 0; i < n; i = i + 1) {
part = StringGetSplitPartAt(i);
if (part == "STOP") {
i = n; // break
} else if (part && !StringStartsWith(part, "skip")) { // continue for empty / skip-*
UtilityPrint(part);
}
}

예상과 다른 우선순위​

C의 우선순위 함정 두 가지와 이를 바로잡는 명시적 괄호입니다.

flags = 6;
a = flags & 4 == 4; // flags & (4 == 4) -> 6 & true -> 0 (Bool operand)
b = (flags & 4) == 4; // true

x = 5;
c = 0 < x < 10; // (0 < x) < 10 -> true < 10 -> false (Bool vs Integer)
d = x > 0 && x < 10; // true

UtilityPrint(StringFormat("a={0} b={1}", a, b));
UtilityPrint(StringFormat("c={0} d={1}", c, d));

&&와 ||는 양쪽을 모두 평가합니다​

두 피연산자는 항상 실행되므로, 오른쪽에 부수 효과가 있거나 실패할 수 있을 때 중요합니다.

StorageSetValue("calls", 0);

// Both sides run even though the left is false; the right side's
// StorageSetValue still happens.
if (false && StorageSetValue("calls", 1)) { }
UtilityPrint(StringFormat("calls = {0}", StorageGetValue("calls"), "")); // calls = 1

// Guard a call that could fail with a nested if, not &&:
text = "no colon here";
idx = StringGetIndexOf(text, ":");
if (idx >= 0) {
UtilityPrint(StringGetSubstring(text, idx + 1, 100));
}

형식이 다른 값의 같음 비교​

Integer와 Real은 숫자로 비교됩니다. 그 밖에 형식이 다른 모든 쌍은 단순히 같지 않습니다.

UtilityPrint(StringFormat("5 == 5.0 -> {0}", 5 == 5.0, "")); // true
UtilityPrint(StringFormat("1 == true -> {0}", 1 == true, "")); // false
UtilityPrint(StringFormat("\"5\" == 5 -> {0}", "5" == 5, "")); // false
UtilityPrint(StringFormat("\"a\" == \"A\" -> {0}", "a" == "A", "")); // false (case-sensitive)
w = WindowFindByTitle("zzz no such window");
UtilityPrint(StringFormat("w == 0 -> {0}, !w -> {1}", w == 0, !w)); // false, true

혼합 형식 산술은 0으로 대체됩니다​

연산자가 이해하지 못하는 조합은 오류 없이 Integer 0을 반환합니다. 결과가 이상해 보이면 값을 출력해 보십시오.

a = "Total: " + 5; // 0 -- not "Total: 5"
b = true + 1; // 0
c = -"text"; // 0
d = 2.5 & 1; // 0 -- bitwise needs Integers
e = "Total: " + StringFormat("{0}", 5, ""); // "Total: 5"
UtilityPrint(StringFormat("{0} {1}", a, b) + StringFormat(" {0} {1} ", c, d) + e);

주석, 빈 문, 블록​

줄 주석과 블록 주석, 불필요한 세미콜론, 최상위 블록(범위를 만들지 않음)입니다.

// line comment
/* block comment
spanning lines */
x = 1;; // the extra ';' is an empty statement
{
y = x + 1; /* blocks group statements... */
}
UtilityPrint(StringFormat("y = {0}", y, "")); // ...but y is still visible here

Integer와 Real 나눗셈, 0으로 나누기​

정수 나눗셈은 0 방향으로 버림하고, 피연산자 중 하나가 Real이면 결과도 Real이며, 0으로 나누면 0이 됩니다.

a = 7 / 2; // 3
b = 7 / 2.0; // 3.5
c = 7 * 1.0 / 2; // 3.5 -- convert first, then divide
d = -7 / 2; // -3 (toward zero)
e = 5 / 0; // 0, no error
UtilityPrint(StringFormat("{0} {1}", a, b) + StringFormat(" {0} {1}", c, d) + StringFormat(" {0}", e, ""));

% 없이 나머지 구하기​

n - (n / d) * d는 C의 %와 같습니다. 제수가 2의 거듭제곱이면 음수가 아닌 숫자에 대해 마스크로도 같은 결과를 얻습니다.

n = 17;
d = 5;
rem = n - (n / d) * d; // 2
isEven = (n & 1) == 0; // false
low3 = n & 7; // 17 % 8 == 1
UtilityPrint(StringFormat("17 % 5 = {0}, even = {1}", rem, isEven));
UtilityPrint(StringFormat("17 % 8 = {0}", low3, ""));

반올림과 Real 수학 기본 제공 함수​

기본 제공 함수가 Real로 선언한 자리에 전달한 Integer는 Real이 됩니다. 반올림에서 0.5는 0에서 먼 쪽으로 처리됩니다.

n = 16;
root = MathSqrt(n); // 4.0 -- n is widened to 16.0

UtilityPrint(StringFormat("round 2.5 -> {0}", MathRound(2.5), "")); // 3
UtilityPrint(StringFormat("round -2.5 -> {0}", MathRound(-2.5), "")); // -3
UtilityPrint(StringFormat("floor -1.5 -> {0}", MathFloor(-1.5), "")); // -2
UtilityPrint(StringFormat("ceil 1.01 -> {0}", MathCeil(1.01), "")); // 2
UtilityPrint(StringFormat("sqrt 16 -> {0}", MathRound(root), "")); // 4

값을 범위 안으로 제한하기​

MathClamp는 MathMin/MathMax처럼 두 숫자 형식을 모두 받으며, 선택된 값을 원래 형식 그대로 반환합니다.

v = 300;
v = MathClamp(v, 0, 255); // 255
dist = MathAbs(-42); // 42 (Integer stays Integer)
UtilityPrint(StringFormat("clamped={0} abs={1}", v, dist));

난수와 동전 던지기​

MathRandom은 범위의 양쪽 끝을 포함합니다.

die = MathRandom(1, 6);
if (MathRandom(0, 1) == 1) side = "heads"; else side = "tails";
UtilityPrint(StringFormat("die={0}, coin={1}", die, side));

제스처 획의 길이​

연속된 제스처 점 사이의 직선 거리를 합산합니다. 제곱의 합인 Integer는 MathSqrt에 전달될 때 Real이 됩니다.

n = ContextGetPointCount();
total = 0.0;
for (i = 1; i < n; i = i + 1) {
dx = ContextGetPointX(i) - ContextGetPointX(i - 1);
dy = ContextGetPointY(i) - ContextGetPointY(i - 1);
total = total + MathSqrt(dx * dx + dy * dy);
}
UtilityPrint(StringFormat("{0} points, {1} px long", n, MathRound(total)));

마우스를 원 모양으로 움직이기​

도를 라디안으로 바꾸고, Real로 삼각 함수를 계산한 뒤, MathRound로 다시 Integer 픽셀 좌표로 바꿉니다.

cx = MouseGetCursorX();
cy = MouseGetCursorY();
radius = 80.0;
for (deg = 0; deg <= 360; deg = deg + 10) {
rad = deg * MathGetPi() / 180;
MouseMoveTo(cx + MathRound(radius * MathCos(rad)), cy + MathRound(radius * MathSin(rad)));
UtilityWait(15);
}
MouseMoveTo(cx, cy);

소수 여섯 자리 없이 Real 서식 지정하기​

배율을 적용하고 반올림하여 백분율을 얻거나, 값을 정수부와 100분의 1 단위로 나누어 소수 두 자리로 표시합니다. 음수가 아닌 값에 사용할 수 있습니다.

ratio = 2.0 / 3.0;
pct = MathRound(ratio * 100); // 67

value = 3.14159;
cents = MathRound(value * 100); // 314
whole = cents / 100; // 3
frac = cents - whole * 100; // 14
if (frac < 10) fracText = StringFormat("0{0}", frac, ""); else fracText = StringFormat("{0}", frac, "");
UtilityPrint(StringFormat("{0}% and {1}", pct, StringFormat("{0}.", whole, "") + fracText)); // 67% and 3.14

플래그 마스크: 설정, 해제, 전환, 검사​

직접 정의한 플래그 값에 대한 네 가지 표준 마스크 연산입니다.

BOLD = 0x1;
ITALIC = 0x2;
UNDERLINE = 0x4;

style = BOLD | UNDERLINE; // 0b101 = 5
style = style | ITALIC; // set -> 7
style = style & ~BOLD; // clear -> 6
style = style ^ UNDERLINE; // toggle -> 2

if ((style & ITALIC) != 0) UtilityPrint("italic is on");
if ((style & BOLD) == 0) UtilityPrint("bold is off");
UtilityPrint(StringFormat("style = {0}", style, ""));

커서 아래 픽셀 색 읽기​

시프트와 마스크로 0xRRGGBB Integer를 분해합니다.

c = DisplayGetPixelColorFromPoint(MouseGetCursorX(), MouseGetCursorY());
if (c < 0) {
UtilityPrint("The cursor isn't over any monitor");
} else {
r = (c >> 16) & 0xFF;
g = (c >> 8) & 0xFF;
b = c & 0xFF;
UtilityPrint(StringFormat("R={0} G={1}", r, g) + StringFormat(" B={0}", b, ""));
if (r > 200 && g < 80 && b < 80) UtilityPrint("That's red");
}

Integer를 16진수 텍스트로​

16진수 서식을 지정하는 기본 제공 함수가 없으므로, 4비트씩 떼어 내어 각 자릿수를 Text에서 찾습니다.

digits = "0123456789ABCDEF";
value = 48879;
hex = "";
for (v = value; v > 0; v = v >> 4) {
hex = StringGetSubstring(digits, v & 0xF, 1) + hex;
}
if (!hex) hex = "0";
UtilityPrint("0x" + hex); // 0xBEEF

설정된 비트 개수 세기​

64비트 전체를 시프트하면서 최하위 비트를 하나씩 더합니다.

v = 0xB6; // 1011 0110
bits = 0;
for (i = 0; i < 64; i = i + 1) {
bits = bits + ((v >> i) & 1);
}
UtilityPrint(StringFormat("0xB6 has {0} bits set", bits, "")); // 5

시프트의 경계 사례​

>>는 부호를 유지하며, 0–63 범위를 벗어난 이동 크기는 0을 반환합니다.

UtilityPrint(StringFormat("1 << 10 = {0}", 1 << 10, "")); // 1024
UtilityPrint(StringFormat("-16 >> 2 = {0}", -16 >> 2, "")); // -4
UtilityPrint(StringFormat("1 << 64 = {0}", 1 << 64, "")); // 0
UtilityPrint(StringFormat("~0 = {0}", ~0, "")); // -1

두 Integer 맞바꾸기​

일반적인 방법은 임시 변수가 필요합니다. XOR을 사용하면 임시 변수 없이 맞바꿀 수 있습니다.

a = 12; b = 34;
t = a; a = b; b = t; // with a temporary
a = a ^ b; b = a ^ b; a = a ^ b; // XOR swap back
UtilityPrint(StringFormat("a={0} b={1}", a, b)); // a=12 b=34

키 상태 비트​

KeyboardGetKeyState는 Win32 원시 값을 반환합니다. 최상위 비트는 키가 눌려 있음을, 최하위 비트는 키가 켜져 있음을 뜻합니다. Is* 기본 제공 함수가 같은 검사를 대신 해 줍니다.

caps = KeyboardGetKeyState(VirtualKey.CapsLock);
shift = KeyboardGetKeyState(VirtualKey.ShiftKey);
UtilityPrint(StringFormat("Caps Lock on: {0}, Shift held: {1}", (caps & 1) != 0, (shift & 0x8000) != 0));
UtilityPrint(StringFormat("same via builtins: {0}, {1}",
KeyboardIsKeyToggled(VirtualKey.CapsLock), KeyboardIsKeyDown(VirtualKey.ShiftKey)));

문자열 이스케이프와 Windows 경로​

경로의 모든 백슬래시를 두 번 쓰십시오. \t, \n, \r은 이스케이프이며, 그 밖의 백슬래시는 제거됩니다.

good = "C:\\Temp\\report.txt"; // C:\Temp\report.txt
bad = "C:\Temp\new.txt"; // C:<no backslash>Temp<line break>ew.txt
quote = "He said \"stop\"";
tabs = "Name\tValue";
cont = "one logical line, \
written on two";
UtilityPrint(good);
UtilityPrint(bad);
UtilityPrint(quote);
UtilityPrint(tabs);
UtilityPrint(cont);

값 두 개보다 많이 서식 지정하기​

StringFormat은 {0}과 {1}만 채웁니다. 호출을 중첩하거나 여러 결과를 +로 연결하십시오.

w = WindowGetActive();
x = WindowGetX(w); y = WindowGetY(w);
width = WindowGetWidth(w); height = WindowGetHeight(w);

// nest: the inner call leaves "{0},{1}" for the outer call to fill
a = StringFormat(StringFormat("{0}x{1} at ", width, height) + "{0},{1}", x, y);
// concatenate: each call formats its own piece
b = StringFormat("pos {0},{1}", x, y) + StringFormat(" size {0}x{1}", width, height);
UtilityPrint(a);
UtilityPrint(b);

분할하고 반복하기​

StringSplit은 개수를 반환하고, StringGetSplitPartAt이 각 부분을 읽습니다. 인접한 두 구분 기호 사이의 부분은 비어 있습니다.

n = StringSplit("red,green,,blue", ",");
for (i = 0; i < n; i = i + 1) {
UtilityPrint(StringFormat("{0}: [{1}]", i, StringGetSplitPartAt(i)));
}
// 0: [red] 1: [green] 2: [] 3: [blue]

중첩 분할: 키=값 쌍​

안쪽 분할이 바깥쪽 분할의 부분을 대체하므로, 매 반복이 시작될 때 바깥쪽 목록을 다시 분할합니다.

config = "width=800;height=600;title=Main";
n = StringSplit(config, ";");
for (i = 0; i < n; i = i + 1) {
StringSplit(config, ";"); // restore the outer parts...
pair = StringGetSplitPartAt(i); // ...and copy this one out
StringSplit(pair, "="); // now the cache holds key, value
UtilityPrint(StringFormat("{0} -> {1}", StringGetSplitPartAt(0), StringGetSplitPartAt(1)));
}

마지막 인덱스: 파일 확장명​

LastIndexOf가 없으므로 모든 위치를 확인하고 마지막으로 일치한 위치를 유지합니다.

file = "report.final.v2.pdf";
last = -1;
len = StringGetLength(file);
for (i = 0; i < len; i = i + 1) {
if (StringGetSubstring(file, i, 1) == ".") last = i;
}
if (last >= 0) {
ext = StringGetSubstring(file, last + 1, len - last - 1);
base = StringGetSubstring(file, 0, last);
UtilityPrint(StringFormat("base={0} ext={1}", base, ext)); // base=report.final.v2 ext=pdf
}

사용자가 입력한 숫자 읽기​

false를 지정하면 사용자의 지역 형식으로 읽습니다(독일 사용자는 1.234,5로 입력). 이때 자릿수 그룹은 해당 형식을 따라야 합니다. 한국어(대한민국) 사용자에게 1,234.5는 숫자이지만 1,5는 숫자가 아닙니다. StringIsNumber는 숫자가 아닌 텍스트를 잡아내며, StringToNumber만 사용하면 그런 텍스트가 0이 됩니다.

text = UIShowInputBox("Width in pixels:", "Resize", "800");
if (!StringIsNumber(text, false)) {
UIShowMessageBox("Not a number: " + text, "Resize", "OK", "Warning");
} else {
width = StringToNumber(text, false); // Integer 800, or a Real if they typed a fraction
shown = StringFromNumber(width * 1.5, 1, false); // "1,200.0" in en-US, "1.200,0" in de-DE
UtilityPrint("one and a half times that: " + shown);
}

프로그램을 실행하고 창을 기다린 다음 작업하기​

WindowWaitFor는 시간 제한(최대 60초)까지 폴링하며, 일치하는 창이 없으면 null 창을 반환합니다. 패턴은 제목에 대한 대/소문자 구분 없는 정규식입니다. 프로그램의 제목은 Windows 표시 언어를 따르므로 바뀌지 않는 부분과 일치시키십시오.

ShellRun("notepad.exe");
w = WindowWaitFor("notepad$", 10000);
if (w) {
WindowSetActive(w);
KeyboardTypeText("Notes for today:\n");
WindowWaitClose(w, 60000); // carries on once the user closes it, or after a minute
UtilityPrint("Notepad is gone");
} else {
UtilityPrint("Notepad didn't open in 10 seconds");
}

정규식으로 복사한 텍스트에서 값 추출하기​

PCRE2 구문을 사용하며, 패턴이 (?i)로 시작하지 않으면 대/소문자를 구분하고, 유니코드를 인식합니다(\w가 악센트 문자와 일치). 그룹 0은 전체 일치 항목입니다. 패턴의 백슬래시는 스크립트 문자열 안에서 \\로 씁니다. 바꿀 문자열의 $1은 그룹 1입니다. Window*Regex/Shell*Regex 기본 제공 함수도 같은 엔진을 사용하며, 항상 대/소문자를 구분하지 않습니다.

text = ClipboardGetText();
if (StringRegexMatch(text, "(?i)order\\s*#?(\\d+)")) {
UtilityPrint("order number: " + StringRegexGetGroupAt(1));
} else {
UtilityPrint("no order number on the clipboard");
}
// every run of spaces, tabs and line breaks becomes one space; "#1234" becomes "order 1234"
text = StringRegexReplace(text, "\\s+", " ");
ClipboardSetText(StringRegexReplace(text, "#(\\d+)", "order $1"));

숫자 앞을 0으로 채우기​

Text가 충분히 길어질 때까지 앞에 문자를 붙입니다.

n = 7;
s = StringFormat("{0}", n, "");
for (s = s; StringGetLength(s) < 3; s = s) {
s = "0" + s;
}
UtilityPrint(s); // 007

Text 뒤집기​

Text를 UTF-16 단위 하나씩 거꾸로 읽습니다. 기본 다국어 평면(BMP) 밖의 문자(대부분의 이모지)는 두 단위이므로 쪼개져서 출력됩니다.

text = "Input.Observer";
rev = "";
for (i = StringGetLength(text) - 1; i >= 0; i = i - 1) {
rev = rev + StringGetSubstring(text, i, 1);
}
UtilityPrint(rev); // revresbO.tupnI

클립보드의 단어 수 세기​

탭과 줄 바꿈을 공백으로 바꾸고, 공백으로 분할한 다음, 비어 있지 않은 부분의 수를 셉니다.

text = ClipboardGetText();
text = StringReplace(text, "\n", " ");
text = StringReplace(text, "\t", " ");
n = StringSplit(text, " ");
words = 0;
for (i = 0; i < n; i = i + 1) {
if (StringTrim(StringGetSplitPartAt(i))) words = words + 1;
}
UtilityPrint(StringFormat("{0} words", words, ""));

대/소문자를 구분하지 않는 비교​

모든 Text 연산자와 기본 제공 함수는 대/소문자를 구분하므로 양쪽을 소문자로 바꿉니다.

title = WindowGetTitle(WindowGetActive());
if (StringContains(StringToLower(title), "visual studio")) {
UtilityPrint("In Visual Studio");
}
exe = WindowGetExecutableName(WindowGetActive());
if (StringToLower(exe) == "notepad.exe") UtilityPrint("In Notepad");

Text 순서는 서수 비교입니다​

<는 UTF-16 코드 단위를 비교합니다. 모든 ASCII 대문자가 모든 소문자보다 앞에 옵니다.

UtilityPrint(StringFormat("\"apple\" < \"banana\" : {0}", "apple" < "banana", "")); // true
UtilityPrint(StringFormat("\"Zebra\" < \"apple\" : {0}", "Zebra" < "apple", "")); // true
UtilityPrint(StringFormat("\"apple\" < \"Banana\" : {0}", "apple" < "Banana", "")); // false
a = "apple"; b = "Banana";
UtilityPrint(StringFormat("case-folded : {0}", StringToLower(a) < StringToLower(b), "")); // true

템플릿을 채워 붙여 넣기​

StringReplace로 %NAME% 자리 표시자를 채웁니다. 붙여 넣기 전후로 클립보드를 저장하고 복원합니다.

template = "Hi %NAME%,\n\nThanks for the report on %APP%.\n";
text = StringReplace(template, "%NAME%", "Sam");
text = StringReplace(text, "%APP%", ContextGetApplicationName());
ClipboardSave();
ClipboardPasteReplacementText(text);
UtilityWait(150); // let the target app read the clipboard first
ClipboardRestore();

클립보드의 여러 줄을 한 줄로 합치기​

\n으로 분할합니다. StringTrim은 Windows 줄 끝의 \r도 제거하므로, 줄이 \r\n으로 끝나든 \n만으로 끝나든 동작합니다.

n = StringSplit(ClipboardGetText(), "\n");
out = "";
for (i = 0; i < n; i = i + 1) {
part = StringTrim(StringGetSplitPartAt(i));
if (part) {
if (out) out = out + " ";
out = out + part;
}
}
ClipboardSetText(out);

선택한 텍스트로 웹 검색하기​

ClipboardCopySelection은 짐작하는 대신 복사가 완료될 때까지 기다립니다. StringUrlEncode는 &, #, 공백, 악센트 문자 때문에 주소가 깨지지 않게 합니다. 전체 URL이 아니라 검색어만 인코딩하십시오.

ClipboardSave();
term = StringTrim(ClipboardCopySelection(1000));
ClipboardRestore();
if (term) ShellRun("https://www.bing.com/search?q=" + StringUrlEncode(term));

제스처 하나, 여러 선택지​

UIShowMenu는 한 줄에 항목 하나를 받으며(-만 있는 줄은 구분선), 0부터 세고 구분선을 건너뛴 선택한 항목의 인덱스를 반환하고, 메뉴를 닫으면 -1을 반환합니다. UIShowInputBox는 [취소]를 누르면 ""를 반환합니다.

choice = UIShowMenu("Maximize\nMinimize\n-\nRename window...");
w = ContextGetWindow();
if (choice == 0) WindowMaximize(w);
else if (choice == 1) WindowMinimize(w);
else if (choice == 2) {
title = UIShowInputBox("New title for this window:", "Rename", WindowGetTitle(w));
if (title) WindowSetTitle(w, title);
}

오늘 날짜와 타임스탬프가 붙은 파일 이름​

DateTimeGetNow는 ISO 8601 텍스트를 반환합니다. DateTimeFormat은 이를 사용자 방식으로 쓰거나, 시간순으로 정렬되고 파일 이름에 안전한 FileStamp로 씁니다.

now = DateTimeGetNow();
KeyboardTypeText(DateTimeFormat(now, DateTimeStyle.LongDate));
path = ShellExpandEnvironmentVariables("%USERPROFILE%\\Desktop\\note-") + DateTimeFormat(now, DateTimeStyle.FileStamp) + ".txt";
FileCreate(path, ClipboardGetText());
if (DateTimeGetPart(now, DateTimePart.Hour) >= 17) UtilityPrint("after hours");

명명된 상수와 숫자 그대로 쓰기​

상수는 해당 숫자로 컴파일되므로 이 호출들은 동일합니다. 16진수는 Win32 문서와 일치합니다.

KeyboardPressKey(VirtualKey.VolumeMute);
KeyboardPressKey(0xAD); // the same key, as a hex VK code
KeyboardPressKey(173); // and in decimal
UtilityPrint(StringFormat("VirtualKey.A = {0}, MouseButton.Right = {1}", VirtualKey.A, MouseButton.Right));

제스처의 창 최대화 전환​

가장 흔한 제스처 작업입니다. ContextGetWindow는 제스처가 시작된 위치의 창입니다.

w = ContextGetWindow();
if (WindowIsMaximized(w)) WindowRestore(w); else WindowMaximize(w);

창을 맨 위에 고정하기​

WindowSetTopmost는 항상 위를 명시적으로 설정하고, WindowToggleTopmost는 이를 전환합니다. 창 속성이 이 스크립트에서 선택한 상태를 기억합니다.

w = ContextGetWindow();
pinned = WindowGetPropertyInteger(w, "io.pinned") == 1;
WindowSetTopmost(w, !pinned);
if (pinned) {
WindowRemoveProp(w, "io.pinned");
} else {
WindowSetPropertyInteger(w, "io.pinned", 1);
ShellShowToast("Pinned on top", WindowGetTitle(w));
}

창 투명도 순환하기​

불투명도를 255에서 105까지 단계적으로 낮춘 다음 다시 255로 되돌립니다. 계층화되지 않은 창은 255를 보고합니다.

w = ContextGetWindow();
a = WindowGetAlpha(w) - 50;
if (a < 100) a = 255;
WindowSetAlpha(w, a);

활성 창을 모니터의 왼쪽 절반에 맞추기​

창이 작업 표시줄을 가리지 않도록 작업 영역(true)을 사용합니다.

w = WindowGetActive();
cx = WindowGetX(w) + WindowGetWidth(w) / 2;
cy = WindowGetY(w) + WindowGetHeight(w) / 2;
x = DisplayMonitorGetRectXFromPoint(cx, cy, true);
y = DisplayMonitorGetRectYFromPoint(cx, cy, true);
width = DisplayMonitorGetRectWidthFromPoint(cx, cy, true);
height = DisplayMonitorGetRectHeightFromPoint(cx, cy, true);
WindowRestore(w);
WindowMoveTo(w, x, y);
WindowResizeTo(w, width / 2, height);

커서 아래의 3×2 격자 셀에 창 맞추기​

Region* 기본 제공 함수는 사각형을 셀로 나누고, 점이 들어 있는 셀을 찾고, 그 경계를 알려 줍니다.

mx = MouseGetCursorX();
my = MouseGetCursorY();
x = DisplayMonitorGetRectXFromPoint(mx, my, true);
y = DisplayMonitorGetRectYFromPoint(mx, my, true);
w = DisplayMonitorGetRectWidthFromPoint(mx, my, true);
h = DisplayMonitorGetRectHeightFromPoint(mx, my, true);

cell = RegionGetCellIndexAt(x, y, w, h, 3, 2, mx, my);
win = ContextGetWindow();
if (cell >= 0 && win) {
WindowRestore(win);
WindowMoveTo(win, RegionGetX(x, y, w, h, 3, 2, cell), RegionGetY(x, y, w, h, 3, 2, cell));
WindowResizeTo(win, RegionGetWidth(x, y, w, h, 3, 2, cell), RegionGetHeight(x, y, w, h, 3, 2, cell));
}

창을 다음 모니터로 보내기​

두 번째 인수를 사용하면 마우스도 창과 함께 이동합니다.

WindowSendToNextScreen(ContextGetWindow(), true);

표시되는 최상위 창 나열하기​

열거한 다음 인덱스로 읽으며, 숨겨진 창, 클로킹된 창, 제목 없는 창은 건너뜁니다.

n = WindowGetAllTopLevel();
shown = 0;
for (i = 0; i < n; i = i + 1) {
w = WindowGetEnumeratedAt(i);
if (WindowGetVisible(w)) {
if (!WindowIsCloaked(w)) {
title = WindowGetTitle(w);
if (title) {
UtilityPrint(StringFormat("{0} [{1}]", title, WindowGetExecutableName(w)));
shown = shown + 1;
}
}
}
}
UtilityPrint(StringFormat("{0} of {1} windows", shown, n));

한 앱의 모든 창 최소화하기​

대/소문자를 구분하지 않는 정규식으로 실행 파일과 일치시킵니다. \\.에 주의하십시오. 정규식 \.를 스크립트 문자열 안에 쓴 것입니다.

n = WindowFindAllByModuleRegex("notepad\\.exe$");
for (i = 0; i < n; i = i + 1) {
WindowMinimize(WindowGetEnumeratedAt(i));
}
UtilityPrint(StringFormat("Minimized {0} Notepad windows", n, ""));

확인 후 제목 패턴으로 창 닫기​

제목에 대한 정규식, 메시지에 표시하는 개수, 클릭한 단추 확인입니다.

n = WindowFindAllByTitleRegex("^Untitled - ");
if (n > 0) {
answer = UIShowMessageBox(StringFormat("Close {0} untitled window(s)?", n, ""),
"Tidy up", "YesNo", "Question");
if (answer == "Yes") {
for (i = 0; i < n; i = i + 1) WindowClose(WindowGetEnumeratedAt(i));
}
}

창 위치를 기억하고 복원하기​

창 속성은 창이 닫힐 때까지 실행 간에 Integer를 창 자체에 보관합니다. 한 번도 설정하지 않은 속성은 0으로 읽힙니다.

w = ContextGetWindow();
if (WindowGetPropertyInteger(w, "io.saved") == 1) {
WindowMoveTo(w, WindowGetPropertyInteger(w, "io.x"), WindowGetPropertyInteger(w, "io.y"));
WindowRemoveProp(w, "io.saved");
WindowRemoveProp(w, "io.x");
WindowRemoveProp(w, "io.y");
} else {
WindowSetPropertyInteger(w, "io.x", WindowGetX(w));
WindowSetPropertyInteger(w, "io.y", WindowGetY(w));
WindowSetPropertyInteger(w, "io.saved", 1);
WindowCenterToScreen(w);
}

창의 자식 컨트롤 살펴보기​

자식 창은 최상위 열거와 같은 창 캐시를 사용합니다.

w = ContextGetWindow();
n = WindowGetAllChildren(w, true);
for (i = 0; i < n; i = i + 1) {
c = WindowGetEnumeratedAt(i);
UtilityPrint(StringFormat("{0}: {1}", WindowGetClassName(c), WindowGetTitle(c)));
}

프로세스에서 창으로​

실행 파일 이름으로 프로세스 ID를 열거한 다음 각 프로세스의 창을 찾습니다.

n = ShellEnumerateProcessIdsByExeRegex("^explorer\\.exe$");
for (i = 0; i < n; i = i + 1) {
pid = ShellGetEnumeratedProcessIdAt(i);
w = WindowFromProcessId(pid);
UtilityPrint(StringFormat("pid {0}: {1}", pid, WindowGetTitle(w)));
}

커서 아래에 있는 것 설명하기​

점 아래의 컨트롤, 그 최상위 창, 클라이언트 좌표로 변환한 점입니다.

x = MouseGetCursorX();
y = MouseGetCursorY();
control = WindowControlFromPoint(x, y);
top = WindowGetRoot(control);
UtilityPrint(StringFormat("control class: {0}; window: {1}", WindowGetClassName(control), WindowGetTitle(top)));
UtilityPrint(StringFormat("client point: {0},{1}",
WindowMapScreenPointToClientX(top, x, y), WindowMapScreenPointToClientY(top, x, y)));

창을 트레이에 숨기기​

알림 영역으로 최소화합니다. TrayRestoreAllWindows()는 숨긴 창을 모두 되돌립니다.

TrayMinimizeWindow(ContextGetWindow());

트리거 컨텍스트가 아는 모든 정보​

제스처에 연결하면 그 스크립트가 읽을 수 있는 정보를 확인할 수 있습니다.

UtilityPrint(StringFormat("action={0} gesture={1}", ContextGetActionName(), ContextGetGestureName()));
UtilityPrint(StringFormat("app={0} window={1}", ContextGetApplicationName(), WindowGetTitle(ContextGetWindow())));
UtilityPrint(StringFormat("box x={0} y={1}", ContextGetBoundingBoxX(), ContextGetBoundingBoxY())
+ StringFormat(" w={0} h={1}", ContextGetBoundingBoxWidth(), ContextGetBoundingBoxHeight()));
UtilityPrint(StringFormat("points={0} button={1}", ContextGetPointCount(), ContextGetStrokeButton()));

획은 어느 방향으로 그려졌나요?​

첫 번째 점과 마지막 점을 비교하여 우세한 축을 고릅니다.

n = ContextGetPointCount();
if (n > 1) {
dx = ContextGetPointX(n - 1) - ContextGetPointX(0);
dy = ContextGetPointY(n - 1) - ContextGetPointY(0);
if (MathAbs(dx) > MathAbs(dy)) {
if (dx > 0) dir = "right"; else dir = "left";
} else {
if (dy > 0) dir = "down"; else dir = "up";
}
UtilityPrint("The stroke went " + dir);
}

획 단추에 따라 분기하기​

ContextGetStrokeButton은 MouseButton.* 값을 반환하며, 제스처나 마우스 단추 트리거 밖에서는 -1을 반환합니다.

b = ContextGetStrokeButton();
if (b == MouseButton.Secondary) {
WindowMinimize(ContextGetWindow());
} else if (b == MouseButton.Middle) {
WindowClose(ContextGetWindow());
} else if (b == -1) {
UtilityPrint("Not triggered by a mouse button");
}

동그라미 친 영역의 스크린샷 찍기​

제스처의 경계 상자를 사진 폴더에 PNG로 저장합니다.

path = ShellExpandEnvironmentVariables("%USERPROFILE%\\Pictures\\gesture-capture.png");
ok = CaptureSaveRegion(path, ContextGetBoundingBoxX(), ContextGetBoundingBoxY(),
ContextGetBoundingBoxWidth(), ContextGetBoundingBoxHeight());
if (ok) ShellShowToast("Captured", path);

인식되지 않은 그림을 그대로 전달하기​

그리기 앱 위에서 제스처를 그렸다면 획을 일반 끌기 동작으로 앱에 다시 재생합니다.

if (StringToLower(ContextGetApplicationName()) == "mspaint.exe") {
ContextRelayGesture();
}

어딘가를 클릭한 다음 커서를 되돌리기​

커서 위치를 변수에 저장하고, 클릭한 다음, 되돌아옵니다.

x = MouseGetCursorX();
y = MouseGetCursorY();
MouseClick(20, 20, MouseButton.Primary);
UtilityWait(50);
MouseMoveTo(x, y);

창 안의 한 점 클릭하기​

클라이언트 좌표는 창을 어디로 옮겨도 올바르게 유지됩니다.

w = WindowFindByTitle("Calculator");
if (w) {
WindowSetActive(w);
MouseClickAtClientPoint(w, 40, 60, MouseButton.Primary);
}

스크립트로 끌기​

누르고, 단계적으로 이동하고, 놓습니다. 누르기, 이동, 놓기용 기본 제공 함수가 따로 있는 이유입니다.

x = MouseGetCursorX();
y = MouseGetCursorY();
MouseButtonDown(MouseButton.Primary);
for (i = 1; i <= 20; i = i + 1) {
MouseMoveTo(x + i * 10, y);
UtilityWait(10);
}
MouseButtonUp(MouseButton.Primary);

눈금 단위로 스크롤하기​

휠 눈금 하나는 120입니다. 음수는 아래쪽 또는 왼쪽으로 스크롤합니다.

MouseScrollVertical(-3 * 120); // three notches down
MouseScrollHorizontal(120); // one notch right

5초 동안 커서를 창 안에 가두기​

MouseLockToRect는 MouseUnlock을 호출할 때까지 시스템 전체에 적용되므로 항상 짝을 지어 사용하십시오.

w = ContextGetWindow();
MouseLockToRect(WindowGetX(w), WindowGetY(w), WindowGetWidth(w), WindowGetHeight(w));
UtilityWait(5000);
MouseUnlock();

키 조합​

한정자 접두사 ^ Ctrl, + Shift, % Alt, @ Win 뒤에 키 하나를 씁니다.

KeyboardPressKeyCombo("^c"); // Ctrl+C
KeyboardPressKeyCombo("^+{ESC}"); // Ctrl+Shift+Esc
KeyboardPressKeyCombo("@{LEFT}"); // Win+Left
KeyboardPressKeyCombo("%{F4}"); // Alt+F4

서명 입력하기​

KeyboardTypeText는 문자를 입력합니다. 모든 앱이 실제 Enter 키 누름으로 인식하도록 줄 사이에 Enter를 키 조합으로 보내십시오.

KeyboardTypeText("Best regards,");
KeyboardPressKeyCombo("{ENTER}");
KeyboardTypeText("Sam");

Shift+클릭​

마우스 동작 전후로 키를 누르고 있다가 같은 스크립트에서 놓습니다. 누른 채로 둔 키는 계속 눌린 상태로 남습니다.

KeyboardKeyDown(VirtualKey.ShiftKey);
MouseClick(MouseGetCursorX(), MouseGetCursorY(), MouseButton.Primary);
KeyboardKeyUp(VirtualKey.ShiftKey);

미디어 키​

미디어 키와 볼륨 키를 VirtualKey 이름으로 보냅니다.

KeyboardPressKey(VirtualKey.MediaPlayPause);
UtilityWait(200);
KeyboardPressKey(VirtualKey.MediaNextTrack);

Ctrl을 누르고 있는 동안 동작 바꾸기​

현재 키 상태를 확인하여 제스처 하나에 두 가지 의미를 부여합니다.

w = ContextGetWindow();
if (KeyboardIsKeyDown(VirtualKey.ControlKey)) {
WindowClose(w);
} else {
WindowMinimize(w);
}

선택한 텍스트를 대문자로 바꾸기​

선택 영역을 복사하고, 바꾸고, 다시 붙여 넣은 다음, 원래 클립보드에 있던 내용을 복원합니다.

ClipboardSave();
ClipboardClear();
KeyboardPressKeyCombo("^c");
UtilityWait(100);
text = ClipboardGetText();
if (text) {
ClipboardPasteReplacementText(StringToUpper(text));
UtilityWait(100);
}
ClipboardRestore();

선택 영역을 복사하고 검색 URL을 엽니다. 공백은 +가 됩니다. 다른 모든 문자도 인코딩하려면 선택한 텍스트로 웹 검색하기처럼 StringUrlEncode를 사용하십시오.

ClipboardSave();
KeyboardPressKeyCombo("^c");
UtilityWait(100);
q = StringTrim(ClipboardGetText());
ClipboardRestore();
if (q) {
ShellRun("https://www.bing.com/search?q=" + StringReplace(q, " ", "+"));
}

복사한 이미지를 파일로 저장하기​

클립보드의 이미지를 디스크에 쓰고 결과를 알려 줍니다.

path = ShellExpandEnvironmentVariables("%USERPROFILE%\\Desktop\\clipboard.png");
if (ClipboardSaveImage(path)) ShellShowToast("Saved", path);
else UtilityPrint("No image on the clipboard");

로그 파일에 추가하기​

처음에는 머리글과 함께 파일을 만들고, 이후 실행할 때마다 한 줄씩 추가합니다.

log = ShellExpandEnvironmentVariables("%TEMP%\\input-observer-gestures.log");
if (!FileExists(log)) FileCreate(log, "gesture\tapplication\n");
FileAppendText(log, StringFormat("{0}\t{1}\n", ContextGetGestureName(), ContextGetApplicationName()));

파일을 읽고 줄 수 세기​

FileReadText는 파일 전체를 Text로 반환하며, 이를 \n으로 분할합니다.

path = "C:\\Windows\\System32\\drivers\\etc\\hosts";
text = FileReadText(path);
n = StringSplit(text, "\n");
comments = 0;
for (i = 0; i < n; i = i + 1) {
if (StringStartsWith(StringTrim(StringGetSplitPartAt(i)), "#")) comments = comments + 1;
}
UtilityPrint(StringFormat("{0} lines, {1} comments", n, comments));
UtilityPrint(StringFormat("{0} bytes, modified {1}", FileGetSize(path), FileGetModifiedDate(path)));

폴더의 파일 형식별 개수 세기​

하위 폴더 없이 폴더를 열거하고 대/소문자를 구분하지 않고 일치 항목을 셉니다.

folder = ShellExpandEnvironmentVariables("%USERPROFILE%\\Downloads");
n = FolderEnumerateAll(folder, false);
pdf = 0; zip = 0;
for (i = 0; i < n; i = i + 1) {
p = StringToLower(FolderGetEnumeratedPathAt(i));
if (StringEndsWith(p, ".pdf")) pdf = pdf + 1;
else if (StringEndsWith(p, ".zip")) zip = zip + 1;
}
UtilityPrint(StringFormat("{0} PDFs, {1} ZIPs", pdf, zip) + StringFormat(" of {0} entries", n, ""));

편집하기 전에 파일 백업하기​

덮어쓰기로 복사한 다음 내용을 바꿉니다.

path = ShellExpandEnvironmentVariables("%USERPROFILE%\\notes.txt");
if (FileExists(path)) {
FileCopy(path, path + ".bak", true);
FileEditText(path, StringToUpper(FileReadText(path)));
}

폴더 감시하기​

FileNotify 플래그를 |로 결합합니다. 감시의 스크립트는 Text이므로 안쪽 따옴표는 이스케이프합니다. 무슨 일이 일어났는지는 ContextGetWatch*로 읽습니다.

FolderWatchCreate("downloads",
ShellExpandEnvironmentVariables("%USERPROFILE%\\Downloads"),
false,
FileNotify.FileName | FileNotify.LastWrite,
"UtilityPrint(ContextGetWatchAction() + \": \" + ContextGetWatchPath());");
// later: FolderWatchDelete("downloads");

횟수를 세는 반복 타이머​

타이머의 스크립트는 자체 변수를 가지므로, Storage가 실행 간에 횟수를 전달합니다. 1초 간격으로 5번 실행됩니다.

StorageSetValue("ticks", 0);
TimerCreate("counter", 1000, 1000, 5,
"t = StorageGetValue(\"ticks\") + 1; StorageSetValue(\"ticks\", t); UtilityPrint(StringFormat(\"tick {0}\", t, \"\"));");

이스케이프가 필요 없는 코드 조각 기반 타이머 스크립트​

긴 타이머 스크립트는 코드 조각에 두고 그 텍스트를 전달하면 이스케이프할 중첩 따옴표가 없습니다.

// Fires once, 10 minutes from now (repeat count 1).
TimerCreate("stretch-reminder", 600000, 1000, 1, SnippetGetScript("Stretch reminder"));

타이머 나열 및 중지하기​

이름 있는 타이머를 열거하고, 하나 또는 전부를 삭제합니다.

n = TimerEnumerateAll();
for (i = 0; i < n; i = i + 1) UtilityPrint(TimerGetEnumeratedNameAt(i));
TimerDelete("counter");
// TimerDeleteAll();

실행 간에 유지되는 전환​

한 번도 설정하지 않은 키의 StorageGetValue는 거짓인 Integer 0이므로, 처음 실행하면 모드가 켜집니다. 값은 Input.Observer가 종료될 때까지 유지되며, Persistent 기본 제공 함수를 사용하면 다시 시작한 뒤에도 유지됩니다.

on = !StorageGetValue("quietMode");
StorageSetValue("quietMode", on);
MultimediaSetMute(AudioEndpoint.Playback, on);
if (on) ShellShowToast("Quiet mode", "On"); else ShellShowToast("Quiet mode", "Off");

다시 시작해도 유지되는 카운터​

Storage*Persistent* 기본 제공 함수는 구성 옆의 storage.toml에 값을 보관하므로 Input.Observer를 다시 시작한 뒤에도 남아 있습니다. 일반 텍스트이며 암호화되지 않으므로 암호를 보관하지 마십시오. Window는 보관할 수 없습니다. 핸들은 다시 시작한 뒤에는 의미가 없습니다. Input.Observer가 시작될 때 storage.toml이 있지만 읽을 수 없으면 StorageSetPersistentValue는 false를 반환합니다. 이때 값은 Input.Observer가 종료될 때까지만 유지되며, 다음 시작 때까지 아무것도 저장되지 않습니다.

n = StorageGetPersistentValue("gestures.used") + 1; // 0 the first time ever
StorageSetPersistentValue("gestures.used", n);
UtilityPrint(StringFormat("{0} gestures since this counter started", n, ""));

Storage에 보관하는 목록​

배열은 없지만 StringFormat으로 만든 번호 붙은 키가 배열처럼 동작합니다.

count = StorageGetValue("recent.count");
StorageSetValue(StringFormat("recent.{0}", count, ""), WindowGetTitle(ContextGetWindow()));
StorageSetValue("recent.count", count + 1);

for (i = 0; i <= count; i = i + 1) {
UtilityPrint(StorageGetValue(StringFormat("recent.{0}", i, "")));
}

한 번에 한 작업만 특정 구간 실행하기​

이름 있는 잠금은 겹쳐 실행되는 두 작업이 서로 섞이지 않게 합니다. 결과를 LockResult와 비교하여 확인하고, 끝나면 잠금을 해제하십시오.

r = UtilityLockAcquire("clipboard-work", 5);
if (r == LockResult.Acquired) {
ClipboardSave();
// ...work that must not overlap another run...
ClipboardRestore();
UtilityLockRelease("clipboard-work");
} else {
UtilityPrint("Another action is busy with the clipboard");
}

질문하기​

단추와 아이콘은 Text 이름입니다. 결과는 클릭한 단추의 이름입니다.

answer = UIShowMessageBox("Save the layout before closing?", "Input.Observer", "YesNoCancel", "Question");
if (answer == "Yes") {
UtilityPrint("saving");
} else if (answer == "No") {
UtilityPrint("discarding");
} else {
UtilityPrint("cancelled");
}

화면 표시와 함께 볼륨 높이기​

볼륨은 0.0에서 1.0까지의 Real입니다. UIShowDisplayMessage는 위치 기반 인수 19개를 받습니다.

v = MathMin(MultimediaGetVolume(AudioEndpoint.Playback) + 0.05, 1.0);
MultimediaSetVolume(AudioEndpoint.Playback, v);
UIShowDisplayMessage(
"Volume", StringFormat("{0}%", MathRound(v * 100), ""),
1200, // durationMs
0.9, // opacity (Real)
Location.BottomCenter, // or a "x,y" Text point
"Segoe UI", 14, true, false, // title font, size, bold, italic
"Segoe UI", 12, false, false, // message font, size, bold, italic
"#1F1F1F", "#F7F7F5", // fore / back color: name, #RRGGBB or "R,G,B"
12, // padding
false, // usePrimaryScreen
TextAlign.Center, TextAlign.Center);

실시간으로 업데이트되는 표시 메시지​

세션 ID를 보관했다가 같은 19개 인수와 함께 UIUpdateDisplayMessage에 전달합니다.

id = UIShowDisplayMessage("Working", "0%", 10000, 0.95, Location.TopRight,
"Segoe UI", 12, true, false, "Segoe UI", 11, false, false,
"black", "255,255,255", 10, true, TextAlign.Left, TextAlign.Left);
for (p = 0; p <= 100; p = p + 20) {
UIUpdateDisplayMessage(id, "Working", StringFormat("{0}%", p, ""), 10000, 0.95, Location.TopRight,
"Segoe UI", 12, true, false, "Segoe UI", 11, false, false,
"black", "255,255,255", 10, true, TextAlign.Left, TextAlign.Left);
UtilityWait(400);
}
UICloseDisplayMessage(id);

Windows 알림​

제목과 메시지를 알림 메시지로 표시합니다.

ShellShowToast("Input.Observer", StringFormat("Profile: {0}", GestureProfileGetActiveId(), ""));

마이크 음소거 전환하기​

Capture 엔드포인트는 기본 녹음 장치입니다.

MultimediaToggleMute(AudioEndpoint.Capture);
if (MultimediaGetMute(AudioEndpoint.Capture)) ShellShowToast("Microphone", "Muted");
else ShellShowToast("Microphone", "Live");

소리 재생하기​

경로로 지정한 WAV 및 MP3 파일을 재생합니다.

MultimediaPlayWavFile("C:\\Windows\\Media\\chimes.wav");

앱을 실행하거나 앱으로 전환하기​

앱이 이미 실행 중이면 활성화하고, 그렇지 않으면 시작합니다.

ShellRunOrActivate("notepad.exe");

동사와 창 스타일을 지정하여 프로그램 실행하기​

동사는 ShellVerb 상수이거나 Text로 쓴 임의의 동사입니다. 창 스타일은 WindowStyle 상수입니다.

ShellRunProgram("notepad.exe", "C:\\Temp\\todo.txt", ShellVerb.Open, WindowStyle.Maximized, false);
ShellRunProgram("C:\\Temp\\report.pdf", "", "print", WindowStyle.Hidden, false); // Text verb

실행하고 종료될 때까지 기다리기​

waitForExit를 true로 설정하면 프로그램이 닫힐 때까지 스크립트의 스레드가 차단됩니다. 그동안에도 입력은 계속 전달됩니다. 모든 작업을 중지하면 대기가 끝나며, 프로그램은 계속 실행되고 호출은 false를 반환합니다.

ShellRunProgram("notepad.exe", "", ShellVerb.Open, WindowStyle.Normal, true);
ShellShowToast("Notepad", "was closed");

환경 변수 확장하기​

모든 사용자의 컴퓨터에서 동작하는 경로를 만듭니다.

UtilityPrint(ShellExpandEnvironmentVariables("%APPDATA%\\Input.Observer"));
UtilityPrint(ShellExpandEnvironmentVariables("%COMPUTERNAME% / %USERNAME%"));

AutoHotkey에 넘기기​

AutoHotkey 코드를 별도 프로세스에서 실행하고 종료 코드를 반환하며, AutoHotkey가 설정되지 않았으면 -1을 반환합니다.

rc = AutoHotkeyExecuteScript("SoundBeep 750, 200");
if (rc == -1) UtilityPrint("AutoHotkey isn't set up");

모니터 나열하기​

각 모니터의 이름, 전체 경계, 작업 영역, DPI를 인덱스 순으로 표시합니다.

n = DisplayMonitorEnumeratedAll();
for (i = 0; i < n; i = i + 1) {
UtilityPrint(StringFormat("#{0} {1}", i, DisplayMonitorGetEnumeratedFriendlyNameAt(i)));
UtilityPrint(StringFormat(" bounds {0} x {1}", DisplayMonitorGetEnumeratedWidthAt(i, false), DisplayMonitorGetEnumeratedHeightAt(i, false))
+ StringFormat(", work area {0} x {1}", DisplayMonitorGetEnumeratedWidthAt(i, true), DisplayMonitorGetEnumeratedHeightAt(i, true))
+ StringFormat(", {0} dpi", DisplayMonitorGetEnumeratedDpiAt(i), ""));
}

창을 특정 모니터로 보내기​

열거 결과의 인덱스로 지정하며, 해당 모니터가 없으면 다음 화면으로 대체합니다.

w = ContextGetWindow();
if (DisplayMonitorEnumeratedAll() > 1) {
WindowSendToMonitorIndex(w, 1, false);
} else {
WindowSendToNextScreen(w, false);
}

다음 제스처 프로필로 순환하기​

활성 프로필의 인덱스를 찾아 1을 더하고, 나머지 관용구로 처음으로 되돌립니다.

n = GestureProfileEnumerateAll();
if (n > 0) {
active = GestureProfileGetActiveId();
next = 0;
for (i = 0; i < n; i = i + 1) {
if (GestureProfileGetEnumeratedIdAt(i) == active) next = i + 1;
}
next = next - (next / n) * n; // next % n
GestureProfileSwitch(GestureProfileGetEnumeratedIdAt(next));
ShellShowToast("Gesture profile", GestureProfileGetEnumeratedNameAt(next));
}

엔진 상태​

엔진이 사용 안 함 상태인지 또는 안전 모드인지 읽은 다음 일시 중지합니다. 트레이 메뉴에서 다시 사용하도록 설정하십시오.

UtilityPrint(StringFormat("disabled={0} safeMode={1}", EngineIsDisabled(), EngineIsSafeMode()));
if (!EngineIsDisabled()) {
EngineDisable();
ShellShowToast("Input.Observer", "Paused. Re-enable it from the tray menu.");
}

다음 오른쪽 끌기를 그대로 통과시키기​

제스처 캡처를 한 번 건너뛰므로, 다음 오른쪽 단추 끌기는 앱으로 전달됩니다.

EngineDisableNextGesture();

재사용 가능한 함수로서의 코드 조각​

인수와 결과는 Storage를 통해 전달합니다. 코드 조각은 다음 줄이 실행되기 전에 끝까지 실행됩니다.

// Snippet "Tile window" reads tile.index and positions ContextGetWindow() accordingly.
StorageSetValue("tile.index", 2);
SnippetExecuteScript("Tile window");
UtilityPrint(StringFormat("snippet reported: {0}", StorageGetValue("tile.result"), ""));

플러그 인과 통신하기​

연결된 플러그 인에 Text 명령을 보내고, 시간 제한까지 Text 응답을 기다립니다.

reply = PluginSendMessage("MyRenderer", "theme dark", 2);
UtilityPrint("Plugin replied: " + reply);

직렬 장치에 질문하기​

포트를 열고, 한 줄을 쓰고, 응답을 최대 2초 동안 기다린 다음 포트를 닫습니다. 패리티와 정지 비트는 상수입니다.

if (SerialOpenPort("COM3", 9600, SerialParity.None, 8, SerialStopBits.One, "\n")) {
SerialWriteTextLine("COM3", "PING", 9600);
reply = SerialGetTextLine("COM3", 2, 9600);
SerialClosePort("COM3");
if (reply) UtilityPrint("Device: " + reply); else UtilityPrint("No reply");
}

직렬 장치의 단추를 미디어 키에 매핑하기​

모니터는 받은 줄마다 스크립트를 실행합니다. ContextGetSerialTextLine이 해당 줄입니다.

SerialMonitorCreate("pad", "COM3", 115200, SerialParity.None, 8, SerialStopBits.One, "\n",
"line = StringTrim(ContextGetSerialTextLine()); if (line == \"B1\") KeyboardPressKey(VirtualKey.MediaPlayPause); else if (line == \"B2\") KeyboardPressKey(VirtualKey.MediaNextTrack);");

COM 포트 나열하기​

지금 존재하는 포트를 보여 줍니다. 예를 들어 Arduino를 방금 연결했을 때 할당된 포트를 확인할 수 있습니다. 직렬 장치와 Arduino를 참조하십시오.

n = SerialEnumeratePorts();
if (n == 0) UtilityPrint("No COM ports. Is the board plugged in?");
for (i = 0; i < n; i = i + 1) {
UtilityPrint(SerialGetEnumeratedPortAt(i));
}

Arduino의 포트를 열어 두고 명령 보내기​

포트가 열릴 때 다시 시작되는 보드가 명령을 놓치지 않도록 포트를 한 번만 연 다음, 한 줄을 보내고 응답을 읽습니다.

// Load global event: open the port once, at the sketch's speed. An Uno or Nano
// restarts every time its port opens, so a port opened per command would lose
// the command while the board boots.
if (!SerialOpenPort("COM4", 115200, SerialParity.None, 8, SerialStopBits.One, "")) {
UtilityPrint("COM4 isn't available: unplugged, or open in the Arduino IDE's Serial Monitor?");
}

// Any gesture or hotkey: send a command line and read the board's reply.
// (The sketch reads a line with Serial.readStringUntil('\n') and answers OK.)
if (SerialWriteTextLine("COM4", "LED 255 0 0", 115200)) {
reply = SerialGetTextLine("COM4", 1, 115200);
if (reply != "OK") UtilityPrint(StringFormat("Board said: {0}", reply, ""));
}

Arduino 노브를 볼륨 조절기로 바꾸기​

각 줄에 담긴 0에서 1023까지의 가변 저항 값이 재생 볼륨이 됩니다.

// Arduino side: a potentiometer on A0, one reading per line.
// void setup() { Serial.begin(115200); }
// void loop() { Serial.println(analogRead(A0)); delay(50); }
// Empty text as the terminator means CR LF, which Serial.println sends.
SerialMonitorCreate("knob", "COM4", 115200, SerialParity.None, 8, SerialStopBits.One, "",
"line = ContextGetSerialTextLine(); if (StringIsNumber(line, true)) MultimediaSetVolume(AudioEndpoint.Playback, StringToNumber(line, true) / 1023.0);");