#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=: # DRG - Custom Input # Version 2.10 # Author : LiTTleDRAgo #:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=: # # How to use : # Keyboard.press?(KEY) # Keyboard.trigger?(KEY) # Keyboard.repeat?(KEY) # Keyboard.release?(KEY) # Mouse.press?(MKEY) # Mouse.trigger?(MKEY) # Mouse.repeat?(MKEY) # Mouse.release?(MKEY) # # KEY = Keyboard key (as a string or symbol), look at ASCII table below # MKEY = 'Mouse Left', 'Mouse Right', 'Mouse Middle' # # Mouse.x # returns x position of the mouse # Mouse.y # returns y position of the mouse # Mouse.dragging? # returns true if mouse is dragging # Mouse.drag_rect # returns as a rect if mouse is dragging # Mouse.in_area?(RECT) # returns true if mouse position inside the area # Mouse.scroll_up? # returns true if mouse wheel is scrolled up # Mouse.scroll_down? # returns true if mouse wheel is scrolled down # # New in v 2.10 # Added MouseWheel.dll for smoother wheel scroll (Credit to KK20 for his dll) # The DLL can be downloaded at forum chaos-project. # (http://forum.chaos-project.com/index.php/topic,15450.0.html) # # Example : # Keyboard.press?(:Enter) # Keyboard.press?('Enter') # #:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=: ($imported ||= {})[:drg_custom_input] = 2.10 core_engine = "This script needs Drago - Core Engine ver 1.51 or above" ($imported[:drg_core_engine] || 0) >= 1.51 || raise(core_engine) #============================================================================== # ** Keyboard #------------------------------------------------------------------------------ # #============================================================================== module Keyboard #---------------------------------------------------------------------------- # * Public Instance Variable #---------------------------------------------------------------------------- class << self attr_sec_reader :getkeysta, 'LiTTleDRAgo.getkeysta' attr_sec_reader :getkeylay, 'LiTTleDRAgo.getkeylay' attr_sec_reader :mapvirkey, 'LiTTleDRAgo.mapvirkey' attr_sec_reader :tounicode, 'LiTTleDRAgo.tounicode' end #---------------------------------------------------------------------------- # * Simple ASCII table #---------------------------------------------------------------------------- Key = {'A' => 65, 'B' => 66, 'C' => 67, 'D' => 68, 'E' => 69, 'F' => 70, 'G' => 71, 'H' => 72, 'I' => 73, 'J' => 74, 'K' => 75, 'L' => 76, 'M' => 77, 'N' => 78, 'O' => 79, 'P' => 80, 'Q' => 81, 'R' => 82, 'S' => 83, 'T' => 84, 'U' => 85, 'V' => 86, 'W' => 87, 'X' => 88, 'Y' => 89, 'Z' => 90, '0' => 48, '1' => 49, '2' => 50, '3' => 51, '4' => 52, '5' => 53, '6' => 54, '7' => 55, '8' => 56, '9' => 57, 'NumberPad 0' => 45, 'NumberPad 1' => 35, 'NumberPad 2' => 40, 'NumberPad 3' => 34, 'NumberPad 4' => 37, 'NumberPad 5' => 12, 'NumberPad 6' => 39, 'NumberPad 7' => 36, 'NumberPad 8' => 38, 'NumberPad 9' => 33, 'F1' => 112, 'F2' => 113, 'F3' => 114, 'F4' => 115, 'F5' => 116, 'F6' => 117, 'F7' => 118, 'F8' => 119, 'F9' => 120, 'F10' => 121, 'F11' => 122, 'F12' => 123, ';' => 186, '=' => 187, ',' => 188, '-' => 189, '.' => 190, '/' => 220, '\\' => 191, '\'' => 222, '[' => 219, ']' => 221, '`' => 192, 'Backspace' => 8, 'Tab' => 9, 'Enter' => 13, 'Shift' => 16, 'Left Shift' => 160, 'Right Shift' => 161, 'Left Ctrl' => 162, 'Right Ctrl' => 163, 'Left Alt' => 164, 'Right Alt' => 165, 'Ctrl' => 17, 'Alt' => 18, 'Esc' => 27, 'Space' => 32, 'Page Up' => 33, 'Page Down' => 34, 'End' => 35, 'Home' => 36, 'Insert' => 45, 'Delete' => 46, 'Arrow Left' => 37, 'Arrow Up' => 38, 'Left' => 37, 'Up' => 38, 'Arrow Right' => 39, 'Arrow Down' => 40, 'Right' => 39, 'Down' => 40, 'Mouse Left' => 1, 'Mouse Right' => 2, 'Mouse Middle' => 4, 'Mouse 4' => 5, 'Mouse 5' => 6} #---------------------------------------------------------------------------- # * Constant #---------------------------------------------------------------------------- KEY_COUNT = 256 ALL_KEYS = (0...KEY_COUNT).to_a DOWN_STATE_MASK = 0x80 DEAD_KEY_MASK = 0x80000000 #---------------------------------------------------------------------------- # * Data #---------------------------------------------------------------------------- @state = "\0" * KEY_COUNT #---------------------------------------------------------------------------- # * update # Updates input. #---------------------------------------------------------------------------- def self.update @language_layout = getkeylay.call(0) getkeysta.call(@state) key = 0 @state.each_byte do |byte| if (byte & DOWN_STATE_MASK) == DOWN_STATE_MASK (@released||=[])[key] = false unless (@pressed||=[])[key] @pressed[key] = (@triggered||=[])[key] = true @repeatedKey = key @repeatedCount = 0 else (@triggered||=[])[key] = false end if key == @repeatedKey (@repeatedCount||=0) < 17 ? @repeatedCount += 1 : @repeatedCount = 15 end elsif !(@released||=[])[key] if (@pressed||=[])[key] (@triggered||=[])[key] = @pressed[key] = false @released[key] = true key == @repeatedKey && (@repeatedKey = -1) && (@repeatedCount = 0) end else @released[key] = false end key += 1 end end #---------------------------------------------------------------------------- # * dir4 # 4 direction check. #---------------------------------------------------------------------------- def self.dir4 return 2 if self.press?(:Down) return 4 if self.press?(:Left) return 6 if self.press?(:Right) return 8 if self.press?(:Up) return 0 end #---------------------------------------------------------------------------- # * dir8 # 8 direction check. #---------------------------------------------------------------------------- def self.dir8 left = self.press?(:Left) return 1 if (down = self.press?(:Down)) && left return 3 if (right = self.press?(:Right)) && down return 7 if (up = self.press?(:Up)) && left return 9 if up && right return 2 if down return 4 if left return 6 if right return 8 if up return 0 end #---------------------------------------------------------------------------- # * trigger? # Test if key was triggered once. #---------------------------------------------------------------------------- def self.trigger?(*keys) keys_collect(keys).any? {|key| (@triggered ||= [])[key]} end #---------------------------------------------------------------------------- # * press? # Test if key is being pressed. #---------------------------------------------------------------------------- def self.press?(*keys) keys_collect(keys).any? {|key| (@pressed ||= [])[key]} end #---------------------------------------------------------------------------- # * pressed_any? # Test if any key is being pressed #---------------------------------------------------------------------------- def self.pressed_any? (@pressed ||= []).any? {|key| key == true} end #---------------------------------------------------------------------------- # * keys_collect # Collecting inputted keys into arrays #---------------------------------------------------------------------------- def self.keys_collect(keys) keys.collect! do |key| key.is_a?(String) || key.is_a?(Symbol) ? Key["#{key}"] : key end end #---------------------------------------------------------------------------- # * repeat? # Test if key is being pressed for repeating. #---------------------------------------------------------------------------- def self.repeat?(*keys) ((@repeatedKey ||= -1) >= 0 && keys_collect(keys).include?(@repeatedKey) && (@repeatedCount == 1 || @repeatedCount == 16)) end #---------------------------------------------------------------------------- # * release? # Test if key was released. #---------------------------------------------------------------------------- def self.release?(*keys) keys_collect(keys).any? {|key| (@released ||= [])[key]} end #---------------------------------------------------------------------------- # * get_character # vk - virtual key # Gets the character from keyboard input using the input locale identifier # (formerly called keyboard layout handles). #---------------------------------------------------------------------------- def self.get_character(vk) c = self.mapvirkey.call(vk, 2, @language_layout) return '' if c < 32 && (c & DEAD_KEY_MASK) != DEAD_KEY_MASK vsc = self.mapvirkey.call(vk, 0, @language_layout) length = self.tounicode.call(vk,vsc,@state,(r="\0"*4),4,0,@language_layout) return (length == 0 ? '' : r) end end #============================================================================== # ** Mouse #------------------------------------------------------------------------------ # This module performs mouse input processing #============================================================================== module Mouse #-------------------------------------------------------------------------- # * Declare Module Variables #-------------------------------------------------------------------------- # Declare API calls @getWindowRect = LiTTleDRAgo.winapi(2, 'GetWindowRect', 'lp', 'v') @getMessage = LiTTleDRAgo.winapi(2, 'GetMessage', 'plll', 'l') @metrics = LiTTleDRAgo.systemmetrix @getCursorPos = LiTTleDRAgo.cursorposition # Get window thread @window = LiTTleDRAgo.hwnd # Set up double click @double_click = false @double_click_frames = 20 @double_click_pixels = 4 @double_click_counter = 0 # Set up wheel scroll @wheel_scroll = false @wheel_scroll_frames = 10 @wheel_dll = File.exist?('MouseWheel.dll') if @wheel_dll StartWheel = Win32API.new('MouseWheel', 'Initialize', 'i', 'i') StartWheel.call(1) WheelDelta = Win32API.new('MouseWheel', 'WheelDelta', '', 'i') end # Set up drag @drag_pixels = 4 @drag_hold = false @mouse_drag = [false, 0, 0, 0, 0] # Set up last click @last_click_x = 0 @last_click_y = 0 @@unpack_cache = {} #---------------------------------------------------------------------------- # ● Redirect Listing #---------------------------------------------------------------------------- redirect_method 'self.press?', 'Keyboard.press?' redirect_method 'self.trigger?', 'Keyboard.trigger?' redirect_method 'self.repeat?', 'Keyboard.repeat?' redirect_method 'self.release?', 'Keyboard.release?' redirect_method 'self.in_screen?', '(@in_screen)' redirect_method 'self.x', '(@mouse_x)' redirect_method 'self.y', '(@mouse_y)' redirect_method 'self.pos', '([x, y])' redirect_method 'self.dragging?', '(@mouse_drag[0])' #-------------------------------------------------------------------------- # * Mouse Keys #-------------------------------------------------------------------------- SWAPBUTTON = @metrics.call(23) == 0 ? false : true # Buttons Swapped LBUTTON = 'Mouse Left' # Physical Left Button RBUTTON = 'Mouse Right' # Physical Right Button MBUTTON = 'Mouse Middle' # Middle Mouse Button XBUTTON1 = 'Mouse 4' # X1 Mouse Button XBUTTON2 = 'Mouse 5' # X2 Mouse Button PRIMARY = !SWAPBUTTON ? LBUTTON : RBUTTON # Primary Mouse Button SECONDARY = !SWAPBUTTON ? RBUTTON : LBUTTON # Secondary Mouse Button #-------------------------------------------------------------------------- # * Frame Update #-------------------------------------------------------------------------- def self.update @getWindowRect.call(@window, (rect = '0'*16)) left, top, right, bottom = rect.unpack("LLLL") @window_x = left + (me5 = @metrics.call(5)) + (me45 = @metrics.call(45)) @window_y = top + (me6 = @metrics.call(6)) + (me46 = @metrics.call(46)) @window_y += (me4 = @metrics.call(4)) @window_width = (right - left) - ((me5 + me45) * 2) @window_height = ((bottom - top) - ((me6 + me46) * 2)) - me4 @getCursorPos.call((cursor_position = '0'*8)) @mouse_x, @mouse_y = cursor_position.unpack('LL') @mouse_x -= @window_x @mouse_y -= @window_y if (@mouse_x < 0 || @mouse_y < 0 || @mouse_x >= @window_width || @mouse_y >= @window_height) @in_screen = false else @in_screen = true end @mouse_x = @mouse_x.clamp(0, @window_width - 1) @mouse_y = @mouse_y.clamp(0, @window_height - 1) @double_click = false x_deviance = (@last_click_x - @mouse_x).abs y_deviance = (@last_click_y - @mouse_y).abs if self.trigger?(PRIMARY) deviance = @double_click_pixels if (@double_click_counter > 0 && x_deviance <= deviance && y_deviance <= deviance) @double_click = true else @double_click_counter = @double_click_frames end @drag_hold = true @mouse_drag[1] = (@last_click_x = @mouse_x) @mouse_drag[2] = (@last_click_y = @mouse_y) elsif self.press?(PRIMARY) if x_deviance >= @drag_pixels || y_deviance >= @drag_pixels @mouse_drag[0] = true end if @mouse_drag[0] == true @mouse_drag[3] = @mouse_x @mouse_drag[4] = @mouse_y end elsif self.release?(PRIMARY) @drag_hold = false @mouse_drag[0] = false @mouse_drag[1] = 0 @mouse_drag[2] = 0 @mouse_drag[3] = 0 @mouse_drag[4] = 0 end @double_click_counter -= 1 if @double_click_counter > 0 @update_scroll_counter = (@update_scroll_counter || -1) + 1 @update_scroll_counter = update_scroll && 0 if self.scroll_can_update? end #-------------------------------------------------------------------------- # * scroll_can_update? #-------------------------------------------------------------------------- def self.scroll_can_update? return false unless @in_screen return true if @wheel_dll return false unless @wheel_scroll return false unless @update_scroll_counter % @wheel_scroll_frames == 0 return false if Keyboard.pressed_any? return false if @drag_hold || @mouse_drag[0] return true end #-------------------------------------------------------------------------- # * unpack_dword #-------------------------------------------------------------------------- def self.unpack_dword(buffer, offset = 0) bitso = [] buffer.each_byte {|s| bitso << s } ret = bitso[offset + 0] & 0x000000ff ret |=(bitso[offset + 1] << (8 * 1)) & 0x0000ff00 ret |=(bitso[offset + 2] << (8 * 2)) & 0x00ff0000 ret |=(bitso[offset + 3] << (8 * 3)) & 0xff000000 return ret end #-------------------------------------------------------------------------- # * wmcallback #-------------------------------------------------------------------------- def self.wmcallback(buffer) @@unpack_cache[buffer] ||= ( message = unpack_dword(buffer,4 * 1) if message == 0x0000020A wparam = unpack_dword(buffer, 4 * 2) wparam > 10000000 ? -1 : 1 else 0 end ) @@unpack_cache.shift! if @@unpack_cache.size > 500 return @@unpack_cache[buffer] end #-------------------------------------------------------------------------- # * scroll_up? #-------------------------------------------------------------------------- def self.scroll_up? (@delta ||= self.update_scroll) > 0 end #-------------------------------------------------------------------------- # * scroll_down? #-------------------------------------------------------------------------- def self.scroll_down? (@delta ||= self.update_scroll) < 0 end #-------------------------------------------------------------------------- # * scroll? #-------------------------------------------------------------------------- def self.scroll? (@delta ||= 0) != 0 end #-------------------------------------------------------------------------- # * update_scroll #-------------------------------------------------------------------------- def self.update_scroll if @wheel_dll @delta = WheelDelta.call return end @getMessage.call((msg = "\0" * 32), @window, 0, 0) @delta = wmcallback(msg) end #-------------------------------------------------------------------------- # * Get Mouse Drag Rect #-------------------------------------------------------------------------- def self.drag_rect return Rect.new(0, 0, 0, 0) unless @mouse_drag[0] x = @mouse_drag[1] <= @mouse_drag[3] ? @mouse_drag[1] : @mouse_drag[3] y = @mouse_drag[2] <= @mouse_drag[4] ? @mouse_drag[2] : @mouse_drag[4] width = @mouse_drag[1] > @mouse_drag[3] ? @mouse_drag[1] : @mouse_drag[3] height = @mouse_drag[2] > @mouse_drag[4] ? @mouse_drag[2] : @mouse_drag[4] width -= x height -= y return Rect.new(x, y, width, height) end #-------------------------------------------------------------------------- # * Get Mouse Drag Coordinates #-------------------------------------------------------------------------- def self.drag_coor return @mouse_drag[0] ? [@mouse_drag[1], @mouse_drag[2]] : self.pos end #-------------------------------------------------------------------------- # * Get Mouse in Area Flag #-------------------------------------------------------------------------- def self.in_area?(*args) return false if !@in_screen if args[0].is_a?(Rect) return (@mouse_x >= args[0].x && @mouse_y >= args[0].y && @mouse_x < args[0].x + args[0].width && @mouse_y < args[0].y + args[0].height) else return (@mouse_x >= args[0] && @mouse_y >= args[1] && @mouse_x < args[0] + args[2] && @mouse_y < args[1] + args[3]) end end #-------------------------------------------------------------------------- # * Initialize Module #-------------------------------------------------------------------------- begin self.update end end Input.auto_update.push([Keyboard,:update],[Mouse,:update]) #============================================================================== # ** Kernel #------------------------------------------------------------------------------ # #============================================================================== module Kernel #------------------------------------------------------------------------- # * Overwriten method: press_any_key #------------------------------------------------------------------------- def press_any_key return true if Keyboard.pressed_any? && Mouse.not.dragging? end end