Clara volta para casa

By | Junho 14, 2022
Ícone de vaca de cachorro de alta resolução que vem com o macOS Ventura
Como começou / Como vai

Você sabia que com o macOS Ventura Clarus, o Dogcow finalmente voltou para casa? Recentemente, enquanto fazia outra coisa, pressionei acidentalmente Cmd + Shift + P, que abriu a caixa de diálogo de configuração da página. Surpreendentemente, fui recebido por uma nova versão do ícone clássico de alta resolução da Clarus que eu nunca tinha visto antes. Olhei para ele brevemente, então fechei o diálogo e voltei ao que eu estava fazendo antes. Eu assumi que, como eu estava em um aplicativo de terceiros na época, o ícone da Clarus era apenas um easter egg deixado pelo desenvolvedor. Mas um pouco mais tarde, comecei a pensar. Quais eram as chances de alguém se esforçar para personalizar a caixa de diálogo Configuração de página, de todas as coisas, apenas para o ovo de Páscoa? Zero, acabou. Esta caixa de diálogo exibe o Clarus na visualização da página em cada aplicativo.

Caixa de diálogo de configuração de página.  A visualização da página à esquerda mostra um ícone Clarus de alta resolução.

Atualmente não tenho uma máquina de testes Monterey (eu, uh, acidentalmente atualizei meu laptop para beta), mas eu acreditam esta é uma nova mudança com Ventura.

Atualizada: Instalei o Monterey em uma máquina virtual para verificar e, de fato, na caixa de diálogo Configurar página não há caracteres Clarus lá.

O próximo passo, então – depois que eu fui completamente nerd por isso – foi descobrir de onde o ícone estava vindo e se eu poderia retirá-lo de qualquer canto onde ele estava escondido.

A primeira parada foi NSPageLayout, o objeto de painel responsável por exibir o painel. É improvável que a classe contenha realmente uma implementação de placa, mas pelo menos esse foi o ponto de partida.

Para realmente olhar para a implementação desmontada desta classe AppKit, eu precisava de uma estrutura binária AppKit real. Desde o macOS Big Sur, todos os binários de quadros do sistema são armazenados mesclados em dyld cache compartilhado, não em arquivos separados. Mas eu preciso deles como arquivos separados para realmente visualizá-los.

Algumas coisas mudaram desde a última vez que escrevi sobre isso. Eu fiz a Apple antes dyld_shared_cache_util de um dos periódicos dyld despejos de origem. Isso é irritante porque você precisa fazer várias alterações no código-fonte para traduzi-lo fora do ambiente interno da Apple. Ele também pode quebrar sempre que houver uma atualização do sistema operacional. Então eu mudei para usar este utilitário quem usa dyld_extractor.bundle que vem com o Xcode. A segunda diferença de antes é menor: o cache compartilhado dyld foi movido. Enquanto antes ele estava em /System/Library/dyld/em beta Venture foi movido para /System/Cryptexes/OS/System/Library/dyld/ (Cryptex parece ser parte Resposta rápida de segurança Recurso anunciado pela Apple).

Com o cache compartilhado extraído, consegui carregar o binário do AppKit no Hopper (tive que desabilitar a análise do Objective-C, caso contrário, o aplicativo travou ao tentar carregar o binário) e comecei a vasculhar. eu estava olhando NSPageLayout uma aula que me interessa e procurei runModalWithPrintInfo: métodos, pois isso parecia um bom candidato para algo que levaria à maior parte da implementação. E, de fato, foi. O método parece ser um wrapper bastante simples PMPrepare... uma função que soa como viver em uma estrutura privada separada.

Uma janela de contêiner do AppKit que exibe o método runModalWithPrintInfo:

O próximo passo foi descobrir onde esta função de preparação foi realmente implementada. Cooper otool -L no sistema binário AppKit não revela nada obviamente útil, mas no diretório PrivateFrameworks extraído do cache compartilhado dyld há algo chamado PrintingPrivate.framework, o que parece promissor. Abrindo-o no Hopper, vi que este era realmente o quadro que eu estava procurando.

