[Solved] Continuous button press

Hey All,

How can continuous button press events be received when the user press and hold a button on the remote? I am getting only one roUniversalControlEvent when the button is pressed and hold for some time.

I have a custom grid, and I would like to scroll through the grid continuously by holding the up/down button in the remote, without multiple button press.

Thanks.

There is a separate code for button release, so you just check whether they pressed the button down and didn’t let go yet.

Here’s my note of button codes:
Back 0,100; Up 2,102; Down 3,103; Left 4,104; Right 5,105; Select(OK) 6,106; Instant Replay 7,107; Rewind 8,108; Fast Forward 9,109’ Info 10,110; Backspace (not on remote) 11,111; Play 13,113; A 17,117; B 18,118

The first number is the press code, the second number is the release code (always 100 more)

Awesome. Thanks KOMAG!

Simple Grid demos three styles
1 - Using Associative Arrays as objects
2- Scrolling using region offsetting
3- Monitoring user button presses to navigate the grid

If you are just interested in the navigation then go to
GridModule:EventLoop Use arrow fw and rw keys to navigate

Quickly put together but adequately demonstrates one method

Sub Main() 

   l_ds = CreateObject( "roDeviceInfo" ).GetDisplaySize()
   l_dw = l_ds.w
   l_dh = l_ds.h
   
   ' Application Globals ( you can create a global object which is a better solution )
   m.BkgColor = &hFFFFFFFF
   m.Screen = CreateObject( "roScreen", True, l_dw, l_dh )
   m.Port   = CreateObject( "roMessagePort" )
   m.Screen.SetMessagePort( m.Port )
  
   ' Create an instance of the grid module
   l_module = NewGridModule()
   if l_module.Create()
      l_module.EventLoop()
   else
      print "NewGridModule Failed"
   end if
   
   l_module.Release()
   
End Sub

'***************************** Grid Module Object ***********************************************************************

Function NewGridModule() As Object

   return {
      
      ' Reference to globals declared in main
      ' They must be referenced during creation, as the m pointer can only point to the AA's members
      ' once created.  However, Screen: and Port: are members referencing the globals so they can be safely used
      ' once assigned
      Screen: m.Screen
      Port: m.Port
      
      ' Member Variable - our simplegird and a timer
      SimpleGrid: Invalid
      Timer: Invalid
      
      ' Member Functions
      Create: grid_module_create
      Initialize: grid_module_initialize
      Release: grid_module_release
      EventLoop: grid_module_event_loop
      
   }
End Function

' All objects work the same, create, initialize, release
Function grid_module_create() As Boolean
   if not m.Initialize() 
      m.Release()
      return False
   end if
   return True
End Function

Function grid_module_initialize() As Boolean

   m.SimpleGrid = NewSimpleGrid()
   if not m.SimpleGrid.Create() then return False
   
   m.Timer = CreateObject( "roTimeSpan" )
   
   l_ds = CreateObject( "roDeviceInfo" ).GetDisplaySize()
   l_dw = l_ds.w
   l_dh = l_ds.h
   
   l_x = l_dw / 2 - m.SimpleGrid.GetWidth()  / 2
   l_y = l_dh / 2 - m.SimpleGrid.GetHeight() / 2
   m.SimpleGrid.MoveTo( l_x, l_y )
  
   return True
End Function

Function grid_module_release() As Void
   if m.SimpleGrid <> Invalid
      m.SimpleGrid.Release()
      m.SimpleGrid = Invalid
   end if
   
   m.Timer = Invalid
   
End Function

