forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

controller button presses

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Lua Player Development
View previous topic :: View next topic  
Author Message
Arwin



Joined: 12 Jul 2005
Posts: 426

PostPosted: Sun Aug 21, 2005 12:55 am    Post subject: controller button presses Reply with quote

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:
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);
}
Back to top
View user's profile Send private message
Dark Killer



Joined: 25 Jan 2005
Posts: 32

PostPosted: Sun Aug 21, 2005 2:57 am    Post subject: Reply with quote

you could just do:

Code:

pad=controls:read()
if pad:cross() then
if pad:circle() then
--whatever
end
end


when O and X are pressed simultaneously, "whatever" will excute.
Back to top
View user's profile Send private message AIM Address
Arwin



Joined: 12 Jul 2005
Posts: 426

PostPosted: Sun Aug 21, 2005 3:05 am    Post subject: Reply with quote

Dark Killer wrote:
you could just do:

Code:

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. ;)
Back to top
View user's profile Send private message
Shine



Joined: 03 Dec 2004
Posts: 731
Location: Germany

PostPosted: Sun Aug 21, 2005 7:28 am    Post subject: Reply with quote

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:

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 :-)
Back to top
View user's profile Send private message
Arwin



Joined: 12 Jul 2005
Posts: 426

PostPosted: Sun Aug 21, 2005 8:38 am    Post subject: Reply with quote

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).

Quote:
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. :)

Quote:
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'. ;)
Back to top
View user's profile Send private message
Arwin



Joined: 12 Jul 2005
Posts: 426

PostPosted: Mon Aug 22, 2005 7:08 pm    Post subject: Reply with quote

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. :)
Back to top
View user's profile Send private message
Arwin



Joined: 12 Jul 2005
Posts: 426

PostPosted: Mon Aug 22, 2005 11:33 pm    Post subject: Reply with quote

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

Code:
-- 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?
Back to top
View user's profile Send private message
Arwin



Joined: 12 Jul 2005
Posts: 426

PostPosted: Fri Aug 26, 2005 12:35 am    Post subject: Reply with quote

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


Code:

   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:

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 ...
Back to top
View user's profile Send private message
Shine



Joined: 03 Dec 2004
Posts: 731
Location: Germany

PostPosted: Fri Aug 26, 2005 1:24 am    Post subject: Reply with quote

Arwin wrote:

Code:

   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:

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(xb) < string.len(yb) do xb = "0" .. xb end
   while string.len(yb) < string.len(xb) do yb = "0" .. yb end
   return xb, yb
end
   
function binaryAnd(x, y)
   xb, yb = toSameLengthBinary(x, y)
   result = ""
   for i = 1, string.len(xb) do
      if string.sub(xb, i, i) == "1" and string.sub(yb, i, i) == "1" then
         result = result .. "1"
      else
         result = result .. "0"
      end
   end
   return toInt(result)
end

function binaryOr(x, y)
   xb, yb = toSameLengthBinary(x, y)
   result = ""
   for i = 1, string.len(xb) do
      if string.sub(xb, i, i) == "1" or string.sub(yb, i, i) == "1" then
         result = result .. "1"
      else
         result = result .. "0"
      end
   end
   return toInt(result)
end

function binaryXor(x, y)
   xb, yb = toSameLengthBinary(x, y)
   result = ""
   for i = 1, string.len(xb) do
      if string.sub(xb, i, i) ~= string.sub(yb, i, i) then
         result = result .. "1"
      else
         result = result .. "0"
      end
   end
   return toInt(result)
end
Back to top
View user's profile Send private message
Arwin



Joined: 12 Jul 2005
Posts: 426

PostPosted: Fri Aug 26, 2005 1:39 am    Post subject: Reply with quote

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.

Quote:
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 :).
Back to top
View user's profile Send private message
Shine



Joined: 03 Dec 2004
Posts: 731
Location: Germany

PostPosted: Sun Sep 11, 2005 2:31 am    Post subject: Reply with quote

Arwin wrote:
Quote:
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.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Lua Player Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group