macOS: Switch to Tahoe style application icon
|
|
@ -193,6 +193,8 @@ Detailed list of changes
|
||||||
|
|
||||||
- Wayland: Use hold gestures to cancel momentum scrolling when fingers are placed on the trackpad, for a more natural kinetic scrolling experience (:iss:`9863`)
|
- Wayland: Use hold gestures to cancel momentum scrolling when fingers are placed on the trackpad, for a more natural kinetic scrolling experience (:iss:`9863`)
|
||||||
|
|
||||||
|
- macOS: Switch to new Tahoe style application icon with different background in light and dark modes
|
||||||
|
|
||||||
- Fix thickness of diagonal lines in box drawing characters not the same as horizontal/vertical lines (:iss:`9719`)
|
- Fix thickness of diagonal lines in box drawing characters not the same as horizontal/vertical lines (:iss:`9719`)
|
||||||
|
|
||||||
- Graphics protocol: Fix crash when handling invalid PNG image with direct transmission
|
- Graphics protocol: Fix crash when handling invalid PNG image with direct transmission
|
||||||
|
|
|
||||||
BIN
logo/Assets.car
Normal file
|
|
@ -1,8 +0,0 @@
|
||||||
Attribution
|
|
||||||
###########
|
|
||||||
|
|
||||||
- Name: macOS Big Sur Icon Template
|
|
||||||
- Author: Václav Vančura
|
|
||||||
- Source: https://www.figma.com/community/file/857303226040719059
|
|
||||||
- License: https://creativecommons.org/licenses/by/4.0/
|
|
||||||
- Modification: Replaced the template logo with kitty's
|
|
||||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4 KiB |
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" fill="none"><g filter="url(#a)"><rect width="824" height="824" x="100" y="100" fill="#fff" rx="184"/><rect width="824" height="824" x="100" y="100" fill="url(#b)" rx="184"/></g><path fill="#DDD" d="M681.218 251.015H362.14v513.183h319.078z"/><path fill="#000" d="M346.486 821.466h331.028a22.44 22.44 0 0 0 20.735-13.855 22.4 22.4 0 0 0 1.708-8.588V467.995a22.44 22.44 0 0 0-22.443-22.443H346.486a22.46 22.46 0 0 0-15.87 6.573 22.44 22.44 0 0 0-6.573 15.87v331.028c0 2.947.581 5.866 1.709 8.588a22.43 22.43 0 0 0 12.145 12.146 22.5 22.5 0 0 0 8.589 1.709m177.296-121.819h118.946a13.466 13.466 0 1 1 0 26.931H523.782a13.465 13.465 0 0 1 0-26.931M380.733 554.444a13.469 13.469 0 0 1 9.614-22.644 13.47 13.47 0 0 1 9.418 3.612l84.114 84.07a13.467 13.467 0 0 1 0 19.032l-84.114 84.115a13.467 13.467 0 0 1-19.032-19.032l74.599-74.599z"/><path fill="#C0C81F" fill-rule="evenodd" d="M436.033 403.534c5.976 0 10.756-11.354 10.756-25.952s-4.78-25.952-10.756-25.952c-5.975 0-10.755 11.354-10.755 25.952s4.78 25.952 10.755 25.952" clip-rule="evenodd"/><path fill="#784421" fill-rule="evenodd" d="M744.298 208.945c-14.598-15.409-81.099 3.244-121.648 28.384-31.629-16.219-69.745-25.951-110.295-25.951-41.36 0-79.477 9.732-111.106 25.951-40.549-25.14-107.861-44.604-121.648-28.384-14.598 16.22 10.543 83.532 40.55 120.837-2.433 9.732-4.055 19.464-4.055 30.007 0 32.439 13.786 62.446 37.305 86.776h133.813c-4.055-8.11 1.622-12.976 25.952-12.976 24.33.811 29.196 4.866 25.952 12.976h133.813c23.519-24.33 37.305-55.148 37.305-86.776 0-10.543-1.621-20.275-4.054-30.007 28.384-37.305 52.714-105.428 38.116-120.837M436.122 423.046c-28.385 0-51.903-23.519-51.903-51.903 0-28.385 23.518-51.904 51.903-51.904s51.903 23.519 51.903 51.904c0 28.384-23.518 51.903-51.903 51.903m152.466 0c-28.385 0-51.903-23.519-51.903-51.903 0-28.385 23.518-51.904 51.903-51.904s51.903 23.519 51.903 51.904c0 28.384-22.707 51.903-51.903 51.903" clip-rule="evenodd"/><path fill="#2B1100" fill-rule="evenodd" d="M666.088 419.802c48.659-60.824 148.411-90.02 214.912-47.848-77.855-12.165-148.411 8.109-214.912 47.848" clip-rule="evenodd"/><path fill="#2B1100" fill-rule="evenodd" d="M662.844 405.204c27.574-64.879 109.484-107.051 175.174-84.343-68.934 8.921-124.893 35.684-175.174 84.343m-.811 31.629c50.281-47.849 161.387-64.068 195.449-13.787-68.935-21.897-128.948-12.165-195.449 13.787m-304.121-17.031c-48.659-60.824-148.411-90.02-214.912-47.848 77.855-12.165 148.411 8.109 214.912 47.848" clip-rule="evenodd"/><path fill="#2B1100" fill-rule="evenodd" d="M361.156 405.204c-27.573-64.879-109.483-107.051-175.174-84.343 68.935 8.921 124.893 35.684 175.174 84.343m.811 31.629c-50.281-47.849-161.387-64.068-195.448-13.787 68.934-21.897 128.947-12.165 195.448 13.787" clip-rule="evenodd"/><path fill="#483737" fill-rule="evenodd" d="M297.899 388.984c-20.275 0-37.306 10.543-45.415 26.763h-2.433c-21.897 0-40.55 16.22-40.55 36.494 0 28.385 29.196 43.794 55.958 34.062 16.22 23.519 51.093 25.141 64.069 0 19.463-.811 51.092-4.055 55.958-34.062 3.244-20.274-17.842-36.494-40.55-36.494h-2.433c-6.488-16.22-24.329-26.763-44.604-26.763m428.202 0c20.275 0 37.306 10.543 45.416 26.763h2.433c11.353 0 40.549 16.22 40.549 36.494 0 28.385-29.196 43.794-55.958 34.062-16.22 23.519-51.093 25.141-64.068 0-19.464-.811-51.093-4.055-55.959-34.062-3.244-20.274 29.196-36.494 40.55-36.494h2.433c6.488-16.22 24.33-26.763 44.604-26.763" clip-rule="evenodd"/><path fill="#C0C81F" fill-rule="evenodd" d="M586.784 403.534c5.975 0 10.756-11.354 10.756-25.952s-4.781-25.952-10.756-25.952-10.755 11.354-10.755 25.952 4.78 25.952 10.755 25.952" clip-rule="evenodd"/><defs><linearGradient id="b" x1="512" x2="512" y1="100" y2="924" gradientUnits="userSpaceOnUse"><stop stop-opacity="0"/><stop offset="1" stop-opacity=".2"/></linearGradient><filter id="a" width="868" height="868" x="78" y="89" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="11"/><feGaussianBlur stdDeviation="11"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.28 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow"/><feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape"/></filter></defs></svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.3 KiB |
BIN
logo/kitty.icns
Normal file
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 514 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 48 KiB |
BIN
logo/kitty.png
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 8.2 KiB |
138
logo/make.py
|
|
@ -2,48 +2,142 @@
|
||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from copy import deepcopy
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
SRC = 'kitty.svg'
|
||||||
base = os.path.dirname(os.path.abspath(__file__))
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
unframed_src = os.path.join(base, 'kitty.svg')
|
|
||||||
framed_src = os.path.join(base, 'kitty-framed.svg')
|
# To generate this template create an icon using Icon Composer on macOS and in
|
||||||
|
# the saved .icon (which is a folder) look for icon.js
|
||||||
|
icon_settings:dict[str, Any] = {
|
||||||
|
'fill-specializations' : [
|
||||||
|
{
|
||||||
|
'value' : {
|
||||||
|
'automatic-gradient' : 'extended-gray:1.00000,1.00000'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'appearance' : 'dark',
|
||||||
|
'value' : {
|
||||||
|
'automatic-gradient' : 'display-p3:0.20500,0.20500,0.20500,1.00000'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'groups' : [
|
||||||
|
{
|
||||||
|
'layers' : [
|
||||||
|
{
|
||||||
|
'blend-mode' : 'normal',
|
||||||
|
'glass' : False,
|
||||||
|
'hidden' : False,
|
||||||
|
'image-name' : 'icon.svg',
|
||||||
|
'name' : 'icon',
|
||||||
|
'opacity' : 1,
|
||||||
|
'position' : {
|
||||||
|
'scale' : 0.9,
|
||||||
|
'translation-in-points' : [
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'shadow' : {
|
||||||
|
'kind' : 'neutral',
|
||||||
|
'opacity' : 0.5
|
||||||
|
},
|
||||||
|
'translucency' : {
|
||||||
|
'enabled' : True,
|
||||||
|
'value' : 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'supported-platforms' : {
|
||||||
|
'circles' : [
|
||||||
|
'watchOS'
|
||||||
|
],
|
||||||
|
'squares' : 'shared'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def abspath(x: str) -> str:
|
||||||
|
return os.path.abspath(os.path.join(base, x))
|
||||||
|
|
||||||
|
|
||||||
def abspath(x):
|
def run(*args: str) -> None:
|
||||||
return os.path.join(base, x)
|
|
||||||
|
|
||||||
|
|
||||||
def run(*args):
|
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(args)
|
subprocess.check_call(args)
|
||||||
except OSError:
|
except OSError:
|
||||||
raise SystemExit(f'You are missing the {args[0]} program needed to generate the kitty logo')
|
raise SystemExit(f'You are missing the {args[0]} program needed to generate the kitty logo')
|
||||||
|
|
||||||
|
|
||||||
def render(output, sz=256, src=unframed_src):
|
def get_svg_viewbox(file_path: str) -> tuple[float, ...]:
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
tree = ET.parse(file_path)
|
||||||
|
root = tree.getroot()
|
||||||
|
viewbox = root.get('viewBox')
|
||||||
|
if viewbox:
|
||||||
|
return tuple(float(x) for x in viewbox.split())
|
||||||
|
width = root.get('width')
|
||||||
|
height = root.get('height')
|
||||||
|
return (0.0, 0.0, float(width or 0), float(height or 0))
|
||||||
|
|
||||||
|
|
||||||
|
def create_icon(name: str, svg_path: str, output_path: str) -> str:
|
||||||
|
view_box = get_svg_viewbox(svg_path)
|
||||||
|
sz = view_box[-1]
|
||||||
|
scale = 0.9 * 1024 / sz
|
||||||
|
icon_dir = os.path.join(output_path, f'{name}.icon')
|
||||||
|
if os.path.exists(icon_dir):
|
||||||
|
shutil.rmtree(icon_dir)
|
||||||
|
os.mkdir(icon_dir)
|
||||||
|
s = deepcopy(icon_settings)
|
||||||
|
for group in s['groups']:
|
||||||
|
for layer in group['layers']:
|
||||||
|
layer['image-name'] = os.path.basename(svg_path)
|
||||||
|
layer['name'] = name
|
||||||
|
layer['position']['scale'] = scale
|
||||||
|
with open(os.path.join(icon_dir, 'icon.json'), 'w') as f:
|
||||||
|
json.dump(s, f, indent=2)
|
||||||
|
assets_dir = os.path.join(icon_dir, 'Assets')
|
||||||
|
os.mkdir(assets_dir)
|
||||||
|
shutil.copy(svg_path, assets_dir)
|
||||||
|
return icon_dir
|
||||||
|
|
||||||
|
|
||||||
|
def create_assets() -> None:
|
||||||
|
actool = [
|
||||||
|
'xcrun', 'actool', '--warnings', '--platform', 'macosx', '--compile', base,
|
||||||
|
'--minimum-deployment-target', '15.0', '--output-partial-info-plist', '/dev/stdout',
|
||||||
|
]
|
||||||
|
icon = create_icon('kitty', abspath(SRC), base)
|
||||||
|
run(*(actool + ['--app-icon', 'kitty', icon]))
|
||||||
|
shutil.rmtree(icon)
|
||||||
|
|
||||||
|
|
||||||
|
def render(output: str, sz: int = 256) -> None:
|
||||||
|
src = abspath(SRC)
|
||||||
print(f'Rendering {os.path.basename(src)} at {sz}x{sz}...')
|
print(f'Rendering {os.path.basename(src)} at {sz}x{sz}...')
|
||||||
run('rsvg-convert', '-w', str(sz), '-h', str(sz), '-o', output, src)
|
run('rsvg-convert', '-w', str(sz), '-h', str(sz), '-o', output, src)
|
||||||
run('optipng', '-quiet', '-o7', '-strip', 'all', output)
|
run('optipng', '-quiet', '-o7', '-strip', 'all', output)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
|
if 'darwin' in sys.platform.lower():
|
||||||
|
create_assets()
|
||||||
|
if sys.argv[-1] == 'remote-macos':
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
run('ssh', 'ox', 'zsh', '-ilc', '~/bin/update-kitty && python3 ~/kitty-src/logo/make.py remote-macos')
|
||||||
|
run('rsync', '-avz', '--include=*.icns', '--include=*.car', '--exclude=*', 'ox:~/kitty-src/logo/', base + '/')
|
||||||
render(abspath('kitty.png'))
|
render(abspath('kitty.png'))
|
||||||
render(abspath('kitty-128.png'), sz=128)
|
render(abspath('kitty-128.png'), sz=128)
|
||||||
iconset = abspath('kitty.iconset')
|
|
||||||
if os.path.exists(iconset):
|
|
||||||
shutil.rmtree(iconset)
|
|
||||||
os.mkdir(iconset)
|
|
||||||
os.chdir(iconset)
|
|
||||||
for sz in (16, 32, 64, 128, 256, 512, 1024):
|
|
||||||
iname = os.path.join(iconset, 'icon_{0}x{0}.png'.format(sz))
|
|
||||||
iname2x = 'icon_{0}x{0}@2x.png'.format(sz // 2)
|
|
||||||
render(iname, sz, src=framed_src)
|
|
||||||
if sz > 16 and sz != 128:
|
|
||||||
shutil.copy2(iname, iname2x)
|
|
||||||
if sz in (64, 1024):
|
|
||||||
os.remove(iname)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
21
setup.py
|
|
@ -1726,6 +1726,7 @@ def macos_info_plist(for_quake: str = '') -> bytes:
|
||||||
CFBundleAllowMixedLocalizations=True,
|
CFBundleAllowMixedLocalizations=True,
|
||||||
TICapsLockLanguageSwitchCapable=True,
|
TICapsLockLanguageSwitchCapable=True,
|
||||||
# User Interface and Graphics
|
# User Interface and Graphics
|
||||||
|
CFBundleIconName=appname,
|
||||||
CFBundleIconFile=f'{appname}.icns',
|
CFBundleIconFile=f'{appname}.icns',
|
||||||
NSHighResolutionCapable=True,
|
NSHighResolutionCapable=True,
|
||||||
NSSupportsAutomaticGraphicsSwitching=True,
|
NSSupportsAutomaticGraphicsSwitching=True,
|
||||||
|
|
@ -1769,24 +1770,8 @@ def macos_info_plist(for_quake: str = '') -> bytes:
|
||||||
|
|
||||||
|
|
||||||
def create_macos_app_icon(where: str = 'Resources') -> None:
|
def create_macos_app_icon(where: str = 'Resources') -> None:
|
||||||
iconset_dir = os.path.abspath(os.path.join('logo', f'{appname}.iconset'))
|
for x in (f'{appname}.icns', 'Assets.car'):
|
||||||
icns_dir = os.path.join(where, f'{appname}.icns')
|
shutil.copy(os.path.join('logo', x), os.path.join(where, x))
|
||||||
try:
|
|
||||||
subprocess.check_call([
|
|
||||||
'iconutil', '-c', 'icns', iconset_dir, '-o', icns_dir
|
|
||||||
])
|
|
||||||
except FileNotFoundError:
|
|
||||||
print(f'{error("iconutil not found")}, using png2icns (without retina support) to convert the logo', file=sys.stderr)
|
|
||||||
subprocess.check_call([
|
|
||||||
'png2icns', icns_dir
|
|
||||||
] + [os.path.join(iconset_dir, logo) for logo in [
|
|
||||||
# png2icns does not support retina icons, so only pass the non-retina icons
|
|
||||||
'icon_16x16.png',
|
|
||||||
'icon_32x32.png',
|
|
||||||
'icon_128x128.png',
|
|
||||||
'icon_256x256.png',
|
|
||||||
'icon_512x512.png',
|
|
||||||
]])
|
|
||||||
|
|
||||||
|
|
||||||
quake_name = f'{appname}-quick-access'
|
quake_name = f'{appname}-quick-access'
|
||||||
|
|
|
||||||