summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAmir Rajan <[email protected]>2019-08-29 18:19:05 -0500
committerAmir Rajan <[email protected]>2019-08-29 18:20:33 -0500
commit9c2ce126dba8a4c3ada82e5f4bd6637b1fa92ab0 (patch)
tree241ed4b1dc46b1bd53f185f80e238f31f4c12403
parent62343afae7e3776b8f700ffeac33c0ff3f651c61 (diff)
downloaddragonruby-game-toolkit-contrib-9c2ce126dba8a4c3ada82e5f4bd6637b1fa92ab0.tar.gz
dragonruby-game-toolkit-contrib-9c2ce126dba8a4c3ada82e5f4bd6637b1fa92ab0.zip
Synced from GTK source.
-rw-r--r--dragon/console.rb191
-rw-r--r--dragon/controller_config.rb390
2 files changed, 531 insertions, 50 deletions
diff --git a/dragon/console.rb b/dragon/console.rb
index fb74da1..3bd3e0b 100644
--- a/dragon/console.rb
+++ b/dragon/console.rb
@@ -1,5 +1,5 @@
# Copyright 2019 DragonRuby LLC
-
+# MIT License
# console.rb has been released under MIT (*only this file*).
module GTK
@@ -8,7 +8,8 @@ module GTK
:text_color, :cursor_color, :font, :animation_duration,
:max_log_lines, :max_history, :current_input_str, :log,
:last_command_errored, :last_command, :error_color, :shown_at,
- :header_color
+ :header_color, :archived_log, :last_log_lines, :last_log_lines_count,
+ :suppress_left_arrow_behavior
def initialize
@disabled = false
@@ -16,9 +17,10 @@ module GTK
@log_offset = 0
@visible = false
@toast_ids = []
+ @archived_log = []
@log = [ 'Console ready.' ]
- @max_log_lines = 100 # I guess...?
- @max_history = 100 # I guess...?
+ @max_log_lines = 1000 # I guess...?
+ @max_history = 1000 # I guess...?
@command_history = []
@command_history_index = -1
@nonhistory_input = ''
@@ -73,13 +75,36 @@ module GTK
end
def addtext obj
+ @last_log_lines_count ||= 1
+
str = obj.to_s
+
+ log_lines = []
+
str.each_line { |s|
- @log.shift if @log.length > 1000
s.wrapped_lines(console_text_width).each do |l|
- @log << l
+ log_lines << l
end
}
+
+ if log_lines == @last_log_lines
+ @last_log_lines_count += 1
+ new_log_line_with_count = @last_log_lines.last + " (#{@last_log_lines_count})"
+ if log_lines.length > 1
+ @log = @log[0..-(@log.length - log_lines.length)] + log_lines[0..-2] + [new_log_line_with_count]
+ else
+ @log = @log[0..-(@log.length - log_lines.length)] + [new_log_line_with_count]
+ end
+ return
+ end
+
+ log_lines.each do |l|
+ @log.shift if @log.length > @max_log_lines
+ @log << l
+ end
+
+ @last_log_lines_count = 1
+ @last_log_lines = log_lines
end
def ready?
@@ -103,6 +128,11 @@ module GTK
def hide
if visible?
toggle
+ @archived_log += @log
+ if @archived_log.length > @max_log_lines
+ @archived_log = @archived_log.drop(@archived_log.length - @max_log_lines)
+ end
+ @log.clear
@show_reason = nil
clear_toast
end
@@ -112,10 +142,6 @@ module GTK
hide
end
- def clear
- @log.clear
- end
-
def clear_toast
@toasted_at = nil
@toast_duration = 0
@@ -193,6 +219,39 @@ S
args.inputs.keyboard.key_down.ordinal_indicator!
end
+ def eval_the_set_command
+ cmd = @current_input_str.strip
+ if cmd.length != 0
+ @log_offset = 0
+ @current_input_str = ''
+
+ @command_history.pop while @command_history.length >= @max_history
+ @command_history.unshift cmd
+ @command_history_index = -1
+ @nonhistory_input = ''
+
+ if cmd == 'quit' || cmd == ':wq' || cmd == ':q!' || cmd == ':q' || cmd == ':wqa'
+ $gtk.request_quit
+ else
+ puts "-> #{cmd}"
+ begin
+ @last_command = cmd
+ $gtk.ffi_mrb.eval("$results = (#{cmd})")
+ if $results.nil?
+ puts "=> nil"
+ else
+ puts "=> #{$results}"
+ end
+ @last_command_errored = false
+ rescue Exception => e
+ @last_command_errored = true
+ puts "#{e}"
+ log "#{e}"
+ end
+ end
+ end
+ end
+
def process_inputs args
if console_toggle_key_down? args
args.inputs.text.clear
@@ -201,40 +260,38 @@ S
return unless visible?
+ if !@suppress_left_arrow_behavior && args.inputs.keyboard.key_down.left && (@current_input_str || '').strip.length > 0
+ log_info "Use repl.rb!", <<-S
+The Console is nice for quick commands, but for more complex edits, use repl.rb.
+
+I've written the current command at the top of a file called ./repl.rb (right next to dragonruby(.exe)). Please open the the file and apply additional edits there.
+S
+ if @last_command_written_to_repl_rb != @current_input_str
+ @last_command_written_to_repl_rb = @current_input_str
+ contents = $gtk.read_file 'repl.rb'
+ contents ||= ''
+ contents = <<-S + contents
+
+# Remove the x from xrepl to run the command.
+xrepl do
+ #{@last_command_written_to_repl_rb}
+end
+
+S
+ $gtk.suppress_hotload = true
+ $gtk.write_file 'repl.rb', contents
+ $gtk.reload_if_needed 'repl.rb', true
+ $gtk.suppress_hotload = false
+ end
+
+ return
+ end
+
args.inputs.text.each { |str| @current_input_str << str }
args.inputs.text.clear
if args.inputs.keyboard.key_down.enter
- cmd = @current_input_str.strip
- if cmd.length != 0
- @log_offset = 0
- @current_input_str = ''
-
- @command_history.pop while @command_history.length >= @max_history
- @command_history.unshift cmd
- @command_history_index = -1
- @nonhistory_input = ''
-
- if cmd == 'quit' || cmd == ':wq' || cmd == ':q!' || cmd == ':q' || cmd == ':wqa'
- $gtk.request_quit
- else
- puts "-> #{cmd}"
- begin
- @last_command = cmd
- $gtk.ffi_mrb.eval("$results = (#{cmd})")
- if $results.nil?
- puts "=> nil"
- else
- puts "=> #{$results}"
- end
- @last_command_errored = false
- rescue Exception => e
- @last_command_errored = true
- puts "#{e}"
- log "#{e}"
- end
- end
- end
+ eval_the_set_command
elsif args.inputs.keyboard.key_down.v
if args.inputs.keyboard.key_down.control || args.inputs.keyboard.key_down.meta
@current_input_str << $gtk.ffi_misc.getclipboard
@@ -279,6 +336,19 @@ S
args.inputs.keyboard.key_held.clear
end
+ def write_line args, left, y, str, errorinfo, headerinfo, txtinfo
+ str ||= ''
+ if include_error_marker? str
+ args.outputs.reserved << [left + 10, y, str, 1, 0, *errorinfo].label
+ elsif include_subdued_markers? str
+ args.outputs.reserved << [left + 10, y, str, 1, 0, [txtinfo[0..2], txtinfo[3].half]].label
+ elsif str.start_with?("====") || str.include?("app")
+ args.outputs.reserved << [left + 10, y, str, 1, 0, *headerinfo].label
+ else
+ args.outputs.reserved << [left + 10, y, str, 1, 0, *txtinfo].label
+ end
+ end
+
def render args
return if !@toggled_at
@@ -315,25 +385,46 @@ S
y += h # !!! FIXME: remove this when we fix coordinate origin on labels.
((@log.size - @log_offset) - 1).downto(0) do |idx|
- str = @log[idx] || ""
- if include_error_marker? str
- args.outputs.reserved << [left + 10, y, str, 1, 0, *errorinfo].label
- elsif str.start_with? "===="
- args.outputs.reserved << [left + 10, y, str, 1, 0, *headerinfo].label
- else
- args.outputs.reserved << [left + 10, y, str, 1, 0, *txtinfo].label
- end
+ write_line args, left, y, @log[idx], errorinfo, headerinfo, txtinfo
+ y += h
+ break if y > top
+ end
+
+ # past log seperator
+ args.outputs.reserved << [0, y - h.half, 1280, y - h.half, [txtinfo[0..2], txtinfo[3].idiv(4)]].line
+
+ y += h
+
+ txtinfo = [ @text_color[0], @text_color[1], @text_color[2], (@text_color[3].to_f * percent.half).to_i, @font ]
+ errorinfo = [ @error_color[0], @error_color[1], @error_color[2], (@error_color[3].to_f * percent.half).to_i, @font ]
+ cursorinfo = [ @cursor_color[0], @cursor_color[1], @cursor_color[2], (@cursor_color[3].to_f * percent.half).to_i, @font ]
+ headerinfo = [ @header_color[0], @header_color[1], @header_color[2], (@header_color[3].to_f * percent.half).to_i, @font ]
+
+ ((@archived_log.size - @log_offset) - 1).downto(0) do |idx|
+ write_line args, left, y, @archived_log[idx], errorinfo, headerinfo, txtinfo
y += h
break if y > top
end
end
def include_error_marker? text
- error_markers.any? { |e| text.downcase.include? e }
+ include_any_words? text, error_markers
end
def error_markers
- ["exception:", "error:", "undefined method", "failed", "syntax"]
+ ["exception", "error", "undefined method", "failed", "syntax", "deprecated"]
+ end
+
+ def include_subdued_markers? text
+ include_any_words? text, subdued_markers
+ end
+
+ def include_any_words? text, words
+ words.any? { |w| text.downcase.include? w }
+ end
+
+ def subdued_markers
+ ["reloaded", "exported the"]
end
def calc args
diff --git a/dragon/controller_config.rb b/dragon/controller_config.rb
new file mode 100644
index 0000000..18bc92d
--- /dev/null
+++ b/dragon/controller_config.rb
@@ -0,0 +1,390 @@
+# Copyright 2019 DragonRuby LLC
+# MIT License
+# controller_config.rb has been released under MIT (*only this file*).
+
+# !!! FIXME: add console command to forget custom binding(s)
+# !!! FIXME: add console command to forget replace existing binding(s)
+# !!! FIXME: add console command go into play_around mode to make sure controller isn't wonky.
+
+module GTK
+ class ControllerConfig
+ def initialize runtime
+ @runtime = runtime
+ @raw_joysticks = {} # things that aren't game controllers to try to configure.
+ @target = nil
+ @animation_duration = (1.5).seconds
+ @toggled_at = 0
+ @fading = 0
+ @current_part = 0
+ @part_alpha = 0
+ @part_alpha_increment = 10
+ @joystick_state = {}
+ @playing_around = false
+ @used_bindings = {}
+ @bindings = []
+ @parts = [
+ [ 919, 282, 'A button', 'a' ],
+ [ 960, 323, 'B button', 'b' ],
+ [ 878, 323, 'X button', 'x' ],
+ [ 919, 365, 'Y button', 'y' ],
+ [ 433, 246, 'left stick left', '-leftx' ],
+ [ 497, 246, 'left stick right', '+leftx' ],
+ [ 466, 283, 'left stick up', '-lefty' ],
+ [ 466, 218, 'left stick down', '+lefty' ],
+ [ 466, 246, 'left stick button', 'leftstick' ],
+ [ 741, 246, 'right stick left', '-rightx' ],
+ [ 802, 246, 'right stick right', '+rightx' ],
+ [ 773, 283, 'right stick up', '-righty' ],
+ [ 773, 218, 'right stick down', '+righty' ],
+ [ 772, 246, 'right stick button', 'rightstick' ],
+ [ 263, 465, 'left shoulder button', 'leftshoulder' ],
+ [ 263, 503, 'left trigger', 'lefttrigger' ],
+ [ 977, 465, 'right shoulder button', 'rightshoulder' ],
+ [ 977, 503, 'right trigger', 'righttrigger' ],
+ [ 318, 365, 'D-pad up', 'dpup' ],
+ [ 360, 322, 'D-pad right', 'dpright' ],
+ [ 318, 280, 'D-pad down', 'dpdown' ],
+ [ 275, 322, 'D-pad left', 'dpleft' ],
+ [ 570, 402, 'select/back button', 'back'],
+ [ 619, 448, 'guide/home button', 'guide' ],
+ [ 669, 402, 'start button', 'start' ],
+ ]
+ end
+
+ def rawjoystick_connected jid, joystickname, guid
+ return if jid < 0
+ @raw_joysticks[jid] = { name: joystickname, guid: guid }
+ end
+
+ def rawjoystick_disconnected jid
+ return if jid < 0
+ if @raw_joysticks[jid] != nil
+ @raw_joysticks.delete(jid)
+ @runtime.ffi_misc.close_raw_joystick(jid)
+ # Fade out the config screen if we were literally configuring this controller right now.
+ if [email protected]? && @target[0] == jid
+ @target[0] = nil
+ @toggled_at = Kernel.global_tick_count
+ @fading = -1
+ end
+ end
+ end
+
+ def build_binding_string
+ bindingstr = ''
+ skip = false
+
+ if skip ; skip = false ; next ; end
+
+ binding = @bindings[i]
+ next if binding.nil?
+
+ part = @parts[i][3]
+
+ # clean up string:
+ # if axis uses -a0 for negative and +a0 for positive, just make it "leftx:a0" instead of "-leftx:-a0,+leftx:+a0"
+ # if axis uses +a0 for negative and -a0 for positive, just make it "leftx:a0~" instead of "-leftx:+a0,+leftx:-a0"
+ if part == '-leftx' || part == '-lefty' || part == '-rightx' || part == '-righty'
+ nextbinding = @bindings[i+1]
+ if binding.start_with?('-a') && nextbinding.start_with?('+a') && binding[2..-1] == nextbinding[2..-1]
+ skip = true
+ part = part[1..-1]
+ binding = binding[1..-1]
+ elsif binding.start_with?('+a') && nextbinding.start_with?('-a') && binding[2..-1] == nextbinding[2..-1]
+ skip = true
+ part = part[1..-1]
+ binding = "#{binding[1..-1]}~"
+ end
+ end
+
+ bindingstr += "#{!bindingstr.empty? ? ',' : ''}#{part}:#{binding}"
+ end
+
+ details = @target[1]
+
+ # !!! FIXME: no String.delete in mRuby?!?! Maybe so when upgrading.
+ #name = details[:name].delete(',')
+ # !!! FIXME: ...no regexp either... :/
+ #name = details[:name].gsub(/,/, ' ') # !!! FIXME: will SDL let you escape these instead?
+ unescaped = details[:name]
+ name = ''
+ for i in 0..unescaped.length-1
+ ch = unescaped[i]
+ name += (ch == ',') ? ' ' : ch
+ end
+ return "#{details[:guid]},#{name},platform:#{@runtime.platform},#{bindingstr}"
+ end
+
+ def move_to_different_part part
+ if !@joystick_state[:axes].nil?
+ @joystick_state[:axes].each { |i| i[:farthestval] = i[:startingval] if !i.nil? }
+ end
+ @current_part = part
+ end
+
+ def previous_part
+ if @current_part > 0
+ # remove the binding that we previous had here so it can be reused.
+ bindstr = @bindings[@current_part - 1]
+ @bindings[@current_part - 1] = nil
+ @used_bindings[bindstr] = nil
+ move_to_different_part @current_part - 1
+ end
+ end
+
+ def next_part
+ if @current_part < (@parts.length - 1)
+ move_to_different_part @current_part + 1
+ else
+ @playing_around = true
+ end
+ end
+
+ def set_binding bindstr
+ return false if !@used_bindings[bindstr].nil?
+ @used_bindings[bindstr] = @current_part
+ @bindings[@current_part] = bindstr
+ return true
+ end
+
+ # Called when a lowlevel joystick moves an axis.
+ def rawjoystick_axis jid, axis, value
+ return if @target.nil? || jid != @target[0] || @fading != 0 # skip if not currently considering this joystick.
+
+ @joystick_state[:axes] ||= []
+ @joystick_state[:axes][axis] ||= {
+ moving: false,
+ startingval: 0,
+ currentval: 0,
+ farthestval: 0
+ }
+
+ # this is the logic from SDL's controllermap.c, more or less, since this is hard to get right from scratch.
+ state = @joystick_state[:axes][axis]
+ state[:currentval] = value
+ if !state[:moving]
+ state[:moving] = true
+ state[:startingval] = value
+ state[:farthestval] = value
+ end
+
+ current_distance = (value - state[:startingval]).abs
+ farthest_distance = (state[:farthestval] - state[:startingval]).abs
+ if current_distance > farthest_distance
+ state[:farthestval] = value
+ farthest_distance = (state[:farthestval] - state[:startingval]).abs
+ end
+
+ # If we've gone out far enough and started to come back, let's bind this axis
+ if (farthest_distance >= 16000) && (current_distance <= 10000)
+ next_part if set_binding("#{(state[:farthestval] < 0) ? '-' : '+'}a#{axis}")
+ end
+ end
+
+ # Called when a lowlevel joystick moves a hat.
+ def rawjoystick_hat jid, hat, value
+ return if @target.nil? || jid != @target[0] || @fading != 0 # skip if not currently considering this joystick.
+
+ @joystick_state[:hats] ||= []
+ @joystick_state[:hats][hat] = value
+
+ return if value == 0 # 0 == centered, skip it
+ next_part if set_binding("h#{hat}.#{value}")
+ end
+
+ # Called when a lowlevel joystick moves a button.
+ def rawjoystick_button jid, button, pressed
+ return if @target.nil? || jid != @target[0] || @fading != 0 # skip if not currently considering this joystick.
+
+ @joystick_state[:buttons] ||= []
+ @joystick_state[:buttons][button] = pressed
+
+ return if !pressed
+ next_part if set_binding("b#{button}")
+ end
+
+ def calc_fading
+ if @fading == 0
+ return 255
+ elsif @fading > 0 # fading in
+ percent = @toggled_at.ease_using_global_tick_count(@animation_duration, :flip, :quint, :flip)
+ if percent >= 1.0
+ percent = 1.0
+ @fading = 0
+ end
+ else # fading out
+ percent = @toggled_at.ease_using_global_tick_count(@animation_duration, :flip, :quint)
+ if percent <= 0.0
+ percent = 0.0
+ @fading = 0
+ end
+ end
+
+ return (percent * 255.0).to_i
+ end
+
+ def render_basics args, msg, fade=255
+ joystickname = @target[1][:name]
+ args.outputs.primitives << [0, 0, 1280, 720, 255, 255, 255, fade].solid
+ args.outputs.primitives << [0, 0, 1280, 720, 'dragonruby-controller.png', 0, fade, 255, 255, 255].sprite
+ args.outputs.primitives << [1280 / 2, 700, joystickname, 2, 1, 0, 0, 0, fade].label
+ args.outputs.primitives << [1280 / 2, 650, msg, 0, 1, 0, 0, 0, 255].label if !msg.empty?
+ end
+
+ def render_part_highlight args, part, alpha=255
+ partsize = 41
+ args.outputs.primitives << [part[0], part[1], partsize, partsize, 255, 0, 0, alpha].border
+ args.outputs.primitives << [part[0]-1, part[1]-1, partsize+2, partsize+2, 255, 0, 0, alpha].border
+ args.outputs.primitives << [part[0]-2, part[1]-2, partsize+4, partsize+4, 255, 0, 0, alpha].border
+ end
+
+ def choose_target
+ if @target.nil?
+ while !@raw_joysticks.empty?
+ t = @raw_joysticks.shift # see if there's a joystick waiting on us.
+ next if t[0] < 0 # just in case.
+ next if t[1][:guid].nil? # did we already handle this guid? Dump it.
+ @target = t
+ break
+ end
+ return false if @target.nil? # nothing to configure at the moment.
+ @toggled_at = Kernel.global_tick_count
+ @fading = 1
+ @current_part = 0
+ @part_alpha = 0
+ @part_alpha_increment = 10
+ @joystick_state = {}
+ @used_bindings = {}
+ @playing_around = false
+ @bindings = []
+ end
+ return true
+ end
+
+ def render_part_highlight_from_bindstr args, bindstr, alpha=255
+ partidx = @used_bindings[bindstr]
+ return if partidx.nil?
+ render_part_highlight args, @parts[partidx], alpha
+ end
+
+ def play_around args
+ return false if !@playing_around
+
+ if args.inputs.keyboard.key_down.escape
+ @current_part = 0
+ @part_alpha = 0
+ @part_alpha_increment = 10
+ @used_bindings = {}
+ @playing_around = false
+ @bindings = []
+ elsif args.inputs.keyboard.key_down.space
+ jid = @target[0]
+ bindingstr = build_binding_string
+ #puts("new controller binding: '#{bindingstr}'")
+ @runtime.ffi_misc.add_controller_config bindingstr
+ @runtime.ffi_misc.convert_rawjoystick_to_controller jid
+ @target[0] = -1 # Conversion closes the raw joystick.
+
+ # Handle any other pending joysticks that have the same GUID (so if you plug in four of the same model, we're already done!)
+ guid = @target[1][:guid]
+ @raw_joysticks.each { |jid, details|
+ if details[:guid] == guid
+ @runtime.ffi_misc.convert_rawjoystick_to_controller jid
+ details[:guid] = nil
+ end
+ }
+
+ # Done with this guy.
+ @playing_around = false
+ @toggled_at = Kernel.global_tick_count
+ @fading = -1
+ return false
+ end
+
+ render_basics args, 'Now play around with the controller, and make sure it feels right!'
+ args.outputs.primitives << [1280 / 2, 90, '[ESCAPE]: Reconfigure, [SPACE]: Save this configuration', 0, 1, 0, 0, 0, 255].label
+
+ axes = @joystick_state[:axes]
+ if !axes.nil?
+ for i in 0..axes.length-1
+ next if axes[i].nil?
+ value = axes[i][:currentval]
+ next if value.nil? || (value.abs < 16000)
+ render_part_highlight_from_bindstr args, "#{value < 0 ? '-' : '+'}a#{i}"
+ end
+ end
+
+ hats = @joystick_state[:hats]
+ if !hats.nil?
+ for i in 0..hats.length-1
+ value = hats[i]
+ next if value.nil? || (value == 0)
+ render_part_highlight_from_bindstr args, "h#{i}.#{value}"
+ end
+ end
+
+ buttons = @joystick_state[:buttons]
+ if !buttons.nil?
+ for i in 0..buttons.length-1
+ value = buttons[i]
+ next if value.nil? || !value
+ render_part_highlight_from_bindstr args, "b#{i}"
+ end
+ end
+
+ return true
+ end
+
+ def tick args
+ return true if play_around args
+ return false if !choose_target
+
+ jid = @target[0]
+
+ if @fading == 0
+ # Cancel config?
+ if args.inputs.keyboard.key_down.escape
+ # !!! FIXME: prompt to ignore this joystick forever or just this run
+ @toggled_at = Kernel.global_tick_count
+ @fading = -1
+ end
+ end
+
+ if @fading == 0
+ if args.inputs.keyboard.key_down.backspace
+ previous_part
+ elsif args.inputs.keyboard.key_down.space
+ next_part
+ end
+ end
+
+ fade = calc_fading
+ if (@fading < 0) && (fade == 0)
+ @runtime.ffi_misc.close_raw_joystick(jid) if jid >= 0
+ @target = nil # done with this controller
+ return false
+ end
+
+ render_basics args, (@fading >= 0) ? "We don't recognize this controller, so tell us about it!" : '', fade
+
+ return true if fade < 255 # all done for now
+
+ part = @parts[@current_part]
+ args.outputs.primitives << [1280 / 2, 575, "Please press the #{part[2]}.", 0, 1, 0, 0, 0, 255].label
+ render_part_highlight args, part, @part_alpha
+ args.outputs.primitives << [1280 / 2, 90, '[ESCAPE]: Ignore controller, [BACKSPACE]: Go back one button, [SPACE]: Skip this button', 0, 1, 0, 0, 0, 255].label
+
+ @part_alpha += @part_alpha_increment
+ if (@part_alpha_increment > 0) && (@part_alpha >= 255)
+ @part_alpha = 255
+ @part_alpha_increment = -10
+ elsif (@part_alpha_increment < 0) && (@part_alpha <= 0)
+ @part_alpha = 0
+ @part_alpha_increment = 10
+ end
+
+ return true
+ end
+ end
+end