; WinLIRC Client ; https://www.autohotkey.com ; 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 function ; to increase the soundcard's volume by 5%. ; Here are the steps to use this script: ; 1) Configure WinLIRC to recognize your remote control and its buttons. ; WinLIRC is at https://sourceforge.net/projects/winlirc/ ; 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. ; ------------------------------------------------- ; 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. g_DelayBetweenButtonRepeats := 200 ; Specify the path to WinLIRC, such as C:\WinLIRC\winlirc.exe WinLIRC_Path := A_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 line. Skip it and continue below. WinLIRC_Init(WinLIRC_Path, WinLIRC_Address, WinLIRC_Port) ; -------------------------------------------- ; 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. class Actions { VolUp() { SoundSetVolume "+5" ; Increase master volume by 5%. } VolDown() { SoundSetVolume "-5" ; Reduce master volume by 5%. } ChUp() { if WinGetClass("A") ~= "^(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. } ChDown() { if WinGetClass("A") ~= "^(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. } Menu() { if WinExist("Untitled - Notepad") { WinActivate } else { Run "Notepad" WinWait "Untitled - Notepad" WinActivate } Send "Here are some keystrokes sent to Notepad.{Enter}" } } ; 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 https://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(Path, IPAddress, Port) { OnExit ExitSub ; For connection cleanup purposes. ; Launch WinLIRC if it isn't already running: if not ProcessExist("winlirc.exe") ; No PID for WinLIRC was found. { if !FileExist(Path) { MsgBox "The file '" Path "' does not exist. Please edit this script to specify its location." ExitApp } Run 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(IPAddress, Port) if socket = -1 ; Connection failed (it already displayed the reason). ExitApp ; 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) Persistent ; 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", A_ScriptHwnd, "UInt", NotificationMsg, "Int", FD_READ|FD_CLOSE) { MsgBox "WSAAsyncSelect() indicated Winsock error " DllCall("Ws2_32\WSAGetLastError") ExitApp } } ConnectToAddress(IPAddress, Port) ; This can connect to most types of TCP servers, not just WinLIRC. ; Returns -1 (INVALID_SOCKET) upon failure or the socket ID upon success. { wsaData := Buffer(400) result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "Ptr", wsaData) ; Request Winsock 2.0 (0x0002) if result ; Non-zero, which means it failed (most Winsock functions return 0 upon 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 SocketAddress := Buffer(SizeOfSocketAddress, 0) NumPut( "UShort", 2 ; sin_family , "UShort", DllCall("Ws2_32\htons", "UShort", Port) ; sin_port , "UInt", DllCall("Ws2_32\inet_addr", "AStr", IPAddress) ; sin_addr.s_addr , SocketAddress) ; Attempt connection: if DllCall("Ws2_32\connect", "UInt", socket, "Ptr", 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. { Critical ; Prevents another of the same message from being discarded due to thread-already-running. socket := wParam ReceivedDataSize := 4096 ; Large in case a lot of data gets buffered due to delay in processing previous data. ReceivedData := Buffer(ReceivedDataSize, 0) ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Ptr", 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, StrGet(ReceivedData, "CP0"), "`n", "`r" { if A_LoopField ~= "^(|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, "`s" ; Extract the button name, which is the third field. if A_Index = 3 ButtonName := A_LoopField static PrevButtonName := "", PrevButtonTime := 0, RepeatCount := 0 ; These variables remember their values between calls. if (ButtonName != PrevButtonName || A_TickCount - PrevButtonTime > g_DelayBetweenButtonRepeats) { if HasMethod(Actions.Prototype, ButtonName) ; There is a method associated with this button. Actions.Prototype.%ButtonName%() ; Call the method. else ; Since there is no associated function, briefly display which button was pressed. { if (ButtonName == PrevButtonName) RepeatCount += 1 else RepeatCount := 1 ToolTip "Button from WinLIRC, " ButtonName " (" RepeatCount ")" SetTimer () => ToolTip(), -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. } ExitSub(*) ; This function 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") }