WinLIRC 클라이언트

이 스크립트는 리모트 콘트롤에 버튼을 누를 때마다 WinLIRC 로부터 고지를 받습니다. Winamp, Windows Media Player, 등등을 자동화 하는 데 사용할 수 있습니다. 쉽게 환경을 구성할 수 있습니다. 예를 들어 WinLIRC가 리모트 콘트롤에서 이름이 "VolUp"인 버튼을 인지하면, 이름이 VolUp인 라벨을 생성하고 그 아래에 명령어 SoundSet +5를 사용하여 사운드카드의 볼륨을 5%만큼 높입니다.

이 스크립트 내려받기  |  다른 샘플 스크립트  |  홈으로

; 다음은 이 스크립트를 사용하는 방법입니다:
; 1) 리모트 콘트롤과 버튼을 인식하도록 WinLIRC를 구성합니다.
;    WinLIRC는 다음에 있습니다: http://winlirc.sourceforge.net
; 2) 아래의 CONFIG 섹션에서 WinLIRC 경로, 주소, 그리고 포트 번호를 편집합니다.
; 3) 이 스크립트를 기동시킵니다. 필요하면 WinLIRC 서버가 자동으로 기동됩니다.
; 4) 리모트 콘트롤에서 버튼을 눌러 봅니다. 버튼을 누를 때마다 
;    작은 창이 나타나 그 버튼의 이름을 보여줍니다.
; 5) 버튼을 구성해 Winamp, Media Player, 등등과 같은 창에
;     키눌림과 마우스 클릭을 전송합니다. 아래 예제를 참조하십시오.

; 이 스크립트는 AutoHotkey 1.0.38.04 이상을 요구합니다.
; 변경 이력
; March 2, 2007:
; - ReceiveData()에 "Critical"를 통하여 신뢰성 개선.
; October 5, 2005:
; - 시스템이 셧다운/로그오프할 때 Winsock 경고 대화상자 "10054"가 나타나는 것을 제거.
; - 반복 속도를 감소시키기 위해 옵션 "DelayBetweenButtonRepeats"를 추가.

; -------------------------------------------------------------
; CONFIGURATION SECTION: 여기에 원하는 대로 설정합니다.
; -------------------------------------------------------------
; 어떤 리모콘은 버튼을 누르고 있는 동안 신호를 빠르게 반복해 보냅니다.
; 이 때문에 리모트 콘트롤이 오직 하나의 신호만 전송하도록 만들기가 어렵습니다
; 다음 설정은  지정된 시간이 경과할 때까지 반복된 신호를 무시함으로써
; 이 문제를 해결합니다. 200이면 보통 좋은 설정입니다.
; 0으로 설정하면 이 특징을 불능으로 만듭니다.
DelayBetweenButtonRepeats = 200

; 경로를 WinLIRC에 지정합니다. 예를 들어 C:\WinLIRC\winlirc.exe
WinLIRC_Path = %A_ProgramFiles%\WinLIRC\winlirc.exe

; WinLIRC의 주소와 포트를 지정합니다. 보통 127.0.0.1 (localhost)과 8765입니다.
WinLIRC_Address = 127.0.0.1
WinLIRC_Port = 8765

; 다음 두 줄은 변경하지 마십시오. 건너뛰고 아래에서 계속하십시오.
Gosub WinLIRC_Init
return

; --------------------------------------------
; ASSIGN ACTIONS TO THE BUTTONS ON YOUR REMOTE
; --------------------------------------------
; 아래에서 리모트 콘트롤 버튼을 구성하십시오. 버튼에 WinLIRC의 이름을 사용합니다.
; 이름은 WinLIRC 환경구성 파일(.cf)에서 보실 수 있습니다.
;  -- 아니면 리모트 콘트롤에 버튼을 마우것나 누르면
; 스크립트가 작은 창에 그 버튼의 이름을 잠시 보여줍니다.
; 
; 아래는 몇 가지 예제입니다.
;  자유롭게 여러분의 기호에 맞게 개조하거나 지원도 됩니다.