Function grid_module_event_loop() As Boolean

   if m.SimpleGrid = Invalid then return False
   
   l_kp_BK = 0
   l_kp_LT = 4
   l_kp_RT = 5
   l_kp_RW = 8
   l_kp_FF = 9
   
   ' Frame rates and scroll pause
   l_fastRate = 4
   l_normalRate = 16
   l_scrollPause = 175
   ' lastkey saves the last button pressed until the button-up event
   l_lastKey = -1
   l_index = -1
   
   m.Timer.Mark()
   
   while( True )
      ' Cannot use wait to do this must be always polling
      l_msg = m.Port.GetMessage() 
      ' The user pressed a button
      if type( l_msg ) = "roUniversalControlEvent"
      
         l_index = l_msg.GetInt()
         l_lastKey = l_index
         
         if l_index = l_kp_LT 
         
            m.SimpleGrid.ScrollLeft( l_normalRate )
            
         else if l_index = l_kp_RT 
         
            m.SimpleGrid.ScrollRight( l_normalRate )
            
         else if l_index = l_kp_RW
         
            m.SimpleGrid.ScrollLeft( l_fastRate )
            
          else if l_index = l_kp_FF
         
            m.SimpleGrid.ScrollRight(  l_fastRate )
         
         else if l_index = l_kp_BK
    
            exit while
         ' The user released a button, set lastkey to -1
         else if l_index >= 100
         
            l_lastKey = -1
            
         end if
         ' Reset the timer regardless of what comes in
         m.Timer.Mark()
       ' user  is holding down button because l_lastkey <> -1
       ' Once the initial pause elapsed then check last key
       else if ( l_lastKey >= 0 and m.Timer.TotalMilliseconds() >= l_scrollPause )
       
          if l_lastKey = l_kp_LT
             m.SimpleGrid.ScrollLeft( l_normalRate )
          else if l_lastKey = l_kp_RT
             m.SimpleGrid.ScrollRight( l_normalRate )
          else if l_lastKey = l_kp_RW
             m.SimpleGrid.ScrollLeft( l_fastRate )
          else if l_lastKey = l_kp_FF
            m.SimpleGrid.ScrollRight( l_fastRate )
          end if
          
       end if
       
   end while
   
   return True
End Function

'***************************************************************************************************************

'********************* Simple Grid Object **************************************************************************
' This grid uses region offsetting to intrinsically scroll the grid
' It also demontrates how to create simple objects using an AA
Function NewSimpleGrid() As Object

   return {
    ' Reference member to global variable.  Must be assigned here, once created the m pointer only points to members
    ' within this AA.  Since it now has a member which references a global you can use it safely
    Screen: m.Screen
    BkgColor: m.BkgColor
    
    ' Variable members
    Count: 5
    GridX: 0
    GridY: 0
    Width:  150
    Height: 100
    Margin: 10
    Buffer:  Invalid
    Region:  Invalid
    
    ' Functions which act upon the variable members. Any function assigned to a function pointer member
    ' can access any other member ( variables and functions ) via the m pointer.  
    Create: simple_grid_create
    Initialize: simple_grid_initialize
    Release: simple_grid_release
    MoveTo: simple_grid_move_to
    GetWidth: simple_grid_get_width
    GetHeight: simple_grid_get_height
    Draw: simple_grid_draw
    ScrollLeft: simple_grid_scroll_left
    ScrollRight: simple_grid_scroll_right
   }
   
End Function

' Once the function is assigned to a function pointer of the AA then all members can be accessed with the m ( this ) pointer
Function simple_grid_create() As Boolean
   if not m.Initialize()
      m.Release()
      return False
   end if
   return True
End Function

Function simple_grid_initialize() As Boolean
   ' Insure everything is released.  This is important if you use sprites. 
   ' They must be explicitly removed
   m.Release()
   ' Calculate the buffer size and create the buffer
   l_w = m.Count * ( m.Width + m.Margin )
   m.Buffer = CreateObject( "roBitmap", { width: l_w, height: m.Height, AlphaEnable: True } )
   if m.Buffer = Invalid then return False
   m.Buffer.Clear( &hC0C0C0FF )
   
   ' Create the region same size as the buffer, but minus the end margin . SetWrap to true
   m.Region = CreateObject( "roRegion", m.Buffer, 0, 0, l_w - m.Margin, m.Height )
   if m.Region = Invalid then return False
   m.Region.SetWrap( True )
   
   ' Create the font and get it's static one-line-height / 2 for centering
   l_font = CreateObject( "roFontRegistry" ).GetDefaultFont( 50, True, False )
   l_strh = l_font.GetOneLineHeight() / 2
   
   ' Grid poster size and offsets ( all precalulated for optimization )
   l_offset = m.Width + m.Margin
   l_cw = m.Width  / 2
   l_ch = m.Height / 2
   l_x = 0
   l_y = 0
   
   ' Draw in the faux grid displaying it's index centered
   for l_i = 1 to m.Count
      m.Buffer.DrawRect( l_x, l_y, m.Width, m.Height, &h8D798BFF )
      l_str  = l_i.ToStr()
      l_strw = l_font.GetOneLineWidth( l_str, m.Width )
      m.Buffer.DrawText( l_str, l_x + ( l_cw - l_strw / 2 ), l_y + ( l_ch - l_strh ), &h606060FF, l_font )
      l_x = l_x + l_offset
   end for
   
   m.Buffer.Finish()
   
   return True
   
