controller button presses

Discuss using and improving Lua and the Lua Player specific to the PSP.

Moderators: Shine, Insert_witty_name

Post Reply
Arwin
Posts: 426
Joined: Tue Jul 12, 2005 7:00 pm

controller button presses

Post by Arwin »

Hi Shine et al,

As we discussed privately would be the best approach, I was considering converting the p-sprint c source to a lua example program. But the first problem I run into is that I can't quite see from the reference how to look at combined button presses. If I look at 'controller' after a read, is that basically the same as looking at pad.Buttons in c? Can I do binary comparisons on it?

For example, if I have this function that checks for combined button presses, how would I do this in Lua? (I bolded the most important binary comparisons)

Code: Select all

int p_spGetControlKeys(unsigned int butpress1, unsigned int butpress2)
{
	/* determines whether or not a two-button combination
	contains a shift option and returns the shift option
	a.k.a. modifiers if true */

	/* to do: allow for combinations of control keys */

	unsigned int mainbut;
	unsigned int shiftbut;
	int shift = 0;
	int control = 0;
	int alt = 0;
	//int num = 0;
	int special = 0;
	
	[b]mainbut = butpress1 & butpress2;[/b]
	
	[b]if ((!(mainbut==butpress2))&(!(butpress2==0)))[/b]
	/* contains shift-option */
	{
		/* TO DO: parse multiple button combos */
		/* get shift value */
		[b]shiftbut = butpress2 ^ butpress1;[/b]
		switch (shiftbut)
		{
		case PSP_CTRL_DOWN:
			special=8;
			break;
		case PSP_CTRL_CROSS:
			special=8;
			break;

		case PSP_CTRL_RIGHT:
			alt=4;
			break;
		case PSP_CTRL_CIRCLE:
			alt=4;
			break;
		case PSP_CTRL_SQUARE:
			control=1;
			break;
		case PSP_CTRL_LEFT:
			control=1;
			break;
		case PSP_CTRL_TRIANGLE:
			shift=2;
			break;
		case PSP_CTRL_UP:
			shift=2;
			break;
		}
	}
	return (alt+control+shift+special+g_shift_state);
}
Dark Killer
Posts: 32
Joined: Tue Jan 25, 2005 3:10 am

Post by Dark Killer »

you could just do:

Code: Select all

pad=controls:read()
if pad:cross() then
if pad:circle() then
--whatever
end
end
when O and X are pressed simultaneously, "whatever" will excute.
Arwin
Posts: 426
Joined: Tue Jul 12, 2005 7:00 pm

Post by Arwin »

Dark Killer wrote:you could just do:

Code: Select all

pad=controls:read()
if pad:cross() then
if pad:circle() then
--whatever
end
end
when O and X are pressed simultaneously, "whatever" will excute.
I know, but that would end up with a lot more code ... ! I'd have to check for all combinations of eight buttons! Shine promised me my psprint code would be shorter if I rewrote it in Lua. ;)
Shine
Posts: 728
Joined: Fri Dec 03, 2004 12:10 pm
Location: Germany

Post by Shine »

There are no binary relational operators in the Lua language, but you can write your programs in such a way that you don't need it.

I think your system is a bit complicated, but I'll try it. Let me summarize it: Most sequences are combos of the form: "key press 1", "key release 1", "key press 2", "key release 2" (where "1" and "2" are arbitrary keys, like cross, circle, up etc.). Some special characters, like space and backspace, are only a sequence of "key press 1", "key release 1". The shift modifier is the up-key, which has to be pressed while holding the first key of a combo. For example the sequence "triangle press", "triangle release", "square press", "square release" produces a "r", but the sequence "triangle press", "up press", "up release", "triangle release", "square press", "square release" produces a "R" (the squence "triangle press", "up press", "triangle release", "up release", "square press", "square release" is valid, too, but the sequence "triangle press", "up press", "triangle release", "square press", "square release", "up release" produces nothing, until you add the sequence "square press", "square release", but this looks like unintended behaviour in your program (version 0.50a)).

