mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2025-02-21 02:55:37 -08:00
feat(fonts): install as user
This commit is contained in:
parent
5289ba1f19
commit
eee5198664
|
@ -10,6 +10,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
user bool
|
||||
|
||||
// fontCmd can work with fonts
|
||||
fontCmd = &cobra.Command{
|
||||
Use: "font [install|configure]",
|
||||
|
@ -37,8 +39,22 @@ This command is used to install fonts and configure the font in your terminal.
|
|||
env := &platform.Shell{}
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
needsAdmin := env.GOOS() == platform.WINDOWS && !env.Root()
|
||||
font.Run(fontName, needsAdmin)
|
||||
|
||||
// Windows users need to specify the --user flag if they want to install the font as user
|
||||
// If the user does not specify the --user flag, the font will be installed as a system font
|
||||
// and therefore we need to be administrator
|
||||
system := env.Root()
|
||||
if env.GOOS() == platform.WINDOWS && !user && !system {
|
||||
fmt.Println(`
|
||||
You need to be administrator to install a font as system font.
|
||||
You can either run this command as administrator or specify the --user flag to install the font for your user only:
|
||||
|
||||
oh-my-posh font install --user
|
||||
`)
|
||||
return
|
||||
}
|
||||
|
||||
font.Run(fontName, system)
|
||||
return
|
||||
case "configure":
|
||||
fmt.Println("not implemented")
|
||||
|
@ -51,4 +67,5 @@ This command is used to install fonts and configure the font in your terminal.
|
|||
|
||||
func init() { //nolint:gochecknoinits
|
||||
RootCmd.AddCommand(fontCmd)
|
||||
fontCmd.Flags().BoolVar(&user, "user", false, "install font as user")
|
||||
}
|
||||
|
|
|
@ -64,18 +64,17 @@ const (
|
|||
downloadFont
|
||||
unzipFont
|
||||
installFont
|
||||
admin
|
||||
quit
|
||||
done
|
||||
)
|
||||
|
||||
type main struct {
|
||||
spinner spinner.Model
|
||||
list *list.Model
|
||||
needsAdmin bool
|
||||
font string
|
||||
state state
|
||||
err error
|
||||
spinner spinner.Model
|
||||
list *list.Model
|
||||
system bool
|
||||
font string
|
||||
state state
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *main) buildFontList(nerdFonts []*Asset) {
|
||||
|
@ -115,17 +114,17 @@ func downloadFontZip(location string) {
|
|||
program.Send(zipMsg(zipFile))
|
||||
}
|
||||
|
||||
func installLocalFontZIP(zipFile string) {
|
||||
func installLocalFontZIP(zipFile string, user bool) {
|
||||
data, err := os.ReadFile(zipFile)
|
||||
if err != nil {
|
||||
program.Send(errMsg(err))
|
||||
return
|
||||
}
|
||||
installFontZIP(data)
|
||||
installFontZIP(data, user)
|
||||
}
|
||||
|
||||
func installFontZIP(zipFile []byte) {
|
||||
err := InstallZIP(zipFile)
|
||||
func installFontZIP(zipFile []byte, user bool) {
|
||||
err := InstallZIP(zipFile, user)
|
||||
if err != nil {
|
||||
program.Send(errMsg(err))
|
||||
return
|
||||
|
@ -134,11 +133,6 @@ func installFontZIP(zipFile []byte) {
|
|||
}
|
||||
|
||||
func (m *main) Init() tea.Cmd {
|
||||
if m.needsAdmin {
|
||||
m.state = admin
|
||||
return tea.Quit
|
||||
}
|
||||
|
||||
isLocalZipFile := func() bool {
|
||||
return !strings.HasPrefix(m.font, "https") && strings.HasSuffix(m.font, ".zip")
|
||||
}
|
||||
|
@ -157,7 +151,7 @@ func (m *main) Init() tea.Cmd {
|
|||
|
||||
defer func() {
|
||||
if isLocalZipFile() {
|
||||
go installLocalFontZIP(m.font)
|
||||
go installLocalFontZIP(m.font, m.system)
|
||||
return
|
||||
}
|
||||
go getFontsList()
|
||||
|
@ -215,7 +209,7 @@ func (m *main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
case zipMsg:
|
||||
m.state = installFont
|
||||
defer func() {
|
||||
go installFontZIP(msg)
|
||||
go installFontZIP(msg, m.system)
|
||||
}()
|
||||
m.spinner.Spinner = spinner.Dot
|
||||
return m, m.spinner.Tick
|
||||
|
@ -250,8 +244,6 @@ func (m *main) View() string {
|
|||
return "\n" + m.list.View()
|
||||
case downloadFont:
|
||||
return textStyle.Render(fmt.Sprintf("%s Downloading %s", m.spinner.View(), m.font))
|
||||
case admin:
|
||||
return textStyle.Render("You need to be admin to install a font on Windows")
|
||||
case unzipFont:
|
||||
return textStyle.Render(fmt.Sprintf("%s Extracting %s", m.spinner.View(), m.font))
|
||||
case installFont:
|
||||
|
@ -264,10 +256,10 @@ func (m *main) View() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func Run(font string, needsAdmin bool) {
|
||||
func Run(font string, system bool) {
|
||||
main := &main{
|
||||
font: font,
|
||||
needsAdmin: needsAdmin,
|
||||
font: font,
|
||||
system: system,
|
||||
}
|
||||
program = tea.NewProgram(main)
|
||||
if _, err := program.Run(); err != nil {
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
func InstallZIP(data []byte) (err error) {
|
||||
func InstallZIP(data []byte, user bool) (err error) {
|
||||
bytesReader := bytes.NewReader(data)
|
||||
|
||||
zipReader, err := zip.NewReader(bytesReader, int64(bytesReader.Len()))
|
||||
|
@ -50,7 +50,7 @@ func InstallZIP(data []byte) (err error) {
|
|||
}
|
||||
|
||||
for _, font := range fonts {
|
||||
if err = install(font); err != nil {
|
||||
if err = install(font, user); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
var FontsDir = path.Join(os.Getenv("HOME"), "Library", "Fonts")
|
||||
|
||||
func install(font *Font) (err error) {
|
||||
func install(font *Font, _ bool) (err error) {
|
||||
// On darwin/OSX, the user's fonts directory is ~/Library/Fonts,
|
||||
// and fonts should be installed directly into that path;
|
||||
// i.e., not in subfolders.
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
// FontsDir denotes the path to the user's fonts directory on Unix-like systems.
|
||||
var FontsDir = path.Join(os.Getenv("HOME"), "/.local/share/fonts")
|
||||
|
||||
func install(font *Font) (err error) {
|
||||
func install(font *Font, _ bool) (err error) {
|
||||
// On Linux, fontconfig can understand subdirectories. So, to keep the
|
||||
// font directory clean, install all font files for a particular font
|
||||
// family into a subdirectory named after the family (with hyphens instead
|
||||
|
|
|
@ -12,34 +12,49 @@ import (
|
|||
)
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-addfontresourcea
|
||||
var FontsDir = filepath.Join(os.Getenv("WINDIR"), "Fonts")
|
||||
|
||||
const (
|
||||
WM_FONTCHANGE = 0x001D //nolint:revive
|
||||
HWND_BROADCAST = 0xFFFF //nolint:revive
|
||||
)
|
||||
|
||||
func install(font *Font) (err error) {
|
||||
func install(font *Font, admin bool) (err error) {
|
||||
// To install a font on Windows:
|
||||
// - Copy the file to the fonts directory
|
||||
// - Add registry entry
|
||||
// - Call AddFontResourceW to set the font
|
||||
// - Notify other applications that the fonts have changed
|
||||
fullPath := filepath.Join(FontsDir, font.FileName)
|
||||
fontsDir := filepath.Join(os.Getenv("WINDIR"), "Fonts")
|
||||
if !admin {
|
||||
fontsDir = filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Local", "Microsoft", "Windows", "Fonts")
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(fontsDir, font.FileName)
|
||||
// validate if font is already installed, remove it in case it is
|
||||
if _, err := os.Stat(fullPath); err == nil {
|
||||
if err = os.Remove(fullPath); err != nil {
|
||||
return fmt.Errorf("Unable to remove existing font file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
err = os.WriteFile(fullPath, font.Data, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
return fmt.Errorf("Unable to write font file: %s", err.Error())
|
||||
}
|
||||
|
||||
// Add registry entry
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts`, registry.WRITE)
|
||||
reg := registry.LOCAL_MACHINE
|
||||
if !admin {
|
||||
reg = registry.CURRENT_USER
|
||||
}
|
||||
|
||||
k, err := registry.OpenKey(reg, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts`, registry.WRITE)
|
||||
if err != nil {
|
||||
// If this fails, remove the font file as well.
|
||||
if nexterr := os.Remove(fullPath); nexterr != nil {
|
||||
return nexterr
|
||||
return errors.New("Unable to delete font file after registry key open error")
|
||||
}
|
||||
|
||||
return err
|
||||
return fmt.Errorf("Unable to open registry key: %s", err.Error())
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
|
@ -47,10 +62,10 @@ func install(font *Font) (err error) {
|
|||
if err = k.SetStringValue(name, font.FileName); err != nil {
|
||||
// If this fails, remove the font file as well.
|
||||
if nexterr := os.Remove(fullPath); nexterr != nil {
|
||||
return nexterr
|
||||
return errors.New("Unable to delete font file after registry key set error")
|
||||
}
|
||||
|
||||
return err
|
||||
return fmt.Errorf("Unable to set registry value: %s", err.Error())
|
||||
}
|
||||
|
||||
gdi32 := syscall.NewLazyDLL("gdi32.dll")
|
||||
|
@ -63,7 +78,7 @@ func install(font *Font) (err error) {
|
|||
|
||||
ret, _, _ := proc.Call(uintptr(unsafe.Pointer(fontPtr)))
|
||||
if ret == 0 {
|
||||
return errors.New("unable to add font resource")
|
||||
return errors.New("Unable to add font resource using AddFontResourceW")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -28,7 +28,9 @@ To see the icons displayed in Oh My Posh, **install** a [Nerd Font][nerdfonts],
|
|||
Oh My Posh has a CLI to help you select and install a [Nerd Font][nerdfonts] (beta):
|
||||
|
||||
:::caution Windows
|
||||
This command needs to be executed as admin. The fonts are installed system wide.
|
||||
This command is best executed as admin so that fonts are installed system wide.
|
||||
In case you have no admin rights, you can install the fonts by adding the `--user` flag.
|
||||
Do know this can have side-effects when using certain applications.
|
||||
:::
|
||||
|
||||
```bash
|
||||
|
|
Loading…
Reference in a new issue