PrintingPrivate no Hopper que exibe a função _PMPrepareAppKitPageSetupDialogWithPrintInfoPrivate

Olhando para a implementação da função de preparação, o que aparece imediatamente é a chamada para _LoadAndGetPrintingUIBundle. Esta parece ser outra camada de indireção com a coisa real implementada no segundo pacote. Há também uma chamada com um nome semelhante no ramo else _LoadAndGetPrintCocoaUIBundlemas vamos começar com o primeiro na esperança de que seja mais comum.

A implementação dessa função passa por outra função auxiliar e termina com o carregamento de um PrintingUI.bundle plugin do pacote de estrutura PrintingPrivate. Isso não faz parte do cache dyld compartilhado, então posso abri-lo facilmente no Hopper sem nenhum ruído.

Se você está procurando a função de chamada PrintingPrivate, acaba no método incluído PMPageSetupController. Isso parece promissor, vamos ver o que mais essa classe pode fazer.

O que é isto? O chamado método updateClarus? Pode ser? Estamos finalmente aqui?

Programa binário PrintingUI no Hopper com uma barra de pesquisa mostrando vários métodos com 'clarus' no título

Este! Clara, estou chegando! Um método que parece particularmente encorajador é -[PMPageSetupController setClarusImageView:]. Se eu conseguir descobrir o que define a exibição da imagem, isso pode levar a onde ele configura com a imagem.

Infelizmente, o setter dessa propriedade não é referenciado em nenhum lugar no programa binário PrintingUI. Ele também não é um pegador. Fiquei preso aqui por um tempo, até que percebi que um setter não convidado é provavelmente um sinal de que a interface do usuário está definida no Nib e que o soquete foi adicionado do Interface Builder, mesmo que nunca tenha sido usado.

É claro que nos recursos de pacotes adicionais há uma PMPageSetup.nib. E se a interface do usuário de configuração da página estiver definida no Nib e o Clarus for exibido na visualização da imagem, a própria imagem provavelmente estará no catálogo de ativos.

Usando o sistema assetutil programa, todos os arquivos no catálogo de ativos compilado podem ser listados. E com certeza, aqui está:

$ assetutil --info /System/Library/PrivateFrameworks/PrintingPrivate.framework/Versions/A/Plugins/PrintingUI.bundle/Contents/Resources/Assets.car | grep -i clarus
    "Name" : "Clarus",
    "RenditionName" : "ClarusSmooth2.pdf",
    "Name" : "Clarus",
    "RenditionName" : "ClarusSmooth2.pdf",
    "Name" : "Clarus",
    "RenditionName" : "ClarusSmooth2.pdf",

Para realmente extrair uma imagem do catálogo de propriedades, tive que usar uma ferramenta de terceiros. extrato de acetato funcionou perfeitamente na primeira tentativa, embora tenha demorado um pouco mais @imports compilar em Venturi de A Fundação não exporta mais CoreGraphics.

E com isso, finalmente encarei a beleza de 512 × 512px que Smooth Clarus é:

Claro Liso

Adicionei um fundo branco à versão mostrada aqui, para que não fique invisível no modo escuro. A imagem original tem um fundo transparente.

Por fim, se você estiver escrevendo um aplicativo para Mac e quiser ocultar o Clarus em algum lugar, poderá carregar o pacote você mesmo e extrair a imagem da seguinte maneira:

let bundle = Bundle(path: "/System/Library/PrivateFrameworks/PrintingPrivate.framework/Versions/A/Plugins/PrintingUI.bundle")!
try! bundle.loadAndReturnError()
let image = bundle.image(forResource: "Clarus")

Não tenho certeza se a Mac App Store consideraria usar SPI privado, então use-o por sua conta e risco.

Seria muito legal ver Clarus retornar um dia como um símbolo do SF. Se centenas de ícones para vários produtos da Apple podem entrar, o mesmo acontece com a vaca favorita de todos.

Deixe uma resposta

O seu endereço de email não será publicado.