Associating a textinput event with the keypressed event that triggered it

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
pgimeno
Party member
Posts: 1909
Joined: Sun Oct 18, 2015 2:58 pm
Location: Valencia, ES

Associating a textinput event with the keypressed event that triggered it

Post by pgimeno » Wed Nov 06, 2019 7:46 pm

I am trying to accomplish what the title says, but it seems quite tricky. See this post for background.

The first problem is that textinput doesn't ever seem to come before keypressed (if it preceded the generating key all the time, it would have been pretty trivial). It always comes afterwards, and of course for some keys it may not come at all. Second, in Android it's possible that some keyreleased events precede the textinput event. For example, in my phone, when I press Shift+"+" in a wireless keyboard with an en_US layout and with setTextInput(true), the events registered are: shift pressed, "=" pressed, "=" released, shift released, and "+" textinput, in that order, which is extremely annoying.

Furthermore, I'd like a solution that is somewhat future-proof, i.e. that doesn't need lots of changes to adapt it to incoming LÖVE versions. Traditionally, love.run has suffered lots of changes over the years, with every major version having a different one. The latest change in 11.0 in particular was quite major.

The best place to perform the interception seems to be love.handlers, which is kind of an intermediate layer between love.run and application code, because very few programs intercept it, and it's the lowest possible level before love.run.

This technique does have problems: if a quit event follows a keypressed event, the quit event is re-enqueued, therefore all events that were waiting in the queue after the quit event, if any, will be processed. Hopefully that's not too much disruption.

Any better solution than this?

(As a proof of concept, I'm passing the associated textinput and the textinput expectancy check as upvalues of love.keypressed and love.textinput).

Code: Select all

local txt
local expected_textinput = false

function love.keypressed(key, scan, is_repeat)
  if key == 'escape' then return love.event.quit() end -- debug key
  print("pressed: ".. key,scan, "text: " .. tostring(txt))
end

function love.keyreleased(key, scan)
  print("release: " .. key, scan)
end

function love.textinput(txt)
  if not expected_textinput then
    print("**** UNEXPECTED TEXTINPUT: ****", txt)
  end
end

local krq = {}
local poll_i = love.event.poll()
local old_keypressed_handler = love.handlers.keypressed

function love.handlers.keypressed(key, scan, is_repeat)
  -- Poll the event queue, as we can't peek
  local name, a, b, c, d, e, f = poll_i()
  local qlen = 0
  while name == 'keyreleased' do
    -- enqueue in order of reception
    qlen = qlen + 4
    krq[qlen - 3] = a
    krq[qlen - 2] = b
    krq[qlen - 1] = c
    krq[qlen] = d
    name, a, b, c, d, e, f = poll_i()
  end
  if name == 'textinput' then
    -- This keypressed event generated a keyreleased event
    txt = a
  else
    -- No textinput event associated with this keypress
    txt = nil
  end
  old_keypressed_handler(key, scan, is_repeat)
  for i = 1, qlen, 4 do
    love.handlers.keyreleased(krq[i], krq[i + 1], krq[i + 2], krq[i + 3])
  end
  if name == 'quit' then
    love.event.push('quit', a, b, c, d, e, f) -- re-enqueue
  elseif name == 'textinput' then
    expected_textinput = true
    love.handlers.textinput(a, b, c, d, e, f)
    expected_textinput = false
  elseif name then
    love.handlers[name](a, b, c, d, e, f)
  end
end
Last edited by pgimeno on Fri Nov 08, 2019 12:09 am, edited 1 time in total.

User avatar
raidho36
Party member
Posts: 1954
Joined: Mon Jun 17, 2013 12:00 pm

Re: Associating a textinput event with the keypressed event that triggered it

Post by raidho36 » Thu Nov 07, 2019 12:06 am

Textinput exist to handle keyboard-independent text input, stuff like japanese writing. Rigging keyboard-agnostic system to a physical keyboard seems like approaching the core problem at a huge tangent.

Keystrokes would produce key events immediately, whereas text input may or may not be generated by that keystrokes. That's why key events would consistently come before input events. Additionally, text input may be generated not by a keystroke at all. In general there's no connection between particular keystrokes and following textinput events. Most of the time there will be evident connection but don't give it any more weight than to a coincidence, as that's only occurring under specific circumstances.

That said, as far as I could tell, appropriate event order will be maintained. If your object is to handle character deletion without missing mid-frame keystrokes, existing system seems perfectly sufficient. Do try to see if the problem exists in the first place to warrant going to the effort of solving it.

User avatar
pgimeno
Party member
Posts: 1909
Joined: Sun Oct 18, 2015 2:58 pm
Location: Valencia, ES

Re: Associating a textinput event with the keypressed event that triggered it

Post by pgimeno » Thu Nov 07, 2019 7:00 pm

raidho36 wrote:
Thu Nov 07, 2019 12:06 am
Do try to see if the problem exists in the first place to warrant going to the effort of solving it.
Maybe you're right. The need arose in relation to babulous' TimelineEvents library, see this thread: https://love2d.org/forums/viewtopic.php ... 2&start=10

I guess that it can enqueue keypressed and textinput events in the same queue, with a type field that distinguishes between them, and read both types with a single function like somethingKeyActivitysomething, which if I understood correctly, is what babulous suggests. A theoretical edit control would then ignore the keystrokes that generated the text input and process the editing ones like arrows and backspace, and the text input events themselves.

Post Reply

Who is online

Users browsing this forum: No registered users and 11 guests