PageRenderTime 54ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/docs/scripts/WinLIRC.ahk

http://autohotkey-chinese.googlecode.com/
AutoHotKey | 280 lines | 163 code | 39 blank | 78 comment | 24 complexity | 0f2eb24858f9fc82a0aae6e82a73b1dd MD5 | raw file
  1. ; WinLIRC Client
  2. ; http://www.autohotkey.com
  3. ; This script receives notifications from WinLIRC whenever you press
  4. ; a button on your remote control. It can be used to automate Winamp,
  5. ; Windows Media Player, etc. It's easy to configure. For example, if
  6. ; WinLIRC recognizes a button named "VolUp" on your remote control,
  7. ; create a label named VolUp and beneath it use the command
  8. ; "SoundSet +5" to increase the soundcard's volume by 5%.
  9. ; Here are the steps to use this script:
  10. ; 1) Configure WinLIRC to recognize your remote control and its buttons.
  11. ; WinLIRC is at http://winlirc.sourceforge.net
  12. ; 2) Edit the WinLIRC path, address, and port in the CONFIG section below.
  13. ; 3) Launch this script. It will start the WinLIRC server if needed.
  14. ; 4) Press some buttons on your remote control. A small window will
  15. ; appear showing the name of each button as you press it.
  16. ; 5) Configure your buttons to send keystrokes and mouse clicks to
  17. ; windows such as Winamp, Media Player, etc. See the examples below.
  18. ; This script requires AutoHotkey 1.0.38.04 or later.
  19. ; HISTORY OF CHANGES
  20. ; March 2, 2007:
  21. ; - Improved reliability via "Critical" in ReceiveData().
  22. ; October 5, 2005:
  23. ; - Eliminated Winsock warning dialog "10054" upon system shutdown/logoff.
  24. ; - Added option "DelayBetweenButtonRepeats" to throttle the repeat speed.
  25. ; -------------------------------------------------
  26. ; CONFIGURATION SECTION: Set your preferences here.
  27. ; -------------------------------------------------
  28. ; Some remote controls repeat the signal rapidly while you're holding down
  29. ; a button. This makes it difficult to get the remote to send only a single
  30. ; signal. The following setting solves this by ignoring repeated signals
  31. ; until the specified time has passed. 200 is often a good setting. Set it
  32. ; to 0 to disable this feature.
  33. DelayBetweenButtonRepeats = 200
  34. ; Specify the path to WinLIRC, such as C:\WinLIRC\winlirc.exe
  35. WinLIRC_Path = %A_ProgramFiles%\WinLIRC\winlirc.exe
  36. ; Specify WinLIRC's address and port. The most common are 127.0.0.1 (localhost) and 8765.
  37. WinLIRC_Address = 127.0.0.1
  38. WinLIRC_Port = 8765
  39. ; Do not change the following two lines. Skip them and continue below.
  40. Gosub WinLIRC_Init
  41. return
  42. ; --------------------------------------------
  43. ; ASSIGN ACTIONS TO THE BUTTONS ON YOUR REMOTE
  44. ; --------------------------------------------
  45. ; Configure your remote control's buttons below. Use WinLIRC's names
  46. ; for the buttons, which can be seen in your WinLIRC config file
  47. ; (.cf file) -- or you can press any button on your remote and the
  48. ; script will briefly display the button's name in a small window.
  49. ;
  50. ; Below are some examples. Feel free to revise or delete them to suit
  51. ; your preferences.
  52. VolUp:
  53. SoundSet +5 ; Increase master volume by 5%. On Vista, replace this line with: Send {Volume_Up}
  54. return
  55. VolDown:
  56. SoundSet -5 ; Reduce master volume by 5%. On Vista, replace this line with: Send {Volume_Down}
  57. return
  58. ChUp:
  59. WinGetClass, ActiveClass, A
  60. if ActiveClass in Winamp v1.x,Winamp PE ; Winamp is active.
  61. Send {right} ; Send a right-arrow keystroke.
  62. else ; Some other type of window is active.
  63. Send {WheelUp} ; Rotate the mouse wheel up by one notch.
  64. return
  65. ChDown:
  66. WinGetClass, ActiveClass, A
  67. if ActiveClass in Winamp v1.x,Winamp PE ; Winamp is active.
  68. Send {left} ; Send a left-arrow keystroke.
  69. else ; Some other type of window is active.
  70. Send {WheelDown} ; Rotate the mouse wheel down by one notch.
  71. return
  72. Menu:
  73. IfWinExist, Untitled - Notepad
  74. {
  75. WinActivate
  76. }
  77. else
  78. {
  79. Run, Notepad
  80. WinWait, Untitled - Notepad
  81. WinActivate
  82. }
  83. Send Here are some keystrokes sent to Notepad.{Enter}
  84. return
  85. ; The examples above give a feel for how to accomplish common tasks.
  86. ; To learn the basics of AutoHotkey, check out the Quick-start Tutorial
  87. ; at http://www.autohotkey.com/docs/Tutorial.htm
  88. ; ----------------------------
  89. ; END OF CONFIGURATION SECTION
  90. ; ----------------------------
  91. ; Do not make changes below this point unless you want to change the core
  92. ; functionality of the script.
  93. WinLIRC_Init:
  94. OnExit, ExitSub ; For connection cleanup purposes.
  95. ; Launch WinLIRC if it isn't already running:
  96. Process, Exist, winlirc.exe
  97. if not ErrorLevel ; No PID for WinLIRC was found.
  98. {
  99. IfNotExist, %WinLIRC_Path%
  100. {
  101. MsgBox The file "%WinLIRC_Path%" does not exist. Please edit this script to specify its location.
  102. ExitApp
  103. }
  104. Run %WinLIRC_Path%
  105. Sleep 200 ; Give WinLIRC a little time to initialize (probably never needed, just for peace of mind).
  106. }
  107. ; Connect to WinLIRC (or any type of server for that matter):
  108. socket := ConnectToAddress(WinLIRC_Address, WinLIRC_Port)
  109. if socket = -1 ; Connection failed (it already displayed the reason).
  110. ExitApp
  111. ; Find this script's main window:
  112. Process, Exist ; This sets ErrorLevel to this script's PID (it's done this way to support compiled scripts).
  113. DetectHiddenWindows On
  114. ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel)
  115. DetectHiddenWindows Off
  116. ; When the OS notifies the script that there is incoming data waiting to be received,
  117. ; the following causes a function to be launched to read the data:
  118. NotificationMsg = 0x5555 ; An arbitrary message number, but should be greater than 0x1000.
  119. OnMessage(NotificationMsg, "ReceiveData")
  120. ; Set up the connection to notify this script via message whenever new data has arrived.
  121. ; This avoids the need to poll the connection and thus cuts down on resource usage.
  122. FD_READ = 1 ; Received when data is available to be read.
  123. FD_CLOSE = 32 ; Received when connection has been closed.
  124. if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_READ|FD_CLOSE)
  125. {
  126. MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
  127. ExitApp
  128. }
  129. return
  130. ConnectToAddress(IPAddress, Port)
  131. ; This can connect to most types of TCP servers, not just WinLIRC.
  132. ; Returns -1 (INVALID_SOCKET) upon failure or the socket ID upon success.
  133. {
  134. VarSetCapacity(wsaData, 32) ; The struct is only about 14 in size, so 32 is conservative.
  135. result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "UInt", &wsaData) ; Request Winsock 2.0 (0x0002)
  136. ; Since WSAStartup() will likely be the first Winsock function called by this script,
  137. ; check ErrorLevel to see if the OS has Winsock 2.0 available:
  138. if ErrorLevel
  139. {
  140. MsgBox WSAStartup() could not be called due to error %ErrorLevel%. Winsock 2.0 or higher is required.
  141. return -1
  142. }
  143. if result ; Non-zero, which means it failed (most Winsock functions return 0 upon success).
  144. {
  145. MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
  146. return -1
  147. }
  148. AF_INET = 2
  149. SOCK_STREAM = 1
  150. IPPROTO_TCP = 6
  151. socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP)
  152. if socket = -1
  153. {
  154. MsgBox % "socket() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
  155. return -1
  156. }
  157. ; Prepare for connection:
  158. SizeOfSocketAddress = 16
  159. VarSetCapacity(SocketAddress, SizeOfSocketAddress)
  160. InsertInteger(2, SocketAddress, 0, AF_INET) ; sin_family
  161. InsertInteger(DllCall("Ws2_32\htons", "UShort", Port), SocketAddress, 2, 2) ; sin_port
  162. InsertInteger(DllCall("Ws2_32\inet_addr", "Str", IPAddress), SocketAddress, 4, 4) ; sin_addr.s_addr
  163. ; Attempt connection:
  164. if DllCall("Ws2_32\connect", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
  165. {
  166. MsgBox % "connect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . ". Is WinLIRC running?"
  167. return -1
  168. }
  169. return socket ; Indicate success by returning a valid socket ID rather than -1.
  170. }
  171. ReceiveData(wParam, lParam)
  172. ; By means of OnMessage(), this function has been set up to be called automatically whenever new data
  173. ; arrives on the connection. It reads the data from WinLIRC and takes appropriate action depending
  174. ; on the contents.
  175. {
  176. Critical ; Prevents another of the same message from being discarded due to thread-already-running.
  177. socket := wParam
  178. ReceivedDataSize = 4096 ; Large in case a lot of data gets buffered due to delay in processing previous data.
  179. VarSetCapacity(ReceivedData, ReceivedDataSize, 0) ; 0 for last param terminates string for use with recv().
  180. ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Str", ReceivedData, "Int", ReceivedDataSize, "Int", 0)
  181. if ReceivedDataLength = 0 ; The connection was gracefully closed, probably due to exiting WinLIRC.
  182. ExitApp ; The OnExit routine will call WSACleanup() for us.
  183. if ReceivedDataLength = -1
  184. {
  185. WinsockError := DllCall("Ws2_32\WSAGetLastError")
  186. if WinsockError = 10035 ; WSAEWOULDBLOCK, which means "no more data to be read".
  187. return 1
  188. if WinsockError <> 10054 ; WSAECONNRESET, which happens when WinLIRC closes via system shutdown/logoff.
  189. ; Since it's an unexpected error, report it. Also exit to avoid infinite loop.
  190. MsgBox % "recv() indicated Winsock error " . WinsockError
  191. ExitApp ; The OnExit routine will call WSACleanup() for us.
  192. }
  193. ; Otherwise, process the data received. Testing shows that it's possible to get more than one line
  194. ; at a time (even for explicitly-sent IR signals), which the following method handles properly.
  195. ; Data received from WinLIRC looks like the following example (see the WinLIRC docs for details):
  196. ; 0000000000eab154 00 NameOfButton NameOfRemote
  197. Loop, parse, ReceivedData, `n, `r
  198. {
  199. if A_LoopField in ,BEGIN,SIGHUP,END ; Ignore blank lines and WinLIRC's start-up messages.
  200. continue
  201. ButtonName = ; Init to blank in case there are less than 3 fields found below.
  202. Loop, parse, A_LoopField, %A_Space% ; Extract the button name, which is the third field.
  203. if A_Index = 3
  204. ButtonName := A_LoopField
  205. global DelayBetweenButtonRepeats ; Declare globals to make them available to this function.
  206. static PrevButtonName, PrevButtonTime, RepeatCount ; These variables remember their values between calls.
  207. if (ButtonName != PrevButtonName || A_TickCount - PrevButtonTime > DelayBetweenButtonRepeats)
  208. {
  209. if IsLabel(ButtonName) ; There is a subroutine associated with this button.
  210. Gosub %ButtonName% ; Launch the subroutine.
  211. else ; Since there is no associated subroutine, briefly display which button was pressed.
  212. {
  213. if (ButtonName == PrevButtonName)
  214. RepeatCount += 1
  215. else
  216. RepeatCount = 1
  217. SplashTextOn, 150, 20, Button from WinLIRC, %ButtonName% (%RepeatCount%)
  218. SetTimer, SplashOff, 3000 ; This allows more signals to be processed while displaying the window.
  219. }
  220. PrevButtonName := ButtonName
  221. PrevButtonTime := A_TickCount
  222. }
  223. }
  224. return 1 ; Tell the program that no further processing of this message is needed.
  225. }
  226. SplashOff:
  227. SplashTextOff
  228. SetTimer, SplashOff, Off
  229. return
  230. InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
  231. ; The caller must ensure that pDest has sufficient capacity. To preserve any existing contents in pDest,
  232. ; only pSize number of bytes starting at pOffset are altered in it.
  233. {
  234. Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
  235. DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
  236. }
  237. ExitSub: ; This subroutine is called automatically when the script exits for any reason.
  238. ; MSDN: "Any sockets open when WSACleanup is called are reset and automatically
  239. ; deallocated as if closesocket was called."
  240. DllCall("Ws2_32\WSACleanup")
  241. ExitApp