Some special sequences are used for switching the group. The start group is group 1, where the above sequences are used. You can switch to group 2 with the sequence "up press", "cross press", "cross release", "up release" (there is a bug, I think: when I switch the group, the last directional key is interpreted as the first key press/release sequence of the new sequence instead of starting with an empty sequence, e.g. if I'm in group 2, the sequence "up press", "cross press", "cross release", "up release", "triangle press", "triangle release" produces a "v", which normally is produces with the sequence "up press", "up release", "triangle press", "triangle release"). In group 2 for example the sequence "right press", "right release", "square press", "square release" produces a "k" (while in group 1 the same sequence produces a "m").

Ok, how can you implement it in Lua? I think the easiest way, and to avoid unintended behaviour, would be to list all sequences and which action should be executed for it. One nice feature of Lua is, that you can use functions as values, this simplifies the code:

Code: Select all

black = Color.new(0, 0, 0);
white = Color.new(255, 255, 255);

-- group 1 tree (with underscore are key release events)
group1 = {
	cross = { char = " ", code = 32 },
	triangle = {
		_triangle = { square = { _square = { char = "r", code = 1 }}},
		up = { _up = { _triangle = { square = { _square = { char = "R", code = 2 }}}}}
	},
	up = { cross = { _cross = { _up = { group = "group2" }}}}
}

-- group 2 tree
group2 = {
	right = {
		_right = { square = { _square = { char = "k", code = 3 }}},
	},
	up = { cross = { _cross = { _up = { group = "group1" }}}}
}

-- mapping from control name to control function
controlFunctions = {
	up = Controls.up, down = Controls.down, left = Controls.left, right = Controls.right,
	cross = Controls.cross, circle = Controls.circle, square = Controls.square, triangle = Controls.triangle
}

-- current pressed controls, initialized to false
pressedControls = {}
for name, _ in controlFunctions do
	pressedControls[name] = false
end

-- current selected group
currentGroup = group1

-- current root within current selected group
currentRoot = currentGroup

-- current text position
x = 0

-- create an empty white image
canvas = Image.createEmpty(480, 272)
canvas:clear(white)

-- main loop
while true do
	-- read button states
	pad = Controls.read()
	
	-- check every button
	for name, testFunction in controlFunctions do
		selected = currentRoot
		check = false

		-- check one button
		if testFunction(pad) then
			if not pressedControls[name] then
				-- if key press event, go down the group tree
				selected = selected[name]
				pressedControls[name] = true
				check = true
			end
		else
			if pressedControls[name] then
				-- if key release event, go down the group tree
				selected = selected["_" .. name]
				pressedControls[name] = false
				check = true
			end
		end
		
		-- if some button changed its state, check if group tree leaf is reached
		if check then
			if selected then
				if selected.char then
					-- char leaf reached, print it
					canvas:print(x, 0, selected.char)
					x = x + 8
					currentRoot = currentGroup
				elseif selected.group then
					-- group leaf reached, change group
					currentGroup = _G[selected.group]
					currentRoot = currentGroup
				else
					-- not a leaf, set current root to next group branch
					currentRoot = selected
				end
			else
				-- sequence not found, start again at root of current group
				currentRoot = currentGroup
			end
		end
	end
	
	-- update screen
	screen:blit(0, 0, canvas, 0, 0, canvas:width(), canvas:height(), false)
	screen.waitVblankStart()
	screen.flip()
	
	-- program end, if start was pressed
	if pad:start() then break end
end
With this code the sequence "triangle press", "triangle release", "square press", "square release", "triangle press", "up press", "up release", "triangle release", "square press", "square release", "up press", "cross press", "cross release", "up release", "right press", "right release", "square press", "square release" produces the string "rRk".

Of course, you don't have to write the low-level group trees yourself, use some more Lua code to generate it from higher-level descriptions of your protocol. And you can write some tree dumping code for debugging.

An interesting idea could be to use the different time when to press and release the key, for example "cross press", "down press", "cross release", "down release" could have a different meaning than "cross press", "down press", "down release", "cross release". This enhances the number of possible inputs with fewer keys, and makes the system even more unusable :-)
Arwin
Posts: 426
Joined: Tue Jul 12, 2005 7:00 pm

