# Interface e entrada

> Botões e cartões de escolha do canvas ficam clicáveis, com destaque automático ao passar o mouse; registre teclas de atalho, clique no chão para escolher uma posição, leia para onde o mouse aponta e saiba quem o jogador local selecionou. Cliques, teclas de atalho, feitiços lançados, o texto completo do chat e jogadores que saem entram no fluxo de eventos.

Fonte: https://war3ai.com/pt/docs/ui-input/

O que o [canvas](https://war3ai.com/pt/docs/canvas/) desenha agora **pode ser clicado**. O runtime assume a entrada da janela do jogo, e um programa externo pode:

| Recurso | Em uma frase | O jogo recebe? |
|---|---|---|
| **Itens clicáveis do canvas** | Botões, cartões de escolha, painéis: o clique gera `ui.click`, e o destaque ao passar o mouse é automático | O clique sobre o botão **não chega** ao jogo |
| **Teclas de atalho** | Registre combinações como `F5` ou `ctrl+shift+Q`; ao pressionar, gera `hotkey` | Pode ser engolida (junto com o caractere que ela produziria) |
| **Clique no chão** | Um clique no mundo gera `mouse.world`, com as coordenadas no chão | Pode ser engolido (“clique numa posição para pôr a torre”) |
| **Posição do mouse** | Atualizada a cada frame: pixels da tela, ponto do chão sob o cursor, item do canvas sob o cursor | — |
| **Seleção** | Quem o jogador local selecionou; a cada mudança, gera `selection.changed` | — |

Tudo é **entrada local + desenho local**: nada entra no fluxo de comandos do jogo, então é seguro também em partidas multijogador. Mas, se o callback alterar o mundo (criar unidades, mudar atributos), aí só funciona em partida solo.

## Python: g.ui

```python
ui = g.ui                                                   # no primeiro uso, o runtime assume a entrada da janela
ui.button("shop", "Comprar uma poção (50 de ouro)", screen=(40, 300), on_click=lambda g, ev: buy(g))
c = ui.choice("Subiu de nível! Escolha uma recompensa", [("Força +5", "Aguenta mais"), ("Vel. de ataque +20%", "Bate mais"), ("Invocar lobo", "Mais um ajudante")],
              pause=True, on_pick=lambda g, i: give(g, i))  # uma fileira de cartões no meio da tela; pause=True pausa o jogo durante a escolha
i = c.wait(timeout=30)                                      # também dá para esperar bloqueando (os eventos continuam sendo processados, nada se perde)
ui.hotkey("F5", lambda g, ev: g.say(hero, "Entendido!"))    # engolida por padrão
ui.hotkey("ctrl+shift+Q", on_press=..., swallow=False)
ui.mouse(on_click, capture=True, buttons=("left", "right"))  # captura cliques no chão: informa botão esquerdo e direito, e os engole
xy = ui.pick_point("Clique no chão: onde fica a torre?")    # versão bloqueante: próximo clique esquerdo no chão -> (x, y); Esc ou timeout -> None
ui.cursor()                                                 # {'screen': (x, y), 'world': (x, y, z) ou None, 'hover': 'shop'}
ui.toast("A onda 3 chegou!", seconds=3)
ui.close()                                                  # recolhe os seus controles e teclas de atalho; só devolve a entrada da janela se nenhum outro programa estiver usando a entrada
g.close()                                                   # ou desconecta de vez (também dá para escrever with Game(...) as g:)
```

Os callbacks recebem `(g, ev)` e disparam quando você chama `g.events()` — o executor de Bots e o de [mods de jogabilidade](https://war3ai.com/pt/docs/mods/) chamam isso a cada tick. Cliques sem callback vão para `ui.clicks`. Uma exceção lançada dentro de um callback só vai para o log, sem afetar os outros callbacks nem os eventos.

Também dá para usar o canvas diretamente: `g.canvas.text(..., clickable=True, hover=cor)`; os cliques chegam pelo fluxo de eventos, e `ev.key` é a key dada ao desenhar. Desenhar um elemento clicável liga a entrada automaticamente, sem precisar mexer em `g.ui` antes.

**Como escrever teclas de atalho**: `F1` ~ `F24`, `A` ~ `Z`, `0` ~ `9`, `numpad0` ~ `numpad9`, `space enter esc tab backspace insert delete home end pageup pagedown left up right down`, com os prefixos opcionais `ctrl+`, `shift+`, `alt+`.

> **Atenção**
>
> Letras e números sem tecla modificadora entram em conflito com a digitação no chat e com os atalhos do jogo. Prefira teclas que o jogo não usa, como F5 ~ F8, ou combinações.

## Novos eventos

`g.events()` passa a trazer estes (campos completos em [Protocolo W3P](https://war3ai.com/pt/docs/protocol/)):

| kind | Quando | Campos de conveniência |
|---|---|---|
| `ui.click` | Um item interativo do canvas foi clicado | `.key` key do canvas, `.button` (`'left'` / `'right'`), `.mods` teclas modificadoras |
| `ui.hover` | O mouse entra / sai de um item do canvas | `.key` (`None` ao sair) |
| `hotkey` | Uma tecla de atalho registrada foi pressionada | `.key` a tecla como foi registrada, `.mods` |
| `mouse.world` | Com os cliques no chão ativados, um clique no mundo | `.x .y` coordenadas no chão, `.button`, `.value` (1 = engolido) |
| `selection.changed` | A seleção do jogador local mudou | Use `g.selection()` para obter as unidades |
| `spell.cast` | Uma unidade lançou uma habilidade (que entrou em recarga) | `.spell` código de quatro caracteres, `.b` nível, `.value` recarga em segundos, `.x .y` ponto de lançamento |
| `message` | Apareceu uma linha numa caixa de mensagens da tela | `.text` texto completo, `.frame` qual caixa, `.chat` (quando é chat) |
| `player.left` | Um jogador saiu ou foi removido por derrota | `.player` |
| `game.ended` | Saída da partida | — |

## Chat e mensagens na tela

O que o jogador digita no chat é lido direto de `.chat` no evento `message`:

```python
for ev in g.events():
    if ev.kind == "message" and ev.chat and ev.chat["text"] == "-follow":
        ...                                    # ev.chat = {'channel': 'Todos', 'sender': 'nome do jogador', 'text': '-follow'}
```

`g.messages()` tem um cursor próprio e independente, e nele também aparecem as dicas do jogo (“Você precisa de mais fazendas”, “Não é possível construir aí”). Ao escrever um Bot, use-o para saber por que um comando não foi executado.

## Usando a partir de outras linguagens

- **Gateway**: os métodos `ui.button`, `ui.choice`, `ui.hotkey`, `ui.mouse`, `ui.cursor` estão disponíveis com o mesmo nome no [gateway](https://war3ai.com/pt/docs/gateway/). Um cliente remoto não pode passar funções de callback: cliques e teclas de atalho chegam pelos eventos enviados (o evento `ui.click` traz a `key`).
- **Escrevendo direto na memória compartilhada**: envie primeiro o comando semântico `input_enable` (opcode W3P 74), e o runtime passa a assumir a entrada; no bloco de entrada `Local\War3Input_<pid>`, você escreve a tabela de teclas de atalho e as chaves do mouse, e ele devolve a posição do mouse, o ponto do chão sob o cursor e o item sob o cursor. O bit `0x40` nas flags de um item do canvas significa “interativo”. O layout está em [Protocolo W3P](https://war3ai.com/pt/docs/protocol/).

## Vários programas ao mesmo tempo

Mods, Farsight, MCP e cada sessão do gateway podem pôr botões e registrar teclas de atalho na mesma partida ao mesmo tempo, sem um atrapalhar o outro:

- Cada programa registra as próprias teclas de atalho e a própria chave de cliques no chão, e o SDK junta tudo numa tabela só para o runtime. Cada tecla aparece uma vez só, os eventos vão para todos, e cada um reconhece as próprias teclas de atalho pela tecla;
- `ui.close()` só retira o que é seu, e a entrada da janela só é devolvida quando o último programa sai;
- Se um programa for encerrado à força, sem tempo de arrumar a casa: o runtime confere a cada 2 segundos e, quando todos os programas registrados já saíram, limpa as teclas de atalho e a interceptação de cliques no chão que eles deixaram, e os botões que desenharam deixam de interceptar cliques.

## Medições

2026-09-25, verificação em partidas reais numa instância de teste, 16/16:

- Clicar no botão → `ui.click` + callback, e o contador de interceptações do runtime sobe 1 (o jogo não recebeu esse clique); clicar fora do botão não dispara nada;
- F6 → `hotkey`; clique no chão → `mouse.world` (engolido);
- Criar um paladino e selecioná-lo → `selection.changed`, e `g.selection()` confere; lançar Escudo Divino → `spell.cast('AHds', 1, 35.0)`;
- Texto do mapa → `message`; chat → `message`, com `.chat` separando quem falou e o conteúdo;
- Derrotar o computador → `player.left`; encerrar a partida → `game.ended`.

Cliques de uma pessoa real nos botões e o destaque ao passar o mouse também foram conferidos um a um.

## Limites e cuidados

- **A posição vem do mouse real**: o próprio jogo lê a posição pelo cursor do sistema, então o destaque e `cursor()` refletem o mouse real. A interceptação só cuida dos cliques e das teclas.
- **Desenhado abaixo do cursor do mouse**: o Warcraft desenha o cursor como parte da imagem, a cada frame. O canvas e os balões de fala são desenhados antes do passo em que o jogo desenha o cursor: cobrem a interface do jogo e ficam cobertos pelo cursor. Só quando o cursor não é desenhado num frame (escondido ou em cinemática) o desenho volta a ser feito no último passo.
- **Escala do sistema**: se você escrever seus próprios testes e enviar cliques por mensagens de janela, as coordenadas enviadas por um processo sem reconhecimento de DPI são ampliadas pelo sistema (medido: ×1.5 com escala de 150%). Declare o programa de teste como DPI-aware antes. Cliques de uma pessoa real não são afetados.
- **Na primeira vez, as fontes precisam ser pré-aquecidas**, o que leva cerca de 1 segundo. Nesse intervalo, os botões ainda não foram desenhados e não podem ser clicados.
- **Fora de uma partida, cliques no chão não são informados**: no menu principal e na tela de resultados, `mouse.world` não é enviado nem engolido.
- Se um clique foi engolido ao pressionar e, antes de soltar, você muda para outro programa ou arrasta o mouse para fora da janela, o estado também é reiniciado: a próxima soltura não é engolida junto.
- A 1.27 não tem funções para criar novos frames de interface do jogo (elas só chegaram na 1.31): os botões e cartões daqui são desenhados pelo runtime, com estilo livre, mas não aparecem na hierarquia de menus do próprio jogo.
