Wallpapers para Windows

O PrimoEngine também roda no Windows 11 (23H2 ou mais novo, PCs x64 e ARM64). O formato é o mesmo .livewallpaper v1: um ZIP com manifest.json + content/. A versão para Windows já saiu, em beta: baixe em https://engine.oprimo.dev.

Resumo: wallpapers web e vídeo funcionam nos dois sistemas com o mesmo arquivo. Shader é um pacote por sistema: metal no Mac, hlsl no Windows.

TipoMacWindows
web✅✅ o mesmo arquivo, a mesma API window.LiveWallpaper
video✅✅ o mesmo arquivo (Premium nos dois)
metal✅❌ recusado: This wallpaper was made for Mac (Metal shader). Look for its Windows version.
hlsl❌ (o Mac não conhece o tipo e recusa o manifest)✅

minMacOS é ignorado no Windows.

Shader no Windows: o tipo hlsl

Um pacote hlsl é o gêmeo de um pacote metal: mesmo id, mesmo config, outro entry. Com o mesmo id, o app trata os dois como o mesmo wallpaper, e os ajustes têm os mesmos nomes. Para compartilhar, mande dois arquivos, por exemplo aurora-mac.livewallpaper e aurora-windows.livewallpaper.

manifest.json:

{
  "schemaVersion": 1,
  "id": "com.example.hello-shader",
  "version": "1.0.0",
  "title": "Hello Shader",
  "author": { "handle": "@you" },
  "type": "hlsl",
  "entry": "content/shader.hlsl",
  "config": [
    { "key": "speed", "type": "float", "min": 0.1, "max": 3.0, "default": 1.0, "label": "Speed" },
    { "key": "tint",  "type": "color", "default": "#FFFFFF", "label": "Tint" }
  ],
  "capabilities": { "network": [] }
}

content/shader.hlsl (é o hello-shader em HLSL; baixe pronto em hello-shader-windows):

// Contrato com o PrimoEngine: mantenha esta struct como está (32 bytes, register b0).
struct Uniforms {
    float2 resolution;   // tamanho da tela em pixels
    float  time;         // segundos desde que o wallpaper começou
    float  speed;        // ajuste "speed" (padrão 1.0)
    float4 tint;         // ajuste "tint" em RGB 0..1 (a = 1)
};
cbuffer PrimoUniforms : register(b0) { Uniforms u; };

// Obrigatório: a função de pixel, com o nome exato f_main.
float4 f_main(float4 fragCoord : SV_Position) : SV_Target {
    float2 uv = fragCoord.xy / u.resolution;          // 0..1, origem no canto superior esquerdo
    float t = u.time * u.speed;
    float v = sin(uv.x * 8.0 + t) + sin(uv.y * 6.0 - t * 0.7)
            + sin(length(uv - 0.5) * 14.0 - t * 1.3);
    float3 col = 0.5 + 0.5 * cos(6.28318 * (v * 0.2 + float3(0.0, 0.33, 0.67)));
    return float4(col * u.tint.rgb, 1.0);
}

O contrato

Shaders que reagem à música

Com "capabilities": { "audioReactive": true }, o shader recebe um resumo do som que o PC está tocando, nunca o áudio em si. Os valores são os mesmos do Mac (veja Shaders que reagem ao som); só o layout muda, porque o HLSL alinha arrays a 16 bytes:

struct AudioUniforms {
    float4 bands4[8];   // banda i = bands4[i >> 2][i & 3], 32 bandas de 30 Hz a 16 kHz, 0..1
    float4 levels;      // x graves, y médios, z agudos, w nível geral
    float  beat, bassTime, levelTime, midTime;
    float4 beatAges;    // segundos desde as 4 últimas batidas, mais nova primeiro; 9 = nenhuma
    float  active;      // vai a 0 depois de ~2 s de silêncio
};
cbuffer PrimoAudio : register(b1) { AudioUniforms audio; };
Texture2D<float4> history : register(t0);   // opcional: 32 × 64, canal r, linha 0 = agora

float band(int i) { return audio.bands4[i >> 2][i & 3]; }

Leia o histórico com history.Load(int3(banda, linha, 0)).r. Sem música (e nas miniaturas), tudo vale 0 e beatAges vale 9: desenhe um visual parado bonito.

Do Metal para o HLSL

MetalHLSL
#include <metal_stdlib>, using namespace metal;, v_mainapague
fragment float4 f_main(float4 p [[position]], constant Uniforms& u [[buffer(0)]])cbuffer PrimoUniforms : register(b0) { Uniforms u; }; e float4 f_main(float4 p : SV_Position) : SV_Target
constant AudioUniforms& audio [[buffer(1)]], audio.bands[i]cbuffer PrimoAudio : register(b1), audio.bands4[i >> 2][i & 3]
texture2d<float> history [[texture(0)]], history.read(uint2(x, y)).rTexture2D<float4> history : register(t0), history.Load(int3(x, y, 0)).r
fract, mix, rsqrt, atan2(y, x)frac, lerp, rsqrt, atan2(y, x)
fmod(x, y)fmod(x, y) (igual; o mod do GLSL é x - y * floor(x / y))
float2x2 m; m * vmul(v, m), com o mesmo construtor: a ordem do produto inverte
float3(1.0)float3(1.0, 1.0, 1.0) ou (float3)1.0

Confira o resultado lado a lado com o Mac: as cores e o movimento devem ser os mesmos.

O que o Windows recusa na importação

O app compila e inspeciona todo shader hlsl ao importar, venha de onde vier:

MensagemO que fazer
Shader is missing the required 'f_main' fragment function.Dê o nome f_main à função de pixel.
Shader didn't compile: …Corrija o erro mostrado. #include sempre falha.
Shader rejected: the source is larger than 512 KB.Diminua o arquivo.
Shader rejected: … writable …Tire recursos de escrita (RWTexture2D, RWBuffer, buffers estruturados, atômicos).
Shader reads audio (register(b1)) but the manifest doesn't declare …Adicione "capabilities": { "audioReactive": true }, ou tire o b1.
Shader rejected: '…' is bound outside …Use só b0, b1, t0 e s0.
Shader rejected: the uniforms … are larger than …b0 vai até 240 bytes e b1 até 192.

Nomes de arquivo

O Windows recusa nomes que o Mac aceita, com Corrupt archive: unsafe path ’…’. Evite em qualquer pacote que deva rodar nos dois:

Empacotar e testar

python3 pack.py <pasta> aceita type: "hlsl": confere o manifest, o f_main, os registros e os recursos proibidos, e gera o .livewallpaper. Ele não compila o HLSL; quem compila é o app, ao importar. No PrimoEngine para Windows, importe pelo botão Importar ou com dois cliques no arquivo.

No Windows o Python não vem instalado: instale o Python 3 pela Microsoft Store (ou em python.org) e rode py pack.py <pasta>.

Para gerar com IA, use o prompt de shader HLSL.