WinLIRC Client (original) (raw)
WinLIRC Client
This script receives notifications from WinLIRC whenever you press a button on your remote control. It can be used to automate Winamp, Windows Media Player, etc. It's easy to configure. For example, if WinLIRC recognizes a button named "VolUp" on your remote control, create a label named VolUp and beneath it use the command "SoundSet +5" to increase the soundcard's volume by 5%.
Download This Script | Other Sample Scripts | Home
; Here are the steps to use this script: ; 1) Configure WinLIRC to recognize your remote control and its buttons. ; WinLIRC is at http://winlirc.sourceforge.net ; 2) Edit the WinLIRC path, address, and port in the CONFIG section below. ; 3) Launch this script. It will start the WinLIRC server if needed. ; 4) Press some buttons on your remote control. A small window will ; appear showing the name of each button as you press it. ; 5) Configure your buttons to send keystrokes and mouse clicks to ; windows such as Winamp, Media Player, etc. See the examples below.
; This script requires AutoHotkey 1.0.38 or later. ; HISTORY OF CHANGES ; October 5, 2005: ; - Eliminated Winsock warning dialog "10054" upon system shutdown/logoff. ; - Added option "DelayBetweenButtonRepeats" to throttle the repeat speed.
; ------------------------------------------------- ; CONFIGURATION SECTION: Set your preferences here. ; ------------------------------------------------- ; Some remote controls repeat the signal rapidly while you're holding down ; a button. This makes it difficult to get the remote to send only a single ; signal. The following setting solves this by ignoring repeated signals ; until the specified time has passed. 200 is often a good setting. Set it ; to 0 to disable this feature. DelayBetweenButtonRepeats = 200
; Specify the path to WinLIRC, such as C:\WinLIRC\winlirc.exe WinLIRC_Path = %ProgramFiles%\WinLIRC\winlirc.exe
; Specify WinLIRC's address and port. The most common are 127.0.0.1 (localhost) and 8765. WinLIRC_Address = 127.0.0.1 WinLIRC_Port = 8765
; Do not change the following two lines. Skip them and continue below. Gosub WinLIRC_Init return
; -------------------------------------------- ; ASSIGN ACTIONS TO THE BUTTONS ON YOUR REMOTE ; -------------------------------------------- ; Configure your remote control's buttons below. Use WinLIRC's names ; for the buttons, which can be seen in your WinLIRC config file ; (.cf file) -- or you can press any button on your remote and the ; script will briefly display the button's name in a small window. ; ; Below are some examples. Feel free to revise or delete them to suit ; your preferences.
VolUp: SoundSet +5 ; Increase master volume by 5%. return
VolDown: SoundSet -5 ; Reduce master volume by 5%. return
ChUp: WinGetClass, ActiveClass, A if ActiveClass in Winamp v1.x,Winamp PE ; Winamp is active. Send {right} ; Send a right-arrow keystroke. else ; Some other type of window is active. Send {WheelUp} ; Rotate the mouse wheel up by one notch. return
ChDown: WinGetClass, ActiveClass, A if ActiveClass in Winamp v1.x,Winamp PE ; Winamp is active. Send {left} ; Send a left-arrow keystroke. else ; Some other type of window is active. Send {WheelDown} ; Rotate the mouse wheel down by one notch. return
Menu: IfWinExist, Untitled - Notepad { WinActivate } else { Run, Notepad WinWait, Untitled - Notepad WinActivate } Send Some keystrokes sent to Notepad.{Enter} return
; The examples above give a feel for how to accomplish common tasks. ; To learn the basics of AutoHotkey, check out the Quick-start Tutorial ; at http://www.autohotkey.com/docs/Tutorial.htm
; ---------------------------- ; END OF CONFIGURATION SECTION ; ---------------------------- ; Do not make changes below this point unless you want to change the core ; functionality of the script.
WinLIRC_Init: OnExit, ExitSub ; For connection cleanup purposes.
; Launch WinLIRC if it isn't already running: Process, Exist, winlirc.exe if not ErrorLevel ; No PID for WinLIRC was found. { IfNotExist, %WinLIRC_Path% { MsgBox The file "%WinLIRC_Path%" does not exist. Please edit this script to specify its location. ExitApp } Run %WinLIRC_Path% Sleep 200 ; Give WinLIRC a little time to initialize (probably never needed, just for peace of mind). }
; Connect to WinLIRC (or any type of server for that matter): socket := ConnectToAddress(WinLIRC_Address, WinLIRC_Port) if socket = -1 ; Connection failed (it already displayed the reason). ExitApp
; Find this script's main window: Process, Exist ; This sets ErrorLevel to this script's PID (it's done this way to support compiled scripts). DetectHiddenWindows On ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel) DetectHiddenWindows Off
; When the OS notifies the script that there is incoming data waiting to be received, ; the following causes a function to be launched to read the data: NotificationMsg = 0x5555 ; An arbitrary message number, but should be greater than 0x1000. OnMessage(NotificationMsg, "ReceiveData")
; Set up the connection to notify this script via message whenever new data has arrived. ; This avoids the need to poll the connection and thus cuts down on resource usage. FD_READ = 1 ; Received when data is available to be read. FD_CLOSE = 32 ; Received when connection has been closed. 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) ; This can connect to most types of TCP servers, not just WinLIRC. ; Returns -1 (INVALID_SOCKET) on failure or the socket ID upon success. { VarSetCapacity(wsaData, 32) ; The struct is only about 14 in size, so 32 is conservative. result := DllCall("Ws2_32\WSAStartup", UShort, 0x0002, UInt, &wsaData) ; Request Winsock 2.0 (0x0002) ; Since WSAStartup() will likely be the first Winsock function called by this script, ; check ErrorLevel to see if the OS has Winsock 2.0 available: if ErrorLevel { MsgBox WSAStartup() could not be called due to error %ErrorLevel%. Winsock 2.0 or higher is required. return -1 } if result ; Non-zero, which means it failed (most Winsock functions return 0 on success). { 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
}
; Prepare for connection:
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", str, IPAddress), SocketAddress, 4, 4) ; sin_addr.s_addr
; Attempt connection:
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 ; Indicate success by returning a valid socket ID rather than -1.
}
ReceiveData(wParam, lParam)
; By means of OnMessage(), this function has been set up to be called automatically whenever new data
; arrives on the connection. It reads the data from WinLIRC and takes appropriate action depending
; on the contents.
{
socket := wParam
ReceivedDataSize = 4096 ; Large in case a lot of data gets buffered due to delay in processing previous data.
Loop ; This loop solves the issue of the notification message being discarded due to thread-already-running.
{
VarSetCapacity(ReceivedData, ReceivedDataSize, 0) ; 0 for last param terminates string for use with recv().
ReceivedDataLength := DllCall("Ws2_32\recv", UInt, socket, Str, ReceivedData, Int, ReceivedDataSize, Int, 0)
if ReceivedDataLength = 0 ; The connection was gracefully closed, probably due to exiting WinLIRC.
ExitApp ; The OnExit routine will call WSACleanup() for us.
if ReceivedDataLength = -1
{
WinsockError := DllCall("Ws2_32\WSAGetLastError")
if WinsockError = 10035 ; WSAEWOULDBLOCK, which means "no more data to be read".
return 1
if WinsockError <> 10054 ; WSAECONNRESET, which happens when WinLIRC closes via system shutdown/logoff.
; Since it's an unexpected error, report it. Also exit to avoid infinite loop.
MsgBox % "recv() indicated Winsock error " . WinsockError
ExitApp ; The OnExit routine will call WSACleanup() for us.
}
; Otherwise, process the data received. Testing shows that it's possible to get more than one line
; at a time (even for explicitly-sent IR signals), which the following method handles properly.
; Data received from WinLIRC looks like the following example (see the WinLIRC docs for details):
; 0000000000eab154 00 NameOfButton NameOfRemote
Loop, parse, ReceivedData, n,
r
{
if A_LoopField in ,BEGIN,SIGHUP,END ; Ignore blank lines and WinLIRC's start-up messages.
continue
ButtonName = ; Init to blank in case there are less than 3 fields found below.
Loop, parse, A_LoopField, %A_Space% ; Extract the button name, which is the third field.
if A_Index = 3
ButtonName := A_LoopField
global DelayBetweenButtonRepeats ; Declare globals to make them available to this function.
static PrevButtonName, PrevButtonTime, RepeatCount ; These variables remember their values between calls.
if (ButtonName != PrevButtonName || A_TickCount - PrevButtonTime > DelayBetweenButtonRepeats)
{
if IsLabel(ButtonName) ; There is a subroutine associated with this button.
Gosub %ButtonName% ; Launch the subroutine.
else ; Since there is no associated subroutine, briefly display which button was pressed.
{
if (ButtonName == PrevButtonName)
RepeatCount += 1
else
RepeatCount = 1
SplashTextOn, 150, 20, Button from WinLIRC, %ButtonName% (%RepeatCount%)
SetTimer, SplashOff, 3000 ; This allows more signals to be processed while displaying the window.
}
PrevButtonName := ButtonName
PrevButtonTime := A_TickCount
}
}
}
return 1 ; Tell the program that no further processing of this message is needed.
}
SplashOff: SplashTextOff SetTimer, SplashOff, Off return
InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4) ; To preserve any existing contents in pDest, only pSize number of bytes starting at pOffset ; are altered in it. The caller must ensure that pDest has sufficient capacity. { mask := 0xFF ; This serves to isolate each byte, one by one. Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data. { DllCall("RtlFillMemory", UInt, &pDest + pOffset + A_Index - 1, UInt, 1 ; Write one byte. , UChar, (pInteger & mask) >> 8 * (A_Index - 1)) mask := mask << 8 ; Set it up for isolation of the next byte. } }
ExitSub: ; This subroutine is called automatically when the script exits for any reason. ; MSDN: "Any sockets open when WSACleanup is called are reset and automatically ; deallocated as if closesocket was called." DllCall("Ws2_32\WSACleanup") ExitApp