Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
KeyInput plugin (was iCade plugin) — Gideros Forum

KeyInput plugin (was iCade plugin)

MagnusviriMagnusviri Member
edited April 2012 in Plugins
I decided it was best to generalize my plugin so that it just sent keyboard events to Lua. This way programmers gets to interpret the events however they want.

@Caroline, you mentioned you had to print the stack to see what was going on. I have this stackDump() function that I got from PIL, but it only prints types. Do you have anything better?

I've got it all working except my event dispatching isn't working. I've read the Ultimate Guide on plugins. I've read some of the Programing In Lua book sections on C. Can anyone help me figure out what I've done wrong?

Also, @Caroline, you mentioned in my thread on the iCadePlugin that I needed to put dispatchEvent inside of the C++ class. I actually got the idea to put eventDispatch at the top from storeKit.mm. And from what I understand, it should work. Although I don't understand why one needs a class that inherits from GEventDispatcherProxy, so I could totally be wrong.

I did do a little goofing around and in my Lua file I redefined Event.new() and put an io.write() command in it and it runs, so I know I'm creating a new event in my dispatchEvent function. And I believe lua_pcall() is even executing successfully.

I'm on my iPad so I can't upload a file, and it wont let me post the full code in one message, so I'll split this over a few posts.

Edited to remove the code. See it in the attachment below.

Likes: Zoyt, johnyc100

+1 -1 (+2 / -0 )Share on Facebook