Post by Arwin »

Shine wrote:There are no binary relational operators in the Lua language, but you can write your programs in such a way that you don't need it.
I'm impressed, but it also gave me a bit of a headache. Who'd have thought something'd look more complicated in a higher level language. ;) And I started on C only a few weeks ago (ok, but I cheated a bit by already knowing JavaScript).
there is a bug, I think: when I switch the group, the last directional key is interpreted as the first key press/release sequence of the new sequence instead of starting with an empty sequence, e.g. if I'm in group 2, the sequence "up press", "cross press", "cross release", "up release", "triangle press", "triangle release" produces a "v", which normally is produces with the sequence "up press", "up release", "triangle press", "triangle release").
Hey, you're on to something there. I should reset the g_prev_btn variable
after a group select. Thanks for spotting that. :)
the system even more unusable :-)
Eh ... thanks. :D I have to say though that after only a week of (not even a lot of) typing with p-sprint, I achieve pretty high speeds already, which I can definitely not match with any other system. The keys are pretty easy to memorise once you get the logic behind it (a trade-off between two basic principles - keys that go together often are near each other and keys that are more common are on the easier combinations).

I have to think over your Lua conversion suggestion a little more. It looks possible to do it that way. p-sprint can also properly pile on modifiers, so I could make a CTRL-SHIFT-V for instance by holding up and pressing triangle, then pressing square, then letting all of them go and press up (notice how a key-press in p-sprint is always a maximum of one keypress more than on a regular keyboard).

But thanks on giving me a good starting point for trying it in Lua. The 'k' you get in the other group by the way is a remnant of copying bits of data for the first group. It should normally be a non-allocated key-slot. There is a few bits of cleaning up to do there, but I haven't given it priority just yet as it doesn't hamper basic functionality - it just means that there is an extra (and pretty useless) way of typing 'k'. ;)
Arwin
Posts: 426
Joined: Tue Jul 12, 2005 7:00 pm

Post by Arwin »

EDIT: ok, I think I should try to make a basic version of p-sprint with your example code. I.e., fill in the rest of the tree. It'll probably give me a headache, but it seems worth it. The real strength for p-sprint would be universal acceptance, after all, because then the (little) effort of memorising the keys easily pays off, and keyboard related development time is reduced for all coders and porters.

Also, if it takes much longer for people to release a text editor, I'll do my best to make something that can edit lua scripts using p-sprint.

EDIT: hmm, in p-sprint it doesn't matter for shifts or controls which button you release first, but in the lua code up it does.

I think for compatibility reasons I should probably try to stick to the original code structure more closely, if possible. If that means I have to make binary comparisons by emulating and and or functions on arrays then I guess that's what I'll have to do ... :S

But I don't quite have Lua in my system yet - this making collections of functions is a new concept to me, and although I understand it, it'll take me a while to see how it works nicely. Can I, for instance, use it directly to make an array function for the buttons?


D$mn, I really miss binary operations. :)
Arwin
Posts: 426
Joined: Tue Jul 12, 2005 7:00 pm

Post by Arwin »

Ok, so if I understand it correctly, this is how I could expand the tree:

Code: Select all

