Módulo event

Visão geral

O modelo de execução de um NCLua é orientado a eventos. Como será visto adiante, um script Lua para TV Digital nada mais é que um tratador de eventos.

Isso quer dizer que existem eventos associados aos usos do controle remoto, transmissões pelo canal de interatividade, sincronismos em documentos NCL, etc, e é através deles que toda dinâmica de um NCLua se faz.

Durante a inicialização do NCLua, antes de se tornar orientado a eventos, o script deve registrar pelo menos uma função de tratamento de eventos. A partir de então, qualquer ação tomada pela aplicação é somente em resposta a um evento recebido por essa função tratadora. A função event.register efetua o registro de tratadores.

--- example.lua ---
...                       -- código de inicialização
function handler (evt)
    ...                   -- código do tratador
end
event.register(handler)   -- registro do tratador

Durante sua execução, um NCLua também pode enviar eventos para se comunicar com o ambiente. Assim é possível que o NCLua envie dados pelo canal de interatividade, sinalize que sua execução terminou, etc. A função event.post efetua o envio de eventos.

Um NCLua também pode usufruir desse mecanismo de eventos internamente, através de eventos da classe user. Tais eventos, portanto, são postados e recebidos pelo próprio NCLua.

Classes de Eventos

Um evento é descrito por uma tabela Lua simples, onde o campo class é obrigatório e identifica a classe do evento. Por exemplo, um evento para indicar que a tecla '0' foi pressionada é descrito pela seguinte tabela:

{ class='key', type='press', key='0' }

As seguintes classes de eventos são definidas:

Classe 'ncl':

Um NCLua se comunica com o documento no qual está inserido através desta classe de eventos.

Em um documento NCL, relacionamentos entre nós de mídia são descritos através de elos que relacionam condições e ações. Um NCLua interage com o documento apenas através de elos em que seu objeto de mídia está associado. Portanto, não é possível que um NCLua interfira diretamente no comportamento de outras mídias presentes no documento.

Em elos que acionam o NCLua, a condição satisfeita faz com que o NCLua receba um evento descrevendo a ação a ser tomada.

Por exemplo, no elo abaixo:

<link xconnector="onBeginStart">
    <bind role="onBegin" component="videoId"/>
    <bind role="start"   component="luaId"/>
</link>

Quando videoId iniciar, o NCLua receberá o evento:

{ class='ncl', type='presentation', action='start' }

Esse evento será recebido pela função registrada durante a inicialização do script.

Em elos cuja condição depende de um NCLua, a ação será disparada quando o NCLua sinalizar o evento que case com a condição esperada.

Por exemplo, no elo abaixo:

<link xconnector="onBeginStart">
    <bind role="onEnd" component="luaId"/>
    <bind role="start" component="imageId"/>
</link>

Assim que o NCLua postar o evento:

event.post { class='ncl', type='presentation', action='stop' }

O elo irá exibir a imagem que participa do elo.

Há dois tipos de eventos da classe ncl suportados pelos NCLua: apresentação e atribuição. Eventos de seleção são tratados pela classe de eventos 'key'.

O tipo é identificado no campo type do evento e pode assumir, portanto, apenas os valores 'presentation' ou 'attribution'.

Tipo 'presentation':

Eventos de apresentação controlam a exibição do nó NCLua.

Eventos de apresentação podem estar associados a áreas (âncoras de apresentação) específicas ou ao nó como um todo. Áreas são identificadas pelo campo area e equivalem ao nó inteiro quando ausentes (i.e. iguais a nil).

O campo action indica a ação a ser tomada ou sinalizada pelo NCLua, dependendo se este está recebendo ou gerando o evento.

Em suma, um evento de apresentação possui a seguinte estrutura:

  • class: 'ncl'
  • type: 'presentation'
  • area: [string] Nome da âncora (label) associada ao evento.
  • action: [string] Pode assumir os seguintes valores: 'start', 'stop', 'abort', 'pause' e 'resume'.

Tipo 'attribution':

Eventos de atribuição controlam as propriedades do nó NCLua.

O campo property do evento contém o nome da propriedade sendo afetada.

Os eventos de atribuição são bastante similares aos de apresentação, uma vez que são regidos pelo mesmo modelo de máquina de estados. Assim, o campo action pode assumir os mesmos valores encontrados nos eventos de apresentação.

O campo value é preenchido com o valor a ser atribuido e é sempre uma string, uma vez que vem de um atributo XML. A ação de start em um evento de atribuição corresponde ao role="set" em um elo NCL.

As propriedades dos NCLua não possuem nenhuma relação direta com as variáveis declaradas no script. Um NCLua que pretende alterar o valor de uma propriedade deve postar um evento para tal fim. As propriedades dos nós são controladas pelo próprio documento NCL.

Por exemplo:

event.post {
    class    = 'ncl',
    type     = 'attribution',
    property = 'myProp',
    action   = 'start',
    value    = '10',
}

Em suma, um evento de atribuição possui a seguinte estrutura:

  • class: 'ncl'
  • type: 'attribution'
  • property: [string] Nome da propriedade (name) associada ao evento.
  • action: [string] Pode assumir os seguintes valores: 'start', 'stop', 'abort', 'pause' e 'resume'.
  • value: [string] Novo valor a ser atribuído à propriedade.