VolUp:
SoundSet +5  ; 마스터 볼륨을 5%만큼 늘립니다. Vista에서는 이 줄을 다음으로 교체하십시오: Send {Volume_Up}
return

VolDown:
SoundSet -5  ; 마스터 볼륨을 5%만큼 줄입니다. Vista에서는 이 줄을 다음으로 교체하십시오: Send {Volume_Down}
return

ChUp:
WinGetClass, ActiveClass, A
if ActiveClass in Winamp v1.x,Winamp PE  ; Winamp가 활성화 되어 있습니다.
    Send {right}  ; 오른쪽-화살표 키눌림을 전송합니다.
else  ; 다른 유형의 창이 활성화 되어 있습니다.
    Send {WheelUp}  ; 마우스 휠을 한 눈금 위로 돌립니다.
return

ChDown:
WinGetClass, ActiveClass, A
if ActiveClass in Winamp v1.x,Winamp PE  ; Winamp가 활성화 되어 있습니다.
    Send {left}  ; 왼쪽-화살표 키눌림을 전송합니다.
else  ; 다른 유형의 창이 활성화 되어 있습니다.
    Send {WheelDown}  ; 마우스 휠을 한 눈금 아래로 돌립니다.
return

Menu:
IfWinExist, Untitled - Notepad
{
    WinActivate
}
else
{
    Run, Notepad
    WinWait, Untitled - Notepad
    WinActivate
}
Send Here are some keystrokes sent to Notepad.{Enter}
return

; 위의 예제에서 어떻게 일반적인 과업을 달성하는지 감을 잡을 수 있습니다.
; 오토핫키의 기본 지식을 습득하려면 빨지 시작하기 자습서를 찾아 보시기 바랍니다:
; http://www.autohotkey.com/docs/Tutorial.htm

; ----------------------------
; END OF CONFIGURATION SECTION
; ----------------------------
; 이 시점 이후로 변경하지 마십시오.
; 단, 스크립트의 핵심 기능을 바꾸고 싶다면 예외입니다.

WinLIRC_Init:
OnExit, ExitSub  ; 접속 청소 목적을 위해.

; 아직 실행되지 않았다면 WinLIRC를 기동합니다:
Process, Exist, winlirc.exe
if not ErrorLevel  ; WinLIRC에 대한 PID가 발견되지 않았습니다.
{
    IfNotExist, %WinLIRC_Path%
    {
        MsgBox 파일 "%WinLIRC_Path%"가 존재하지 않습니다. 이 스크립트를 편집해 그 위치를 지정하십시오.
        ExitApp
    }
    Run %WinLIRC_Path%
    Sleep 200  ; WinLIRC에 초기화 할 시간을 줍니다 (아마도 거의 필요하지 않을 겁니다. 신경쓰지 마십시오).
}

; WinLIRC에 접속합니다 (또는 그를 대신하면 어떤 종류의 서버이든 접속합니다):
socket := ConnectToAddress(WinLIRC_Address, WinLIRC_Port)
if socket = -1  ; 접속 실패 (이미 이유는 보여주었습니다).
    ExitApp

; 이 스크립트의 메인 창을 찾습니다:
Process, Exist  ; 이렇게 하면 ErrorLevel에 이 스크립트의 PID가 설정됩니다 (이런식으로 하면 컴파일된 스크립트를 지원할 수 있습니다).
DetectHiddenWindows On
ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel)
DetectHiddenWindows Off

; OS가 스크립트에게 들어오는 데이터가 수신되기를 기다리고 있다고 고지할 때,
; 다음은 그 데이터를 읽기 위하여 함수를 기동시킵니다:
NotificationMsg = 0x5555  ; 임의의 메시지 번호, 그러나 0x1000 보다는 커야 합니다.
OnMessage(NotificationMsg, "ReceiveData")