End Function

Function simple_grid_release() As Void
   m.Buffer = Invalid
   m.Region = Invalid
End Function

Function simple_grid_move_to( a_x As Integer, a_y As Integer, a_isDraw = True As Boolean ) As Boolean
   if m.Buffer = Invalid then return False
   
   m.GridX = a_x
   m.GridY = a_y
   if a_isDraw then m.Draw()
   
   return True
End Function

Function simple_grid_draw() As Boolean
   if m.Region = Invalid then return False
   m.Screen.Clear( m.BkgColor )
   m.Screen.DrawObject( m.GridX, m.GridY, m.Region )
   m.Screen.SwapBuffers()
   return True
End Function

Function simple_grid_get_width() As Integer
   if m.Region = Invalid then return -1
   return m.Region.GetWidth()
End Function

Function simple_grid_get_height() As Integer
   if m.Region = Invalid then return -1
   return m.Region.GetHeight()
End Function

Function simple_grid_scroll_left( a_frames = 16 As Integer ) As Boolean
   if m.Region = Invalid then return False
   
   l_offset  = 0
   l_prevset = 0 
   l_offdiff = 0
 
   l_columnWidth = m.Width + m.Margin
   
   for l_i = 1 to a_frames
     
      l_offset  = int( l_columnWidth * l_i / a_frames )
      l_offdiff = l_offset - l_prevset
      l_prevset = l_offset
      
      m.Region.Offset( -l_offdiff, 0, 0, 0 )
      m.Region.Finish()
      m.Draw()
       
   end for
 
   return True
End Function

Function simple_grid_scroll_right( a_frames = 16 As Integer ) As Boolean
   if m.Region = Invalid then return False
   
   l_offset  = 0
   l_prevset = 0 
   l_offdiff = 0
 
   l_columnWidth = m.Width + m.Margin
   
   for l_i = 1 to a_frames
     
      l_offset  = int( l_columnWidth * l_i / a_frames )
      l_offdiff = l_offset - l_prevset
      l_prevset = l_offset
      
      m.Region.Offset( l_offdiff, 0, 0, 0 )
      m.Region.Finish()
      m.Draw()
       
   end for
  
   return True
End Function

'*****************************************************************************************************************************

It’s worth pointing out that you can get the button codes from the brightscript core library:


Library "v30/bslCore.brs"

function main()
   keyCodes = bslUniversalControlEventCodes()
   buttonUpPressed   = keyCodes.button_up_pressed
   buttonUpReleased = keyCodes.button_up_released
end function

Yes I know. You can also make use of the global AA. But this is primarily to show how to use the m pointer, region Offsetting and AA objects

Thank you Guys!

“Rek” wrote:
It’s worth pointing out that you can get the button codes from the brightscript core library:


Library "v30/bslCore.brs"

function main()
   keyCodes = bslUniversalControlEventCodes()
   buttonUpPressed   = keyCodes.button_up_pressed
   buttonUpReleased = keyCodes.button_up_released
end function

Hi Rek,
How can I match these button released values on onkeyEvent() called? Then only I can know is the button released or not.
onkeyEvent gives only key press event only. Not the button press and release value to match and make our logic.
Thank you.

evnt = port.GetMessage()
		'IF evnt <> INVALID THEN PRINT "evnt" evnt ", Type(evnt) = " Type(evnt)		' This prints everything (mainly button presses/releases)
		IF (Type(evnt) = "roUniversalControlEvent") THEN controls(evnt)		' CONTROL EVENTS - REMOTE		' Only send remote button press events to controls()