-- group 0 tree (with underscore are key release events) 
group0 = { 
   cross = { char = " ", code = 32, modifiers = 0, name="Space" }, 
   down = { char = "", code = 8, modifiers = 0, name="Backspace" }, 
   triangle = { 
      --right side
      _triangle = { square = { _square = { char = "r", code = 82, modifiers = 0, name = "" }}}, 
      up = { _up = { _triangle = { square = { _square = { char = "R", code = 82, modifiers = 2, name="" }}}}} 
      _triangle = { triangle = { _triangle = { char = "e", code = 69, modifiers = 0, name = "" }}}, 
      up = { _up = { _triangle = { triangle = { _triangle = { char = "E", code = 69, modifiers = 2, name="" }}}}} 
      _triangle = { circle = { _circle = { char = "a", code = 65, modifiers = 0, name = "" }}}, 
      up = { _up = { _triangle = { circle= { _cirlce = { char = "A", code = 65, modifiers = 2, name="" }}}}} 
      --left side
      _triangle = { up = { _up = { char = "w", code = 88, modifiers = 0, name = "" }}}, 
      up = { _up = { _triangle = { up = { _up = { char = "W", code = 88, modifiers = 2, name="" }}}}} 

   }, 
   up = { cross = { _cross = { _up = { group = "group2" }}}} 
   left = { cross = { _cross = { _up = { group = "group1" }}}} 
   right = { cross = { _cross = { _up = { group = "group3" }}}} 
} 

-- group 1 tree 
group1 = { 
   right = { 
      _right = { square = { _square = { char = "", code = 121, modifiers = 121, name = "F1" }}}, 
   }, 
   up = { cross = { _cross = { _up = { group = "group0" }}}} 
} 

-- group 2 tree 
group2 = { 
   right = { 
      _right = { square = { _square = { char = "+", code = 107, modifiers = 0, name = "+ (NUM)"}}}, 
   }, 
   up = { cross = { _cross = { _up = { group = "group0" }}}} 
} 
Correct? If so, I will expand this to the basic keys, and for now only implement the basic, most common combinations and keys - Alphabet, Numbers, the basic F1-F12, Insert, Home, etc. keys, shift values for those keys that have a meaning assigned to this normally (Alphabet) and a few common CTRL values (like CTRL-X, CTRL-V, CTRL-C).

And can I also do something like char = 22, if the key has a non-printable ASCII value?
Arwin
Posts: 426
Joined: Tue Jul 12, 2005 7:00 pm

Post by Arwin »

Shine, there is a problem with your routine, unfortunately. I finally got a charged psp again and tested it, and this causes problems:

Code: Select all

   triangle = { 
      _triangle = { square = { _square = { char = "r", code = 82, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { square = { _square = { char = "R", code = 82, modifiers = 2, nam="" }}}}},
      _triangle = { triangle = { _triangle = { char = "e", code = 69, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { triangle = { _triangle = { char = "E", code = 69, modifiers = 2, nam="" }}}}}, 
      _triangle = { circle = { _circle = { char = "a", code = 65, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { circle= { _circle = { char = "A", code = 65, modifiers = 2, nam="" }}}}}
   }
Here, only the last two, _traingle that leads to "a" and up that leads to "A" will be read, the rest is ignored. Do you know what is happening exactly? Is the problem perhaps that the items in the collection cannot have the same name when on the same level, because they are also their own index?

Here is the current full version of my script. As you can see I've also isolated the R in a separate Triangle branch to see if that gets ignored too, and it does.

Code: Select all

black = Color.new(0, 0, 0); 
white = Color.new(255, 255, 255); 

-- group 0 tree (with underscore are key release events) 
group0 = { 
   cross = { char = "*", code = 32, modifiers = 0, nam="Space" }, 
   down = { char = "", code = 8, modifiers = 0, nam="Backspace" }, 
   triangle = { 
      _triangle = { square = { _square = { char = "r", code = 82, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { square = { _square = { char = "R", code = 82, modifiers = 2, nam="" }}}}}
   },
   triangle = { 
      _triangle = { up = { _up = { char = "w", code = 88, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { up = { _up = { char = "W", code = 88, modifiers = 2, nam="" }}}}},
      _triangle = { square = { _square = { char = "r", code = 82, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { square = { _square = { char = "R", code = 82, modifiers = 2, nam="" }}}}},
      _triangle = { triangle = { _triangle = { char = "e", code = 69, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { triangle = { _triangle = { char = "E", code = 69, modifiers = 2, nam="" }}}}}, 
      _triangle = { circle = { _circle = { char = "a", code = 65, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { circle= { _circle = { char = "A", code = 65, modifiers = 2, nam="" }}}}}
   }, 
   up = { cross = { _cross = { _up = { group = "group2" }}}},
   left = { cross = { _cross = { _up = { group = "group1" }}}}, 
   right = { cross = { _cross = { _up = { group = "group3" }}}} 
} 

-- group 1 tree 
group1 = { 
   right = { 
      _right = { square = { _square = { char = "", code = 121, modifiers = 121, nam = "F1" }}}, 
   }, 
   up = { cross = { _cross = { _up = { group = "group0" }}}} 
} 

-- group 2 tree 
group2 = { 
   right = { 
      _right = { square = { _square = { char = "+", code = 107, modifiers = 0, nam = "+ (NUM)"}}}, 
   }, 
   up = { cross = { _cross = { _up = { group = "group0" }}}} 
} 

-- mapping from control name to control function 
controlFunctions = { 
   up = Controls.up, down = Controls.down, left = Controls.left, right = Controls.right, 
   cross = Controls.cross, circle = Controls.circle, square = Controls.square, triangle = Controls.triangle 
} 

-- current pressed controls, initialized to false 
pressedControls = {} 
for name, _ in controlFunctions do 
   pressedControls[name] = false 
end 

-- current selected group 
currentGroup = group0 

-- current root within current selected group 
currentRoot = currentGroup 

-- current text position 
x = 8 

-- create an empty white image 
canvas = Image.createEmpty(480, 272) 
canvas:clear(white) 
canvas:print(x, 8, "_") 

-- main loop 
while true do 
   -- read button states 
   pad = Controls.read() 
    
   -- check every button 
   for name, testFunction in controlFunctions do 
      selected = currentRoot 
      check = false 

      -- check one button 
      if testFunction(pad) then 
         if not pressedControls[name] then 
            -- if key press event, go down the group tree 
            selected = selected[name] 
            pressedControls[name] = true 
            check = true 
         end 
      else 
         if pressedControls[name] then 
            -- if key release event, go down the group tree 
            selected = selected["_" .. name] 
            pressedControls[name] = false 
            check = true 
         end 
      end 
       
      -- if some button changed its state, check if group tree leaf is reached 
      if check then 
         if selected then 
	   
            if selected.char then 
               -- char leaf reached, print it 
               canvas:print(x, 8, selected.char) 
               x = x + 8 
	       canvas:print(x, 8, "_") 
               currentRoot = currentGroup 
            elseif selected.group then 
               -- group leaf reached, change group 
               currentGroup = _G[selected.group] 
               currentRoot = currentGroup 
            else 
               -- not a leaf, set current root to next group branch 
               currentRoot = selected 
            end 
         else 
            -- sequence not found, start again at root of current group 
            currentRoot = currentGroup 
         end 
      end 
   end 
    
   -- update screen 
   screen:blit(0, 0, canvas, 0, 0, canvas:width(), canvas:height(), false) 
   screen.waitVblankStart() 
   screen.flip() 
    
   -- program end, if start was pressed 
   if pad:start() then break end 
end 
Oh, and drawn keys are superimposed, so I guess I'll have to write my own clearing routine for one character ...
Shine
Posts: 728
Joined: Fri Dec 03, 2004 12:10 pm
Location: Germany

Post by Shine »

Arwin wrote:

Code: Select all

   triangle = { 
      _triangle = { square = { _square = { char = "r", code = 82, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { square = { _square = { char = "R", code = 82, modifiers = 2, nam="" }}}}},
      _triangle = { triangle = { _triangle = { char = "e", code = 69, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { triangle = { _triangle = { char = "E", code = 69, modifiers = 2, nam="" }}}}}, 
      _triangle = { circle = { _circle = { char = "a", code = 65, modifiers = 0, nam = "" }}}, 
      up = { _up = { _triangle = { circle= { _circle = { char = "A", code = 65, modifiers = 2, nam="" }}}}}
   }
Here, only the last two, _traingle that leads to "a" and up that leads to "A" will be read, the rest is ignored. Do you know what is happening exactly? Is the problem perhaps that the items in the collection cannot have the same name when on the same level, because they are also their own index?
Yes. But it is a tree structure, if you have { a = { b }, a = { c } } you really mean { a = { b, c } }.

But you don't need it any more, when I release the next version of Lua Player, because then you'll have binary operators, which is easier instead of writing it like this in Lua:

Code: Select all

function toBinary(int)
	result = ""
	while int > 0 do
		if math.mod(int, 2) == 1 then
			result = "1" .. result
		else
			result = "0" .. result
		end
		int = math.floor(int / 2)
	end
	if result == "" then result = "0" end
	return result
end

function toInt(binary)
	result = 0
	pow = 1
	for i = string.len(binary), 0, -1 do
		if string.sub(binary, i, i) == "1" then result = result + pow end
		pow = pow * 2
	end
	return result
end

function toSameLengthBinary(x, y)
	xb = toBinary(x)
	yb = toBinary(y)
	while string.len&#40;xb&#41; < string.len&#40;yb&#41; do xb = "0" .. xb end
	while string.len&#40;yb&#41; < string.len&#40;xb&#41; do yb = "0" .. yb end
	return xb, yb
end
	
function binaryAnd&#40;x, y&#41;
	xb, yb = toSameLengthBinary&#40;x, y&#41;
	result = ""
	for i = 1, string.len&#40;xb&#41; do
		if string.sub&#40;xb, i, i&#41; == "1" and string.sub&#40;yb, i, i&#41; == "1" then
			result = result .. "1"
		else
			result = result .. "0"
		end
	end
	return toInt&#40;result&#41;
end

function binaryOr&#40;x, y&#41;
	xb, yb = toSameLengthBinary&#40;x, y&#41;
	result = ""
	for i = 1, string.len&#40;xb&#41; do
		if string.sub&#40;xb, i, i&#41; == "1" or string.sub&#40;yb, i, i&#41; == "1" then
			result = result .. "1"
		else
			result = result .. "0"
		end
	end
	return toInt&#40;result&#41;
end

function binaryXor&#40;x, y&#41;
	xb, yb = toSameLengthBinary&#40;x, y&#41;
	result = ""
	for i = 1, string.len&#40;xb&#41; do
		if string.sub&#40;xb, i, i&#41; ~= string.sub&#40;yb, i, i&#41; then
			result = result .. "1"
		else
			result = result .. "0"
		end
	end
	return toInt&#40;result&#41;
end
Arwin
Posts: 426
Joined: Tue Jul 12, 2005 7:00 pm

Post by Arwin »

Shine wrote: Yes. But it is a tree structure, if you have { a = { b }, a = { c } } you really mean { a = { b, c } }.
Great, then I understood it correctly.
But you don't need it any more, when I release the next version of Lua Player, because then you'll have binary operators, which is easier instead of writing it like this in Lua:
Yes, having to go through all that hurts! Can't wait :).
Shine
Posts: 728
Joined: Fri Dec 03, 2004 12:10 pm
Location: Germany

Post by Shine »

Arwin wrote:
But you don't need it any more, when I release the next version of Lua Player, because then you'll have binary operators, which is easier instead of writing it like this in Lua:
Yes, having to go through all that hurts! Can't wait :).
New release is ready (see functions.txt for details of the Controls changes), now you can port your keyboard program without too much changes.
Post Reply