; 접속을 설정해 이 스크립트에게 새로 데이터가 도착할 때마다 메시지를 통하여 고지합니다.
; 이렇게 하면 접속을 폴할 필요가 없으므로, 자원 낭비가 감소합니다.
FD_READ = 1     ; 데이터를 읽을 수 있을 때 수신됩니다.
FD_CLOSE = 32   ; 접속이 닫힐 때 수신됩니다.
if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_READ|FD_CLOSE)
{
    MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
    ExitApp
}
return



ConnectToAddress(IPAddress, Port)
; 대부분의 유형의 TCP 서버에 접속할 수 있습니다. WinLIRC 만으로 제한되지 않습니다.
; 실패하면 -1 (INVALID_SOCKET)을 돌려주고 성공하면 소켓 ID를 돌려줍니다.
{
    VarSetCapacity(wsaData, 400)
    result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "UInt", &wsaData) ; Winsock 2.0 (0x0002)을 요구합니다
    ; WSAStartup()이 이스크립트가 첫 번째로 호출한 Winsock 함수일 가능성이 높으므로,
    ; ErrorLevel을 점검해 OS에 Winsock 2.0이 있는지 알아 봅니다:
    if ErrorLevel
    {
        MsgBox WSAStartup() could not be called due to error %ErrorLevel%. Winsock 2.0 or higher is required.
        return -1
    }
    if result  ; 0이 아니라면, 실패했다는 뜻입니다 (대부분의 Winsock 함수는 성공하면 0을 돌려줍니다).
    {
        MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
        return -1
    }

    AF_INET = 2
    SOCK_STREAM = 1
    IPPROTO_TCP = 6
    socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP)
    if socket = -1
    {
        MsgBox % "socket() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
        return -1
    }

    ; 접속을 준비합니다:
    SizeOfSocketAddress = 16
    VarSetCapacity(SocketAddress, SizeOfSocketAddress)
    InsertInteger(2, SocketAddress, 0, AF_INET)   ; sin_family
    InsertInteger(DllCall("Ws2_32\htons", "UShort", Port), SocketAddress, 2, 2)   ; sin_port
    InsertInteger(DllCall("Ws2_32\inet_addr", "AStr", IPAddress), SocketAddress, 4, 4)   ; sin_addr.s_addr

    ; 접속을 시도합니다:
    if DllCall("Ws2_32\connect", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
    {
        MsgBox % "connect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . ". Is WinLIRC running?"
        return -1
    }
    return socket  ; -1 말고 유효한 소켓 ID를 돌려주어 성공을 알립니다.
}