Comments

  • MagnusviriMagnusviri Member
    edited April 2012
    EDITED to remove all of the code, find it in the attachment below.

    And here is the Lua file. It toggles the keyboard on and off every 4 seconds. Feedback is written to the Xcode console (and so far is coming from the plugin, not the Lua code). None of the Lua event handlers are getting called.
    function keyboardWillShow(event)
    	io.write("Lua keyboardWillShow!\n")
    end
    function keyboardWillHide(event)
    	io.write("Lua keyboardWillHide!\n")
    end
    function keyPressed(event)
    	io.write("Lua keyPressed!\n")
    end
     
    require "keyinput"
    keyinput:setActive(true)
     
    stage:addEventListener("keyboardWillShow",keyboardWillShow,nil)
    stage:addEventListener("keyboardWillHide",keyboardWillHide,nil)
    stage:addEventListener("keyPressed",keyPressed,nil)
     
    local timer = Timer.new(4000, 0)
    local function onTimer(event)
    	keyinput:setActive(not keyinput:active())
    end
    timer:addEventListener(Event.TIMER, onTimer)
    timer:start()
  • MagnusviriMagnusviri Member
    edited April 2012
    Oh my, the forum removed a bunch of things from the code. I'll upload a file.

    Edit: I've updated the file.
    zip
    zip
    keyinput.mm.zip
    3K
  • I was probably wrong about the dispatchEvent.

    I'll have a fumble with your code until Atilim leaps in and sorts it out in a blinding flash.

    You're just trying to show/hide the keyboard, nothing else right?
  • CarolineCaroline Guru
    edited April 2012
    Your plugin is dispatching events, except I haven't worked out why keyboardWillShow won't work.

    Lua code I'm using:
    require "keyinput"
     
    keyinput:setActive(true)
     
    keyinput:addEventListener("keyboardWillShow", function ()
    	print("keyboard shown")
    end)
     
    keyinput:addEventListener("keyPressed", function(event)
    	print("key pressed", event.keyPressed)
    	keyinput:setActive(false)
    end)
     
    keyinput:addEventListener("keyboardWillHide", function()
    	print("keyboard hidden")
    end)
    That just shows the keyboard, then when I press a key, it does the keyPressed bit which hides the keyboard. Both "key pressed" with the letter that was pressed, and "keyboard hidden" are printed out to the Gideros console.

    As for tracking, I tend to use stackdump as you have it and NSLog to print out to the Xcode console.

    (Edited to update code.)
  • CarolineCaroline Guru
    edited April 2012
    I just realised that you uploaded lua code - I didn't see it.

    To format your code like mine, do

    < pre lang="lua" >code here< /pre >

    (without the spaces before and after the "<" and ">")

    I think, and this is where I can't put it into technical words because I am new to Events, that the reason your Lua code didn't work and mine does, is because you are dispatching an event using the KeyInput class, and trying to catch it using the stage class.

    This code altered from the code in the documentation illustrates this:
    ClassA = Core.class(EventDispatcher)
    ClassB = Core.class(EventDispatcher)
     
    local a = ClassA.new()
    local b = ClassB.new()
     
    function onMyEvent()
    	print("myevent")
    end
     
    --The next line works, the uncommented one following doesn't
    --b:addEventListener("myevent", onMyEvent)
    a:addEventListener("myevent", onMyEvent)
     
    b:dispatchEvent(Event.new("myevent"))
    In that example, a is trying to pick up an event that b dispatched, which doesn't work.

  • This is your code that works:
    require "keyinput"
     
    function keyboardWillShow(event)
    	print("Lua keyboardWillShow!\n")
    end
    function keyboardWillHide(event)
    	print("Lua keyboardWillHide!\n")
    end
    function keyPressed(event)
    	print("Lua keyPressed: ", event.keyPressed, "!\n")
    end
     
    keyinput:setActive(true)
     
    keyinput:addEventListener("keyboardWillShow",keyboardWillShow,nil)
    keyinput:addEventListener("keyboardWillHide",keyboardWillHide,nil)
    keyinput:addEventListener("keyPressed",keyPressed,nil)
     
    local timer = Timer.new(4000, 0)
    local function onTimer(event)
    	keyinput:setActive(not keyinput:active())
    end
    timer:addEventListener(Event.TIMER, onTimer)
    timer:start()
    Congratulations :).
  • I tried this out and it's *almost* what I need (thanks @Magnusviri and @Caroline) ... except that it doesn't produce events for the backspace key. I looked through the code and it's got the "deleteBackward" method in there so I called "dispatchEvent" in that method but don't get an event? Any suggestions on how to get an event for the backspace key?
  • Nevermind, I figured it out. I was editing the wrong file ... sigh.
  • I actually decided I needed to add a deleteBackwards event last night! Anyway, if you add it can you send it back to me? I should probably figure out how to use GitHub. It would be awesome if there were a way to have a central repository for all Gideros stuff. For example, is it possible to tag GitHub projects as Gideros projects so that I could go to GitHub and find all Gideros stuff written by the community? Or would it be possible to add something like that to the Gideros site?
  • ndossndoss Guru
    edited April 2012
    I don't know that this is a good solution, but I defined like so:
    - (void)deleteBackward {
        dispatchEvent(L, KEY_PRESSED, FALSE, FALSE, <a href="http://forum.giderosmobile.com/profile/backspace" rel="nofollow">@backspace</a>);
    }
    Then in the lua code I check whether
    keyPressed == "backspace"
  • gorkemgorkem Maintainer
    @magnusviri on github, search for gideros and you'll find many links there. However I'm also trying to find a viable solution, but couldnt find a good method yet :(

    I'd be interested your opinions and suggestions on this.
  • Hm. That works lol. I'm not sure if it's best or not. I have no idea what the performance difference would be to create another event listener vs using the keyPressed event and to checking for "backspace". I would prefer to put something like \h though, I think that is the backspace character.
  • I have no good ideas how to do share code and I'm probably the most backwards person to ask about it. I think GitHub is probably best because it's well known and easy to use, but it's like we need a Gideros subhub, lol, kinda like how Reddit has subreddits. And it would need to be dynamic so that there isn't the constant updating of an external database of stuff. I'll go look at GitHub to see what it can do but it will probably be later today (it's afternoon for me right now).
  • ndossndoss Guru
    edited April 2012
    You can create "organizations" on github. I just created one named "gideros-open-source-contrib" and made "gideros" an owner. I assume email wil have gone to @gorkem or @atilim? Hope that's ok, just playing with it.
  • Open repositories get 300 MB group space. Anyone that's made an owner can do whatever they want with the group (rename it, delete it, etc.). People can be made team members with limited rights.

    Not sure that this is a good solution for what @Magnusviri suggested though.
  • I would prefer to put something like \h though, I think that is the backspace character.
    Tried \h but it complains that it's an unknown escape sequence.
  • gorkemgorkem Maintainer
    Thanks Nathan. No notifications via email but I can see it on the dashboard. Will have a look at this "model", but need more insight and info. @magnusviri - what do you think?
  • @gorkem, I've never had success collaborating with code before, so I have no ideas what to do or what can be done. I can tell you want I would like to see though.

    Right now when I download Gideros it comes with a lot of examples. I would like to same ease of use with community code. I don't want to go to multiple websites to get the code. I'd like to just go to one page and see a list and even be able to download it all.

    However, with the Gideros sample code, if there is a change that needs to be made, you guys make those changes and they are released in the next version. There's no way that you or anyone else could try to keep track of all community changes. It needs to be automated.

    That's why I like the idea of a tag on github or something. I just searched github for "gideros" and found 4 projects.

    https://github.com/search?utf8=✓&q=gideros&type=Everything&repo=&langOverride=&start_value=1

    Anyway, I don't think this is a really serious issue but it would certainly be nice to be able to keep up to date with what other community members do. For example, I know that the TNT Particle engine was just modified. If I got busy doing some other project I probably wouldn't have the time to keep up on the forums and I would have missed that. I also know that it's very time consuming to try to find all of the community code. I have made a strong effort and I still feel like I'm missing something.

    And ok, while I'm at it, it would be nice if we did keep track of community code because I seriously see that the community is filling in a lot of missing pieces of Gideros, for example, this KeyInput plugin. To me, this plugin should simply be a part of Gideros, but how would you manage that? And right now it's iOS only. To add it officially, it should have a comparable Android version, and I can't write it because I don't know the first thing about Android (and I don't really have the time to learn it), but I don't want to put the burden on anyone else to write it. So it probably shouldn't be an official plugin until it has the Android support, which could be a long time, who knows.

    Etc etc etc, anyway. I just think some way of keeping track of all of the community additions would be nice but without putting a burden on anyone to manage it.

    When I get my KeyInput plugin to a state I feel like putting on the internet somewhere, I'll see about putting it on GitHub and using some sort of tag there. Then I'll start to know what GitHub can do.
  • @ndoss, I looked it up and ^h will cause a backspace in a terminal but the escape sequence is \b. The following works.
    - (void)deleteBackward {
        dispatchEvent(L, KEY_PRESSED, FALSE, FALSE, <a href="http://forum.giderosmobile.com/profile/%5Cb" rel="nofollow">@\b</a>);
    }
    function keyPressed(event)
     
    	if event.keyPressed == "\b" then
    		io.write("delete")
    	else
    		io.write(event.keyPressed)
    	end
    end
    Another problem I've found is the keyboard is only portrait. If I run

    application:setOrientation(Application.LANDSCAPE_LEFT)

    Then the Gideros content is landscape but the keyboard isn't. I'm not sure what to do about it. I need to get some other stuff done and I don't plan on showing the keyboard anyway, so I don't need to worry about this for now. I don't have any autorotation stuff set, so maybe that's the problem.
  • MagnusviriMagnusviri Member
    edited April 2012
    Oh, and @Caroline, thanks for figuring that out about adding events to stage vs keyinput. I never would have figured that out and still don't know why it is an issue. I'll have to reread the docs on events.

    Edit to add: the reason I was confused was because I know that touch events go to all sprites. But I think I was looking at it backwards. Event listeners aren't added to the thing that you want to listen, they are added to the thing that generates the event.

    Edit to add more: I also have no idea why Event.ENTER_FRAME can be added to anything.
  • CarolineCaroline Guru
    edited April 2012
    @Magnusviri - I'm also confused about event listeners.

    It seems to me that user-defined events aren't broadcast globally, only to the instance that broadcast it. As in the ClassA/B example I posted above.

    However, to contradict that, the Sprite example on this page:
    http://www.giderosmobile.com/documentation/events.html
    works, so I can only assume that user defined classes are different from Gideros defined classes in some way.

    keyinput received the event, because keyinput was what broadcast it.
  • MagnusviriMagnusviri Member
    edited April 2012
    Ok, I've updated my plugin file (listed above).

    I never got back to github. Instead I got this done lol...

    (Edit to fix typos, and add examples)
    --[[----------------------------------------------------------------------------
     
    iCade
     
    Basic usage:
     
    	require "keyinput"
    	icade = iCade.new(keyinput)
    	keyinput:addEventListener("keyPressed", icade:onKeyPressed, nil)
    	keyinput:setActive(true)
     
    	function onEnterFrame(event)
    		if icade.joystickDirection[1] == 0 and icade.joystickDirection[2] == 0 then 
    			print("Do something already")
    		else
    			print("Now we're movin!")
    		end
    		if icade.buttonsPressed[7] == 1 then 
    			print("Ouch!")
    		end
    	end
    	stage:addEventListener(Event.ENTER_FRAME, onEnterFrame)
     
    Description of values
     
    icade.joystickDirection
    	{-1,-1}	{0,-1}	{1,-1}
    	{-1,0}	{0,0}	{1,0}
    	{-1,1}	{0,1}	{1,1}
     
    icade.buttonsPressed indexes (if 0 then not pressed, if 1 then pressed)
    	1	3	5	7
    	2	4	6	8
     
    	1 = top left button (red button)
    	2 = bottom left button (red button)
    	...
    	8 = bottom right button (white button)
     
    	iMame4all button layout: 1 = coin, 2 = start, 7 = fire, 8 = jump/other
     
    icade.lastChange = {index, upDown}
    	index:
    		1			5	7	9	11
    	4		2		6	8	10	12
    		3
     
    	upDown: 0 = up (released), 1 = down (pressed)
     
    ---------------
     
    Here are various examples of how you can use the data to do things to a sprite.
     
    A thrust example, speed increases as joystick is held constant, e.g. Asteroids.
     
    	thrust = .1 -- each frame adds this much thrust to the speed
    	function onEnterFrame(event)
    		speedX = speedX + icade.joystickDirection[1] * thrust
    		speedY = speedY + icade.joystickDirection[2] * thrust
    		sprite:setPosition(sprite:getX() + speedX, sprite:getY() + speedY)
    	end
    	stage:addEventListener(Event.ENTER_FRAME, onEnterFrame)
     
    Instantaneous acceleration example, stopped or moving at constant speed, e.g. Dig Dug.
     
    	speed = 5
    	function onEnterFrame(event)
    		speedX = icade.joystickDirection[1] * speed
    		speedY = icade.joystickDirection[2] * speed
    		sprite:setPosition(sprite:getX() + speedX, sprite:getY() + speedY)
    	end
    	stage:addEventListener(Event.ENTER_FRAME, onEnterFrame)
     
    A one press moves one space example, e.g. Frogger.
     
    	directions = {{0,-5},{5,0},{0,5},{-5,0}}
    	function moveOneSpot()
    		if icade.lastChange[2] == 1 and icade.lastChange[1] < 5 then
    			local index = icade.lastChange[1]
    			local x = sprite:getX() + directions[index][1]
    			local y = sprite:getY() + directions[index][2]
    			sprite:setPosition(x, y)
    		end
    	end
    	keyinput:addEventListener("keyPressed", moveOneSpot, nil)
     
    Sidescroller, accelerating to a max speed/decelerating when released, and skid/squat
    when pressed down, e.g. Mario Bros.  (Note, all of these rules go out the window when
    in the air either from a jump, fired from a canon, etc.,.)
     
    	thrustX = .05
    	maxSpeedX = 3
    	speedX = 0
    	function onEnterFrame(event)
    		local thrustX = thrustX
    		local maxSpeedX = maxSpeedX
    		if icade.joystickDirection[2] > 0 then
    			if icade.joystickDirection[1] == 0 then
    				-- skid acceleration
    				thrustX = thrustX * 2
    			else
    				-- squat acceleration
    				thrustX = thrustX * .5
    			end
    			-- squat speed
    			maxSpeedX = maxSpeedX * .5
    		end
     
    		local desiredSpeedX = icade.joystickDirection[1] * maxSpeedX
     
    		if desiredSpeedX == 0 and math.abs(speedX) < thrustX then
    			speedX = 0 -- zero speed if it's really small
    		else
    			if speedX < desiredSpeedX then
    				speedX = speedX + thrustX
    			elseif speedX > desiredSpeedX then
    				speedX = speedX - thrustX
    			end
    		end
    		sprite:setPosition(sprite:getX() + speedX, sprite:getY())
    	end
    	stage:addEventListener(Event.ENTER_FRAME, onEnterFrame)
     
     
    ]]
     
    iCade = Core.class()
    function iCade:init(keyinput)
    	self.keyinput = keyinput
    	self.keys = {
    		w={1,1}, d={2,2}, x={3,4}, a={4,8},
    		e={1,-1}, c={2,-2}, z={3,-4}, q={4,-8},
    		y={5,1}, h={6,2}, u={7,4}, j={8,8},
    		i={9,16}, k={10,32}, o={11,64}, l={12,128},
    		t={5,-1}, r={6,-2}, f={7,-4}, n={8,-8},
    		m={9,-16}, p={10,-32}, g={11,-64}, v={12,-128},
    	}
    	self.joystickLookups = {
    		["0"]={0,0}, ["1"]={0,-1}, ["3"]={1,-1}, ["2"]={1,0}, ["6"]={1,1},
    		["4"]={0,1}, ["12"]={-1,1}, ["8"]={-1,0}, ["9"]={-1,-1}
    	}
    	self:reset()
    end
    function iCade:reset()
    	self.lastChange = {}
    	self.joystickDirection = {0,0}
    	self.joystickMask = 0
    	self.joystickButtonsPressed = { 0,0,0,0 }
    	self.buttonsMask = 0
    	self.buttonsPressed = { 0,0,0,0,0,0,0,0 }
    end
    function iCade:onKeyPressed(event)
    	local key = event.keyPressed
    	local currentKey = self.keys[key]
    	if currentKey ~= nil then
    		local index = currentKey[1]
    		local mask = currentKey[2]
    		local upDown = ( mask > 0 ) and 1 or 0
    		self.lastChange = {index, upDown}
    		if index < 5 then
    			self.joystickButtonsPressed[index] = upDown
    			self.joystickMask = self.joystickMask + mask
    			self.joystickDirection = self.joystickLookups[tostring(self.joystickMask)]
    			if self.joystickDirection == nil then
    				io.write( "Joystick error, resetting.\n" )
    				self:reset()
    			end
    		else
    			self.buttonsPressed[index-4] = upDown
    			self.buttonsMask = self.buttonsMask + mask
    		end
    		return 1
    	else
    		return 0
    	end
    end
  • @gorkem,
    Ok, I was thinking of putting this online somewhere central and so I looked at github and do they charge to host files? I thought they were more like sourceforge (free afaik). Anyway, if they charge, then that is obviously out (for me anyway). Otherwise I'll just put this on my webpage I guess. Maybe we can have a list on the Gideros wiki with links to all of the community code webpages? I don't know.
  • gorkemgorkem Maintainer
    No, github doesnt charge. Just send your file to github, and @ndoss or I will be putting it in a group.

    Your idea to provide a list of plugins & community code is nice. I'll take care of this in the near future - or better - if there's someone who can open up a page in wiki to fill in, I can help too
  • This is easier than following all those terminal commands:

    http://mac.github.com/
  • MichalMichal Member
    edited April 2012
    Hello, I guess I am just looking for this kind of plugin for android, as I am using HP TouchPad with android 4, and this tables has no physical buttons (except for "home"), so the only way to put some text in is to either use native onscreen keyboard (which needs a plugin I guess) or implement my own keyboard within gideros (doable, but takes time). Any other ideas about entering text on android?
    If native widgets would be available (ie textfield), would the android OS automatically show the keyboard if the textfield widget would get focused?

    EIDT: I may go with virtual keyboard route, this could be easier and not that bad (and would work cross-platform). I am attaching a "virtual keyboard" file to emulate hardware buttons. Usage: just add this file to Example/Hardware/Keyboard and click the red box to test. Then it is easy to test it with Gideros Player on a desktop. I will make it full-blown keyboard some day, so there will be an alternative way to type.
    txt
    txt
    vkeyboard.lua.txt
    2K
  • @Michal, I know nothing about Android so I can't help with your question.

    I have been thinking about what to do if someone did something similar and I don't think there should be an iOS version and an Android version that have different interfaces. So whatever is done, I think they should be made identical if possible and if they are different then I should name mine ioskeyinput or something.

    When I say they should be identical, I mean that this should work for both:
    require "keyinput"
    function onKeyPressed(event)
    	io.write(event.keyPressed)
    end
    keyinput:addEventListener("keyPressed", onKeyPressed, nil)
    keyinput:setActive(true)
Sign In or Register to comment.