Classe 'key':

Esta classe de eventos é utilizada para representar o uso do controle remoto pelo usuário. Para esta classe não faz sentido que o NCLua gere eventos, uma vez que o controle remoto é um dispositivo unicamente de entrada.

Exemplo:

{ class='key', type='press', key='0' }

Eventos da classe key possuem a seguinte estrutura:

  • class: 'key'
  • type: [string] Pode assumir 'press' ou 'release'.
  • key: [string] Valor da tecla em questão.

Classe 'user':

Aplicações podem extender sua funcionalidade criando seus próprios eventos através desta classe.

Nenhum campo da tabela representando o evento está definido (além, claro, do campo class).

Como eventos desta classe são para uso interno, não faz sentido a postagem de seus eventos com o destino igual a 'out'.

Exemplo:

{ class='user', data='mydata' }

Classe 'tcp':

O uso do canal de interatividade é realizado por meio desta classe de eventos.

De modo a enviar e receber dados, uma conexão deve ser pré estabelecida, postando um evento como a seguir.

event.post {
    class = 'tcp',
    type  = 'connect',
    host  = <addr>,
    port  = <number>,
    [timeout = <number>,]
}

O resultado da conexão é retornado em um tratador de eventos pré registrado. O evento retornado possui a seguinte estrutura:

evt = {
    class      = 'tcp',
    type       = 'connect',
    host       = <addr>,
    port       = <number>,
    connection = identifier,
    error      = <err_msg>,
}

Os campos error e connection são mutuamente exclusivos. Quando houver um problema na conexão, uma mensagem de erro é retornada no campo error. Quando a conexão sucede, um identificador único para a conexão é retornado no campo connection.

Um NCLua envia dados através do canal de retorno postando eventos na seguinte forma:

event.post {
    class      = 'tcp',
    type       = 'data',
    connection = <identifier>,
    value      = <string>,
    [timeout   = number,]
}

De maneira similar, um NCLua recebe dados do canal de retorno em eventos da seguinte forma:

evt = {
    class      = 'tcp',
    type       = 'data',
    value      = <string>,
    connection = <identifier>,
    error      = <err_msg>,
}

Novamente, os campos error e connection são mutuamente exclusivos. Quando houver um problema na conexão, uma mensagem de erro é retornada no campo error. Quando a conexão sucede, um identificador único para a conexão é retornado no campo connection.

Para fechar uma conexão, o seguinte evento deve ser postado:

event.post {
    class      = 'tcp',
    type       = 'disconnect',
    connection = <identifier>,
}

Funções

event.register ([pos,] f)
Registra a função passada como um tratador de eventos, isto é, sempre que ocorrer um evento, `f` será chamada.
event.unregister (f)
Desregistra a função passada como um tratador de eventos, isto é, novos eventos não serão mais passados a `f`.
event.post ([dst,] evt)
Posta o evento passado.
event.timer (time, f)
Cria um timer que expira após `time` (em milisegundos) e então chama a função `f`.
event.uptime ()
Retorna o número de milisegundos decorridos desde o início da aplicação.

event.register ([pos,] f)

Recebe:

  • pos: [number] Posição de inserção da função de tratamento.
  • f: [function] Função de tratamento.

A assinatura de f deve ser:

function f (evt)
    -- returns boolean
end

Onde evt é o evento que, ao ocorrer, ativa a função. A função pode retornar true, para sinalizar que o evento foi tratado e, portanto, não deve ser enviado a outros tratadores.

É recomendado que a função (definida pela aplicação) retorne rapidamente, já que enquanto ela estiver executando, nenhum outro evento é processado.

O formatador NCL garante que as funções recebem os eventos na ordem em que foram registradas. Enquanto os tratadores não retornam o valor true, o formatador notifica o próximo tratador registrado.

O parâmetro pos é opcional e, caso não seja passado, a função registrada será a última a receber eventos.


event.unregister (f)

Desregistra a função passada como um tratador de eventos, isto é, novos eventos não serão mais passados a f.

Recebe:

  • f: [function] Função de tratamento.

Posta o evento passado.


event.post ([dst,] evt)

Recebe:

  • dst: [string] Destinatário do evento. Pode assumir os valores 'in' (envio para a própria aplicação) e 'out' (envio para o formatador NCL). Caso seja omitido, assume o valor 'out'.
  • evt: [table] Evento a ser postado.

Retorna:

  • sent: [boolean] Se o evento foi enviado com sucesso.
  • err_msg: [string] Mensagem em caso de erro.

event.timer (time, f)

Cria um timer que expira após time (em milisegundos) e então chama a função f.

Recebe:

  • time: [number] Tempo em milisegundos.
  • f: [function] Função de retomada.

Retorna:

  • unreg: [function] Função que, quando chamada, cancela o timer.

A assinatura de f é simples, sem parâmetros:

function f () end

O valor de 0 milisegundos é válido. Neste caso, f é chamada assim que possível (nunca imediatamente, dentro de um tratador de eventos). Assim como tratadores de eventos, a função f deve retornar rapidamente, pois enquanto ela é executada nenhum evento é tratado.


event.uptime ()

Retorna o número de milisegundos decorridos desde o início da aplicação.