ReceiveData(wParam, lParam)
; OnMessage()를 수단으로, 이 함수는 새 데이터가 접속에 도착할 때마다
; 자동으로 호출되도록 설정되어 있습니다. 데이터를 WinLIRC로부터 읽어서
; 그 내용에 따라 적절하게 조치를 취합니다.
{
    Critical  ; 이미 쓰레드가 실행되어 있기 때문에 또다른 같은 메시지가 폐기되지 않도록 막습니다.
    socket := wParam
    ReceivedDataSize = 4096  ; 이전 데이터를 처리하는데 지연 시간이 있어서 많은 데이터가 버퍼에 찰 경우를 대비해 크게 잡습니다.

    VarSetCapacity(ReceivedData, ReceivedDataSize, 0)  ; recv()에 사용하기 위해 0을 마지막 매개변수에 지정해 문자열을 종료합니다.
    ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Str", ReceivedData, "Int", ReceivedDataSize, "Int", 0)
    if ReceivedDataLength = 0  ; 접속이 우아하게 닫혔습니다. 아마도 WinLIRC를 끝냈기 때문일 것입니다.
        ExitApp  ; OnExit 루틴이 우리를 대신하여 WSACleanup()을 호출해 줍니다.
    if ReceivedDataLength = -1
    {
        WinsockError := DllCall("Ws2_32\WSAGetLastError")
        if WinsockError = 10035  ; WSAEWOULDBLOCK, "더 이상 읽을 데이터가 없다"는 뜻입니다.
            return 1
        if WinsockError <> 10054 ; WSAECONNRESET, WinLIRC가 시스템 셧다운/로그오프를 통하여 닫힐 때 일어납니다.
            ; 예측 하지 못한 에러이므로 보고합니다.  또 무한 회돌이를 피하기 위해 종료합니다.
            MsgBox % "recv() indicated Winsock error " . WinsockError
        ExitApp  ; OnExit 루틴이 우리를 대신에 WSACleanup()를 호출합니다.
    }
    ; 그렇지 않으면, 수신된 데이터를 처리합니다. 테스트에 의하면 한 번에 한 줄 이상을 얻을 수 있습니다
    ; (명시적으로-전송된 IR 신호에 대해서도 마찬가지입니다), 다음 메쏘드가 적절하게 처리합니다.
    ; WinLIRC로부터 얻은 데이터는 다음 예제와 비슷합니다 (자세한 것은 WinLIRC 문서를 참조하십시오):
    ; 0000000000eab154 00 NameOfButton NameOfRemote
    Loop, parse, ReceivedData, `n, `r
    {
        if A_LoopField in ,BEGIN,SIGHUP,END  ; 빈 줄과 WinLIRC의 시작 메시지를 무시합니다.
            continue
        ButtonName =  ; 아래에서 3 개 보다 적게 필드가 발견될 경우를 대비해 비워서 초기화합니다.
        Loop, parse, A_LoopField, %A_Space%  ; 버튼 이름을 추출합니다. 세 번째 필드입니다.
            if A_Index = 3
                ButtonName := A_LoopField
        global DelayBetweenButtonRepeats  ; 전역 변수를 선언해 이 함수에 적용합니다.
        static PrevButtonName, PrevButtonTime, RepeatCount  ; 이 변수들은 호출 사이에 값을 기억합니다.
        if (ButtonName != PrevButtonName || A_TickCount - PrevButtonTime > DelayBetweenButtonRepeats)
        {
            if IsLabel(ButtonName)  ; 이 버튼에 연관된 서브루틴이 있습니다.
                Gosub %ButtonName%  ; 그 서브루틴을 기동시킵니다.
            else ; 연관된 서브루틴이 없으므로, 간략하게 어느 버튼이 눌렸는지 보여줍니다.
            {
                if (ButtonName == PrevButtonName)
                    RepeatCount += 1
                else
                    RepeatCount = 1
                SplashTextOn, 150, 20, Button from WinLIRC, %ButtonName% (%RepeatCount%)
                SetTimer, SplashOff, 3000  ; 이렇게 하면 창이 보이는 동안에 더 많은 신호를 처리할 수 있습니다.
            }
            PrevButtonName := ButtonName
            PrevButtonTime := A_TickCount
        }
    }
    return 1  ; 프로그램에게 더 이상 이 메시지를 처리할 필요가 없다고 알립니다.
}



SplashOff:
SplashTextOff
SetTimer, SplashOff, Off
return



InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; 호출자는 pDest의 가용 능력이 충분한지 확인해야 합니다. 기존의 내용을 pDest에 보존하기 위해,
; pOffset에서 시작해서 오직 pSize 개수의 바이트만 변경됩니다.
{
    Loop %pSize%  ; 각 바이트를 구조체 안으로 날 이진 데이터로 복사합니다.
        DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}



ExitSub:  ; 이 서브루틴은 스크립트가 어떤 이유로 종료될 때 자동으로 호출됩니다.
; MSDN에 의하면: "WSACleanup이 호출될 때 열린 소켓은 모두 리셋되고 
; 마치 closesocket 이 호출될 것처럼 자동으로 할당이 해제됩니다."
DllCall("Ws2_32\WSACleanup")
ExitApp
선택 | 내려받기