By: Gustavo Bicalho (Gu574v0; Gust)
Introduction
Gus' Text Input Module (GTI) is an input module to complement the old ones. It provides a way to capture key presses like a text editor would: with special signs, diacritical marks and respecting the keyboard layout. The old input modules can't do that, so they are not appropriate for text input (like allowing the player to write a name or a memo).
On the other side, this input module doesn't track key-ups and downs, so it's not really appropriate for normal game play because you can't get the current state of a key, you can only know which characters were typed since the last update; and it will only pick key presses that create characters.
But anyway, GTI won't break any input system, so it can be used together with them.
That said, my advice: use a good old input module (or just the default one) for general gameplay, and activate GTI whenever you need text input.
I don't know exactly how many people out there wanted to let the player type something and couldn't, but I hope this can be used to make user interface friendlier.
Features
- Characters typed are read correctly, accordingly to the keyboard layout.
- Won't break the old input modules (unless they have a DLL that also messes with the window procedures, but I don't think any of them does that. Anyway, if that is the case, placing GTI under those scripts should solve the problem.)
- The module is updated automatically by the Input module.
Script
Download of the DLL
DLL Source Code (if you want to check it out)
Code:
#================================================================
# ** GTI - Gus' Text Input for RPG Maker XP/VX
# (CC-BY) Gustavo Bicalho
#---------------------------------------------------------------
# * Version 1.00
# - First release
#---------------------------------------------------------------
# Gus' Text Input (GTI) is an input module to complement the old
# ones. It provides a way to capture key presses like a text
# editor would: with special signs, diacritical marks and
# respecting the keyboard layout. The old input modules can't
# do that, so they are not appropriate for text input.
#
# On the other side, this input module doesn't track key-ups and
# downs, so it's not appropriate for normal game play because
# you can't get the current state of a key, you can only know
# which characters were typed since the last update.
#
# Anyway, GTI won't break any input system, so it can be used
# together with them.
#
# That said, my advice: use a good old input module (or just
# the default one) for general gameplay, and activate GTI
# whenever you need text input.
#
# * PLACE THIS SCRIPT ABOVE MAIN *
#---------------------------------------------------------------
# Some Info:
#
# 1) GTI is updated automatically by the Input module update.
# So all you need to do is iterate through the events list
# (GTI.events) and handle each event. If you call the update
# method directly, you'll lose the events cought in the previous
# update. The events list is cleared when the module updates,
# so don't bother doing that.
#
# 2) Each event is an array where the first element is a string
# containing the character typed (this string can be several
# bytes long, because of the UTF-8 encoding. But it's one
# character per event).
# The second element in the array is the repeat count: when
# the user holds a key, Windows sometimes will send one event
# for several "repeats", with the repeat count indicating how
# many repeats that event represents. Most of the time, however,
# Windows will send one event per repeat. So you could ignore
# the repeat count without losing any important info.
#
# 3) A few constants are provided which represent common
# special keys. You should probably make a few other tests to
# check if a character is printable before writing them to the
# screen or whatever.
#
# 4) GTI is NOT thread-safe. I know almost nobody uses threads
# in RGSS, but I thought I should warn you anyway.
#================================================================
module GTI
#-------------------------------------------------------------
# * Special key strings
#-------------------------------------------------------------
BACKSPACE = "\010"
ESCAPE = "\e"
RETURN = "\r"
#-------------------------------------------------------------
# * WinApi and GTI DLL constants and callers.
#-------------------------------------------------------------
DLL_NAME = 'GusTextInput'
HookTextInput = Win32API.new(DLL_NAME,'HookTextInput','L','L')
UnhookTextInput = Win32API.new(DLL_NAME,'UnhookTextInput','','L')
PopEvent = Win32API.new(DLL_NAME,'PopEvent','P','I')
GetActiveWindow = Win32API.new('user32', 'GetActiveWindow', '', 'L')
#-------------------------------------------------------------
# * GTI.running?
# Returns true if GTI is active, false otherwise.
#-------------------------------------------------------------
@running = false
def self.running?
return @running
end
#-------------------------------------------------------------
# * GTI.events
# Returns the event list, where the events catch at the
# last update are stored for handling.
#-------------------------------------------------------------
@events = []
def self.events
return @events
end
#-------------------------------------------------------------
# * GTI.start
# Activates the text input.
#-------------------------------------------------------------
def self.start
return if @running
# Call the DLL method that activates the text input on
# the game window
err = HookTextInput.call(GetActiveWindow.call)
# err stores the error code. If it's 0, it's ok.
# If it's different from 0, raises an exception.
# This will never happen unless you're using another thing
# that also messes with the window procs, so you don't
# need to worry.
raise "Couldn't hook the text input. Error #{err}" if (err != 0)
@running = true; # GTI is now running
end
#-------------------------------------------------------------
# * GTI.stop
# Deactivates the text input.
#-------------------------------------------------------------
def self.stop
return if !@running
# Call the DLL method that deactivates the text input
err = UnhookTextInput.call
# err stores the error code. If it's 0, it's ok.
# If it's different from 0, raises an exception.
# This will never happen unless you're using another thing
# that also messes with the window procs, so you don't
# need to worry.
raise "Couldn't unhook the text input. Error #{err}" if (err != 0)
@running = false # GTI is not running anymore
end
#-------------------------------------------------------------
# * GTI.update
# Updates the module, popping the events from the DLL event
# stack and placing them at the event list.
#-------------------------------------------------------------
def self.update
return if !@running # Stop if GTI is not running
@events.clear; # Clear the event list
# While there are events on the event stack, pop one
# and add it to the event list.
while ((c = GTI.pop) != nil)
@events.push(c)
end
end
private
#-------------------------------------------------------------
# * GTI.pop
# Pops an event from the DLL event stack. Users shouldn't
# call this method. Use the event list instead.
#-------------------------------------------------------------
def self.pop
# Allocate space so the DLL can put its data there
# The DLL needs a 8-byte string.
s = ' '*8
# Call the PopEvent method from the DLL. It puts the data
# in the string bytes, and returns the number of bytes
# used to store the character. This number is up to 4.
# If it returns 0, there were no events to pop, so
# return nil.
return nil if ((ret = PopEvent.call(s)) == 0)
# Copy the part of s that store the character
n = s[0,ret]
# Unpack the string to get its bytes' values. We'll
# only need the 5th byte (that's the first C), that stores
# the repeat count for the event.
s = s.unpack("LCCCC")
return [n,s[1]] # Array with character and repeat count
end
end
#================================================================
# ** Input
#---------------------------------------------------------------
# Changes the Input.update method so it calls GTI.update.
#================================================================
module Input
class << self
unless method_defined?(:gus_gti_input_update)
alias :gus_gti_input_update :update
def update
gus_gti_input_update
GTI.update
end
end
end
end
Instructions
Place this script above Main, and below the default scripts (and below any scripts with custom DLLs that mess with the window). Place the GusTextInput.dll in the same directory as Game.exe.
Whenever you need text input, call GTI.start to start the event processing. You can just leave it running through the whole game, or activate it only in Scenes where you need it and then call GTI.stop to stop it. The latter is probably the best if you need performance.
Anyway, once you activate it, you can get the event list (an array) from GTI.events at each update. The list is cleared when the module is updated, so each frame you get only the event of that frame. You can then iterate through the list, processing each event (the first of the list happened first and therefore should be processed first. Just use an each statement).
Below is an example of how Scene_Name (on RMXP) could be implemented using GTI:
Code:
class Scene_Name
def main
@actor = $game_actors[$game_temp.name_actor_id]
@edit_window = Window_NameEdit.new(@actor, $game_temp.name_max_char)
Graphics.transition
GTI.start # Start Text Input
loop do
Graphics.update
Input.update
update
if $scene != self
break
end
end
GTI.stop # Stop Text Input
Graphics.freeze
@edit_window.dispose
end
#this handles the events as they come
def handle_input(c)
if c[0] == GTI::BACKSPACE #if a backspace is received, go back
@edit_window.back
return
elsif c[0] == GTI::RETURN # a return, confirm
if @edit_window.name == "" # if the box is empty
@edit_window.restore_default # restore the default name
$game_system.se_play($data_system.buzzer_se)
return
end
# Set the actor name and go back to the map
@actor.name = @edit_window.name
$game_system.se_play($data_system.decision_se)
$scene = Scene_Map.new
return
elsif c[0] == GTI::ESCAPE # escape pressed, back to the map
$scene = Scene_Map.new
return
else # any other key pressed is added to the name
if @edit_window.index == $game_temp.name_max_char
$game_system.se_play($data_system.buzzer_se)
return
end
@edit_window.add(c[0]*c[1])
return
end
end
def update
@edit_window.update
# Iterate the event list, calling the handler method
GTI.events.each { |c| handle_input(c) }
end
end
Terms and Conditions
- You can redistribute and change this script and the DLL as you wish, as long as you give credit to me (Gustavo Bicalho) as the original creator.