BusKill / buskill-app

BusKill's main CLI/GUI app for arming/disarming/configuring the BusKill laptop kill cord
https://www.buskill.in
GNU General Public License v3.0
157 stars 24 forks source link

Making the fonts optional #55

Open fmarier opened 1 year ago

fmarier commented 1 year ago

Right now, it looks like the fonts are required for the UI to load. It would be great if the app would fallback to using whatever the defaults fonts are on the system when the Roboto* files are missing.

Some dyslexic users for example choose to set their system fonts to https://opendyslexic.org/ and therefore it would be better on some systems to avoid overriding the user-selected fonts. (I would probably do this on Debian.)

maltfield commented 1 year ago

Thanks for the ticket :)

Just to clarify: Currently, if the font file is missing, no font is rendered at-all? It doesn't currently fallback to system fonts? If fallback is broken, that's terrible. I would expect that kivy would already handle this..

I know you build BusKill different from me. Could you please post the steps to reproduce this?

I certainly plan to add the ability for a user to customize the font face and size after #16 unblocks #37.

But I'm on the fence if I want the BusKill app to actually default to the system-defined font. I guess I would be open to this if I could programmatically determine if the system font is set to either [a] the OS default or [b] if the user explicitly customized the system font. If the system font was explicitly set by the user, then I agree it should default to the system font (eg for accessibility reasons).

fmarier commented 1 year ago

Currently if the font is not in the expected directory (fonts/ from where buskill_gui.py lives), then a fatal exception is thrown and the application exits:

 Traceback (most recent call last):
   File "/usr/share/buskill/main.py", line 139, in <module>
     from buskill_gui import BusKillApp
   File "/usr/share/buskill/buskill_gui.py", line 533, in <module>
     class BusKillApp(App):
   File "/usr/share/buskill/buskill_gui.py", line 553, in BusKillApp
     LabelBase.register(
   File "/usr/lib/python3/dist-packages/kivy/core/text/__init__.py", line 315, in register
     raise IOError('File {0} not found'.format(font_type))
 OSError: File fonts/RobotoMono-Regular.ttf not found

even if the font is available system-wide.

fmarier commented 1 year ago

If you want to replicate the setup I have:

  1. Copy src/* into /usr/share/buskill/ recursively.
  2. Create /usr/bin/buskill which contains the following:
    #!/bin/bash
    exec "/usr/bin/python3" "/usr/share/buskill/main.py" "$@"
    exit "$?"

Then, you can delete the /usr/share/buskill/fonts/RobotoMono-Regular.tff file and you'll get the above error.

maltfield commented 7 months ago

I can reproduce the issue where the app doesn't load when the fonts file cannot be found.

However:

  1. I see no cross-platform way to get the "system default font" in Python (even across all Linux DEs), and
  2. Kivy actually ships with Roboto by default

On a fresh install of buskill on Debian 12, for example, we see can call kivy.core.text.LabelBase.get_system_fonts_dir(), and we get

['/usr/share/fonts', '/usr/share/fonts/cMap', '/usr/share/fonts/cmap', '/usr/share/fonts/woff', '/usr/share/fonts/woff/material-design-icons-iconfont', '/usr/share/fonts/woff/ebgaramond', '/usr/share/fonts/fonts-go', '/usr/share/fonts/type1', '/usr/share/fonts/type1/texlive-fonts-recommended', '/usr/share/fonts/type1/urw-base35', '/usr/share/fonts/eot', '/usr/share/fonts/eot/material-design-icons-iconfont', '/usr/share/fonts/X11', '/usr/share/fonts/X11/75dpi', '/usr/share/fonts/X11/encodings', '/usr/share/fonts/X11/encodings/large', '/usr/share/fonts/X11/Type1', '/usr/share/fonts/X11/util', '/usr/share/fonts/X11/100dpi', '/usr/share/fonts/X11/misc', '/usr/share/fonts/truetype', '/usr/share/fonts/truetype/lato', '/usr/share/fonts/truetype/andika', '/usr/share/fonts/truetype/croscore', '/usr/share/fonts/truetype/material-design-icons-iconfont', '/usr/share/fonts/truetype/font-awesome', '/usr/share/fonts/truetype/noto', '/usr/share/fonts/truetype/libreoffice', '/usr/share/fonts/truetype/freefont', '/usr/share/fonts/truetype/clear-sans', '/usr/share/fonts/truetype/charis', '/usr/share/fonts/truetype/crosextra', '/usr/share/fonts/truetype/gentiumplus', '/usr/share/fonts/truetype/droid', '/usr/share/fonts/truetype/gentiumplus-compact', '/usr/share/fonts/truetype/liberation2', '/usr/share/fonts/truetype/adf', '/usr/share/fonts/truetype/roboto', '/usr/share/fonts/truetype/roboto/unhinted', '/usr/share/fonts/truetype/roboto/unhinted/RobotoTTF', '/usr/share/fonts/truetype/liberation', '/usr/share/fonts/truetype/comfortaa', '/usr/share/fonts/truetype/paratype', '/usr/share/fonts/truetype/lyx', '/usr/share/fonts/truetype/open-sans', '/usr/share/fonts/truetype/dejavu', '/usr/share/fonts/truetype/gentium-basic', '/usr/share/fonts/truetype/quicksand', '/usr/share/fonts/truetype/ebgaramond', '/usr/share/fonts/truetype/gentium', '/usr/share/fonts/truetype/ancient-scripts', '/usr/share/fonts/opentype', '/usr/share/fonts/opentype/artemisia', '/usr/share/fonts/opentype/inter', '/usr/share/fonts/opentype/stix-word', '/usr/share/fonts/opentype/font-awesome', '/usr/share/fonts/opentype/stix', '/usr/share/fonts/opentype/freefont', '/usr/share/fonts/opentype/didot', '/usr/share/fonts/opentype/solomos', '/usr/share/fonts/opentype/junicode', '/usr/share/fonts/opentype/complutum', '/usr/share/fonts/opentype/linux-libertine', '/usr/share/fonts/opentype/asana-math', '/usr/share/fonts/opentype/lobstertwo', '/usr/share/fonts/opentype/cantarell', '/usr/share/fonts/opentype/lobster', '/usr/share/fonts/opentype/roboto', '/usr/share/fonts/opentype/roboto/slab', '/usr/share/fonts/opentype/olga', '/usr/share/fonts/opentype/comic-neue', '/usr/share/fonts/opentype/urw-base35', '/usr/share/fonts/opentype/ebgaramond', '/usr/share/fonts/opentype/neohellenic', '/usr/share/fonts/opentype/cabin', '/usr/local/share/fonts', '/usr/lib/python3/dist-packages/kivy/data/fonts']

The Roboto font is actually installed 3 times by the following packages:

  1. buskill
  2. python3-kivy
  3. fonts-roboto-unhinted
user@disp9272:/usr$ dpkg -S /usr/share/buskill/fonts/Roboto-Regular.ttf 
buskill: /usr/share/buskill/fonts/Roboto-Regular.ttf
user@disp9272:/usr$ 

user@disp9272:/usr$ dpkg -S /usr/lib/python3/dist-packages/kivy/data/fonts/Roboto-Regular.ttf 
python3-kivy: /usr/lib/python3/dist-packages/kivy/data/fonts/Roboto-Regular.ttf
user@disp9272:/usr$ 

user@disp9272:/usr$ dpkg -S /usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Regular.ttf
fonts-roboto-unhinted: /usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Regular.ttf
user@disp9272:/usr$ 

So @fmarier you can probably remove the fonts-roboto dependency on the buskill package, since it's already installed by python3-kivy. And, well, it's also being installed by this buskill package too

user@disp9272:/usr$ ls /usr/lib/python3/dist-packages/kivy/data/fonts/
DejaVuSans.ttf         Roboto-Bold.ttf    RobotoMono-Regular.ttf
Roboto-BoldItalic.ttf  Roboto-Italic.ttf  Roboto-Regular.ttf
user@disp9272:/usr$ 

user@disp9272:/usr$ ls /usr/share/buskill/fonts/
MaterialIcons-Regular.ttf  RobotoMono-Regular.ttf
Roboto-Medium.ttf          Roboto-Regular.ttf
user@disp9272:/usr$ 
maltfield commented 7 months ago

As for fallbacks to system fonts, I don't think that's possible in python/kivy.

The best I could do is find some random font in the fonts dir returned by kivy's LabelBase.get_system_fonts_dir(), but I would have no way of knowing if the user, for example, had set some dyslexic-friendly font as their default system font

The default font in kivy appears to be, in fact, Roboto

user@disp9272:/usr$ ls /usr/lib/python3/dist-packages/kivy/data/fonts/
DejaVuSans.ttf         Roboto-Bold.ttf    RobotoMono-Regular.ttf
Roboto-BoldItalic.ttf  Roboto-Italic.ttf  Roboto-Regular.ttf
user@disp9272:/usr$ 

user@disp9272:/usr$ dpkg -S /usr/lib/python3/dist-packages/kivy/data/fonts/Roboto-Regular.ttf 
python3-kivy: /usr/lib/python3/dist-packages/kivy/data/fonts/Roboto-Regular.ttf
user@disp9272:/usr$ 
# msg = "DEBUG: Default font = " + str(Config.get('kivy', 'default_font'))
# print( msg ); logger.debug( msg )
DEBUG: Default font = ['Roboto', 'data/fonts/Roboto-Regular.ttf', 'data/fonts/Roboto-Italic.ttf', 'data/fonts/Roboto-Bold.ttf', 'data/fonts/Roboto-BoldItalic.ttf']

The one thing that I'm doing that diverges is that I've used Roboto-Medium.ttf in a few cases, which actually does not ship with python3-kivy

maltfield commented 7 months ago

TODO: make this more robust by

  1. Renaming "Roboto", "RobotoMedium", and "RobotoMono" to "FontRegular", "FontMedium", and "FontMono"
  2. If the first attempt to load the fonts in buskill_gui.py fails, then try to "find" Roboto-Medium.ttf and the other font files in all the system font dirs
fmarier commented 7 months ago

you can probably remove the fonts-roboto dependency on the buskill package, since it's already installed by python3-kivy. And, well, it's also being installed by this buskill package too

Both of these Roboto-Regular.ttf are symlinks to the font shipped by the fonts-roboto package:

$ ls -l /usr/lib/python3/dist-packages/kivy/data/fonts/Roboto-Regular.ttf
lrwxrwxrwx - root root 10 déc  2023 /usr/lib/python3/dist-packages/kivy/data/fonts/Roboto-Regular.ttf -> ../../../../../../share/fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Regular.ttf
$ ls -l /usr/share/buskill/fonts/Roboto-Regular.ttf 
lrwxrwxrwx - root root  8 sep  2023 /usr/share/buskill/fonts/Roboto-Regular.ttf -> ../../fonts/truetype/roboto/unhinted/RobotoTTF/Roboto-Regular.ttf

So there's only one copy of it on the system.

fmarier commented 7 months ago

The one exception is RobotoMono-Regular.ttf which until recently wasn't explicitly Open Source and hasn't yet been added to Debian (now that its licensing status has been clarified): https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=819273#99

maltfield commented 7 months ago

I spent some time looking for how a user could specify their own custom font (eg for accessibility needs), but it's non-trivial.

There is no built-in "font picker" in kivy. I didn't find any examples from others online. The best I've got is a list of directories with font files, and then I can recursively poke through those for ttf files.

I created a ComplexOption in the Settings screen for "font" and wrote some code to dynamically find all .ttf files and add them as an option on the screen. The result is horrendous. A few iterations of tinkering slowed my computer down. Then it totally locked-up my computer and I had to restart the VM.

Most of these fonts are probably useless anyway. For example, I randomly picked-one and it was a font for the Tamil language. We need a way to par-down the list to be shorter.

TODO

  1. remove symlinks (duplicates) from the list of fonts
  2. remove fonts that don't have glyphs for latin characters

For #2, I found some code on SE that uses the python module fonttools, but I actually liked this one better -- as it just uses the built-in module unicodedata

maltfield commented 7 months ago

I found that, on my dev system, I have:

  1. 77 font directories containing
  2. 6,182 font files
user@buskill:~/tmp/fonts$ cat font_test.py 
#!/usr/bin/env python3
import os
font_dirs = ['/usr/share/fonts', '/usr/share/fonts/cMap', '/usr/share/fonts/cmap', '/usr/share/fonts/woff', '/usr/share/fonts/woff/material-design-icons-iconfont', '/usr/share/fonts/woff/ebgaramond', '/usr/share/fonts/fonts-go', '/usr/share/fonts/type1', '/usr/share/fonts/type1/texlive-fonts-recommended', '/usr/share/fonts/type1/urw-base35', '/usr/share/fonts/eot', '/usr/share/fonts/eot/material-design-icons-iconfont', '/usr/share/fonts/X11', '/usr/share/fonts/X11/75dpi', '/usr/share/fonts/X11/encodings', '/usr/share/fonts/X11/encodings/large', '/usr/share/fonts/X11/Type1', '/usr/share/fonts/X11/util', '/usr/share/fonts/X11/100dpi', '/usr/share/fonts/X11/misc', '/usr/share/fonts/truetype', '/usr/share/fonts/truetype/lato', '/usr/share/fonts/truetype/andika', '/usr/share/fonts/truetype/croscore', '/usr/share/fonts/truetype/material-design-icons-iconfont', '/usr/share/fonts/truetype/font-awesome', '/usr/share/fonts/truetype/noto', '/usr/share/fonts/truetype/libreoffice', '/usr/share/fonts/truetype/freefont', '/usr/share/fonts/truetype/clear-sans', '/usr/share/fonts/truetype/charis', '/usr/share/fonts/truetype/crosextra', '/usr/share/fonts/truetype/gentiumplus', '/usr/share/fonts/truetype/droid', '/usr/share/fonts/truetype/gentiumplus-compact', '/usr/share/fonts/truetype/liberation2', '/usr/share/fonts/truetype/adf', '/usr/share/fonts/truetype/roboto', '/usr/share/fonts/truetype/roboto/unhinted', '/usr/share/fonts/truetype/roboto/unhinted/RobotoTTF', '/usr/share/fonts/truetype/liberation', '/usr/share/fonts/truetype/comfortaa', '/usr/share/fonts/truetype/paratype', '/usr/share/fonts/truetype/lyx', '/usr/share/fonts/truetype/open-sans', '/usr/share/fonts/truetype/dejavu', '/usr/share/fonts/truetype/gentium-basic', '/usr/share/fonts/truetype/quicksand', '/usr/share/fonts/truetype/ebgaramond', '/usr/share/fonts/truetype/gentium', '/usr/share/fonts/truetype/ancient-scripts', '/usr/share/fonts/opentype', '/usr/share/fonts/opentype/artemisia', '/usr/share/fonts/opentype/inter', '/usr/share/fonts/opentype/stix-word', '/usr/share/fonts/opentype/font-awesome', '/usr/share/fonts/opentype/stix', '/usr/share/fonts/opentype/freefont', '/usr/share/fonts/opentype/didot', '/usr/share/fonts/opentype/solomos', '/usr/share/fonts/opentype/junicode', '/usr/share/fonts/opentype/complutum', '/usr/share/fonts/opentype/linux-libertine', '/usr/share/fonts/opentype/asana-math', '/usr/share/fonts/opentype/lobstertwo', '/usr/share/fonts/opentype/cantarell', '/usr/share/fonts/opentype/lobster', '/usr/share/fonts/opentype/roboto', '/usr/share/fonts/opentype/roboto/slab', '/usr/share/fonts/opentype/olga', '/usr/share/fonts/opentype/comic-neue', '/usr/share/fonts/opentype/urw-base35', '/usr/share/fonts/opentype/ebgaramond', '/usr/share/fonts/opentype/neohellenic', '/usr/share/fonts/opentype/cabin', '/usr/local/share/fonts', '/usr/lib/python3/dist-packages/kivy/data/fonts']

# find every font file in in all the font dirs
font_paths = []
for fonts_dir_path in font_dirs:

    for root, dirs, files in os.walk(fonts_dir_path):
        for file in files:
            if file.lower().endswith(".ttf"):
                font_paths.append(str(os.path.join(root, file)))

print( len(font_dirs) )
print( len(font_paths) )
user@buskill:~/tmp/fonts$ 

user@buskill:~/tmp/fonts$ ./font_test.py 
77
6182
user@buskill:~/tmp/fonts$ 

I eliminated symlinks, but I still found the same number of fonts. Also, running this is actually very fast. The issue with kivy crashing the computer is just a kivy GUI issue with so many widgets being rendered on a screen. Perhaps it could be solved by a RecycleView. Or by going back to the normal non-complex OptionItem (if I can figure out how to populate it at runtime).

user@buskill:~/tmp/fonts$ tail -n 13 font_test.py 
# find every font file in in all the font dirs
font_paths = []
for fonts_dir_path in font_dirs:

    for root, dirs, files in os.walk(fonts_dir_path):
        for file in files:
            if file.lower().endswith(".ttf"):
                file_path = os.path.join(root,file)
                if not os.path.islink(file_path):
                    font_paths.append(str(file_path))

print( len(font_dirs) )
print( len(font_paths) )
user@buskill:~/tmp/fonts$ 
user@buskill:~/tmp/fonts$ time ./font_test.py
77
6182

real    0m0.043s
user    0m0.024s
sys 0m0.015s
user@buskill:~/tmp/fonts$ 
maltfield commented 7 months ago

I opened an upstream feature request with the Kivy team to create a standard "FontPicker" SettingItem

This is already standard in other GUI frameworks, such as GTK and .NET

maltfield commented 7 months ago

Last night I spent several hours trying to adapt our custom ComplexOptions class OptionsItem to be compatible with RecycleView (the positional arguments are an issue; I tried switching to kwargs, but it didn't work)

I also just now tried to load the 6,182 fonts into a normal option widget The normal built-in "option" widget handled the >6,000 entries better than my custom ComplexOption widget, but it still doesn't work:

1. It takes a few seconds to load
2. It doesn't scroll, so a user can only see the first ~10 fonts

TODO: try file picker (defaulting inside the font dir with the most number of TTF files found)

fmarier commented 7 months ago

As for fallbacks to system fonts, I don't think that's possible in python/kivy.

Is there a way to simply not set the font (in case of an exception while looking for it)?

What I had in mind when initially filing this issue was that buskill should load even its font is missing. Yes, ideally, it should load whatever is defined as the default font for serif or sans serif on the system, but if that's too hard, then it should just pick an arbitrary font (ideally not picking anything and just letting the OS free to use whatever it wants). That would be better than not loading at all.

But maybe Kivy has a requirement to specify a font and there's no default or fallback?

maltfield commented 7 months ago

If I don't set a font, then it will default to Roboto, which ships with kivy. If Roboto cannot be found, then it will throw an error.

(ideally not picking anything and just letting the OS free to use whatever it wants). That would be better than not loading at all.

A far as I can tell, there is no way to ask the OS to pick the font for us (at least not cross-platform let alone cross-DE). We could pick one arbitrarily, but then we risk picking a font that doesn't have glyphs for the characters we want.

fmarier commented 7 months ago

If I don't set a font, then it will default to Roboto, which ships with kivy. If Roboto cannot be found, then it will throw an error.

That sounds like a reasonable solution then. If Roboto cannot be found in the buskill directory, then try again without setting the font. In that case, it will use Kivy's default (i.e. Roboto in the Kivy directory). If that fails, then that's not buskill's problem and it's instead a bug in the Kivy installation.

fmarier commented 7 months ago

It sounds like the fact that there's no way to honor the OS defaults for fonts is a problem (or maybe feature request) with Kivy. If that ever gets fixed, then buskill will benefit from it automatically.

maltfield commented 7 months ago

Yes. And I'd say the problem goes higher than kivy. It seems that python in-general should have some cross-platform way to fetch the OS default font.

maltfield commented 7 months ago

I didn't have very good luck with the FilePicker. Namely the path type in the Kivy Settings didn't allow much customization of the FilePicker widget -- such as being able to specify a filter to only show *.ttf files.

I could have made a custom widget for this, but it also looked really bad and I figured it was likely just as much work to keep trying the RecycleView.

Indeed, I had much better luck today getting the fonts to appear in a recycle view. My latest commit displays >500 fonts (not sure why it's less than the 6,000 items before), and (with RecycleView) it loads near-instantaneous and has no lag when clicking around the screen.

This is just a proof of concept. I got the list of fonts to display, but I horribly broke the actual ability to click them and change the settings in the process. The biggest issue now is that my BusKillOptionItem() class used to take positional arguments, but the way that RecycleView is able to spawn custom widget is by passing it an array of dict()s with the values that will be passed into the widget as Kivy Properties.

(this was all very difficult to wrap my head around, and this example was instrumental in helping me get this far https://groups.google.com/g/kivy-users/c/4_xaX7xtL_s)

While I've managed to get ^ that working for the basic arguments (like icon, option value, description, etc), I'm not able to access them from within the object's functions itself -- namely __init__(). This means at least two things are broken:

  1. I can't reach-back to the Config object to get() or set() settings because the object property is None within the object's __init()__ function.
  2. The OptionItem doesn't know what is the currently-set value for the setting, so I can't make the radio button appear "checked" or "unchecked" as-needed

To address this, I've made a simplified example of this issue and posted it on SE:

maltfield commented 7 months ago

I was successfully able to get the BusKillOptionItems() created and clickable and saving to the Config object last night.

I had to switch from using __init__() to the kivy on_<property_name>() function. In the case = on_manager(). For more info, see my answer to the SE question linked-above

https://github.com/BusKill/buskill-app/blob/2d2264a43ba07b4bb3d7ffc908e7620c3ea194c4/src/buskill_gui.py#L542-L550

Unfortunately, I found some strange bug where if a user scrolls "up" at the top or "left" or "right" then it registers it like a click, so a user just scrolling through the list of fonts will change to the font under their cursor when they scroll at the "end". I've been fighting with this all day with no solution so-far. I created another SE question about this here:

maltfield commented 7 months ago

I built the app with Debian 12, and the issue still occurs.

Unfortunately I just discovered that this issue occurs even on the dev branch, so the RecycleView change was a red herring.

Yeah, I just downloaded v0.7.0 and confirmed that the scroll-becomes-a-click is also a bug on the Trigger setting.

maltfield commented 7 months ago

I fixed the scroll-click bug by checking the value of the touch.button inside the on_touch_up(self, touch) function. More info:

maltfield commented 7 months ago

In my latest commit, I have eliminated all references in the markup to Roboto fonts. Now fonts will just be rendered using the default_font Setting that's originally setup by Kivy (which defaults to Roboto).

The user can now select a font in the GUI Settings menu, which updates default_font directly, if set.

There's two exceptions to this where I specify a custom font:

  1. bkmono
  2. mdicons

I reference a custom font named "bkmono" in the text markup, which allows me to choose which mono font to use on-launch. By default, it will use RobotoMono.

The startup is also much more robust. If for some reason there's an issue finding the Roboto default fonts, BusKill will search all the Kivy-given font dirs recursively for the Roboto fonts and use whatever it finds as the default.

Of course, the app will still crash if it can't find any Roboto or Material Design Icons font on the system still, but what we have now is far more robust. And we have never had a report from a user indicating an issue with fonts.

Currently it requires 2 restarts of the app to actually apply the changes of the user's font to the app actually using it. I opened this SE question to address how to change all the widget's label's font face during runtime:

maltfield commented 7 months ago

I've successfully gotten the app to update the font face of all widgets when the user exits the Settings menu, but now I'm struggling with the "reset" button again now that I've switched from using the buskill section's gui_font_face setting, and now I'm using the built-in kivy section's default_font setting.

Now that I'm deleting the kivy section's options when the reset button is pressed, I get a NoOptionError when refresh_values() tries to figure out the default_fonts.

This could be easily fixed by updating my bulid_config()'s function with another Config.setdefaults('kivy', {...}) call, but then I'd have to hardcode the default_font. And since Kivy has changed this value over-time, I think it's better to reference the actual kivy code to set this value for us.

The code for that is here:

https://github.com/kivy/kivy/blob/c492a33f8cf79e89ea7240690feb5f6d25b08389/kivy/config.py#L902-L908

...but I don't understand how to reference it because it's at the root of the file and not in any publicly-exposed method.

I asked this question on SE about this:

maltfield commented 7 months ago

The latest commit to the font_setting branch now includes otf files (in addition to ttf files) for fonts, so users can now select a custom font in the GUI, including the Open Dyslexic fonts as mentioned in the OP

See the following screenshot of the BusKill App using the Open Dyslexic font

buskill-opendyslexic 20240325

maltfield commented 7 months ago

This has been merged into the dev branch

maltfield commented 7 months ago

TODO: test on macOS & Windows (blocked by #78)

maltfield commented 7 months ago

I finally was able to hack together a MacOS build, and I found a bug. The app crashes immediately with failure to find mdicons.ttf

02:30:54,319 buskill_gui DEBUG DEBUG: Found 207 font files.
02:30:54,320 buskill_gui DEBUG DEBUG: Default font = ['Roboto', 'data/fonts/Roboto-Regular.ttf', 'data/fonts/Roboto-Italic.ttf', 'data/fonts/Roboto-Bold.ttf', 'data/fonts/Roboto-BoldItalic.ttf']
02:30:54,320 buskill_gui INFO INFO: Failed to load fonts (File fonts/RobotoMono-Regular.ttf not found)
02:30:54,321 buskill_gui DEBUG DEBUG: Found Roboto Mono ['/Volumes/buskill-1711842980/buskill-1711842980.app/Contents/Frameworks/kivy_install/data/fonts/RobotoMono-Regular.ttf']
02:30:54,321 buskill_gui DEBUG DEBUG: Found Material Icons []
02:30:54,322 buskill_gui WARNING WARNING: Failed to find fonts (list index out of range)
...
02:30:54,453 kivy INFO Base: Start application main loop
02:30:54,467 kivy INFO Base: Leaving application in progress...
02:30:54,470 kivy WARNING stderr: Traceback (most recent call last):
02:30:54,470 kivy WARNING stderr:   File "main.py", line 150, in <module>
02:30:54,471 kivy WARNING stderr:     BusKillApp( bk ).run()
02:30:54,471 kivy WARNING stderr:   File "kivy/app.py", line 956, in run
02:30:54,472 kivy WARNING stderr:   File "kivy/base.py", line 574, in runTouchApp
02:30:54,472 kivy WARNING stderr:   File "kivy/base.py", line 339, in mainloop
02:30:54,472 kivy WARNING stderr:   File "kivy/base.py", line 379, in idle
02:30:54,473 kivy WARNING stderr:   File "kivy/clock.py", line 733, in tick
02:30:54,473 kivy WARNING stderr:   File "kivy/clock.py", line 776, in post_idle
02:30:54,474 kivy WARNING stderr:   File "kivy/_clock.pyx", line 620, in kivy._clock.CyClockBase._process_events
02:30:54,474 kivy WARNING stderr:   File "kivy/_clock.pyx", line 653, in kivy._clock.CyClockBase._process_events
02:30:54,474 kivy WARNING stderr:   File "kivy/_clock.pyx", line 649, in kivy._clock.CyClockBase._process_events
02:30:54,475 kivy WARNING stderr:   File "kivy/_clock.pyx", line 218, in kivy._clock.ClockEvent.tick
02:30:54,475 kivy WARNING stderr:   File "kivy/uix/label.py", line 425, in texture_update
02:30:54,475 kivy WARNING stderr:   File "kivy/core/text/__init__.py", line 836, in refresh
02:30:54,476 kivy WARNING stderr:   File "kivy/core/text/markup.py", line 141, in render
02:30:54,476 kivy WARNING stderr:   File "kivy/core/text/markup.py", line 228, in _pre_render
02:30:54,476 kivy WARNING stderr:   File "kivy/core/text/__init__.py", line 419, in resolve_font_name
02:30:54,477 kivy WARNING stderr: OSError: Label: File 'mdicons.ttf' not found
02:30:54,477 kivy WARNING stderr: [INFO/MainProcess] process shutting down
02:30:54,478 kivy WARNING stderr: [DEBUG/MainProcess] running all "atexit" finalizers with priority >= 0
02:30:54,478 kivy WARNING stderr: [DEBUG/MainProcess] running the remaining "atexit" finalizers
maltfield commented 7 months ago

Somehow it looks like the Material Design font is no longer being included in our MacOS builds

From the logs we can see that our app's font dir is part of the list of font dirs returned by Kivy

19:45:11,487 buskill_gui DEBUG DEBUG: system_fonts_dirs:|['/Library/Fonts', '/System/Library/Fonts', '/System/Library/Fonts/Supplemental', '/Users/maltfield/Library/Fonts', '/Volumes/buskill-1711910000/buskill-1711910000.app/Contents/Frameworks/kivy_install/data/fonts'] font files.

But the dir doesn't have the MD font

maltfield@host ~ % ls /Volumes/buskill-1711910000/buskill-1711910000.app/Contents/Frameworks/kivy_install/data/fonts
DejaVuSans.ttf          Roboto-BoldItalic.ttf   Roboto-Regular.ttf
Roboto-Bold.ttf         Roboto-Italic.ttf       RobotoMono-Regular.ttf
maltfield@host ~ % 

It looks like we already have the fonts here

/Volumes/buskill-1711910000/buskill-1711910000.app/Contents/Resources/fonts/MaterialIcons-Regular.ttf

It looks like the stable version of buskill_gui.py had a commented-out line that prepended bk.EXE_DIR.

https://github.com/BusKill/buskill-app/blob/a585f03be17d286531870ba233f7cd665cc82e1a/src/buskill_gui.py#L1118-L1122

maltfield commented 7 months ago

After adding the bk.APP_DIR to the font_dirs list, I was finally able to open the app on MacOS.

Unfortunately, the hamburger menu icon is missing. And, f I slide the navbar from the left with the mouse gesture and click Settings, then the app crashes claiming it can't find our buskill_settings.json file.

20:49:01,612 kivy WARNING stderr:   File "/Volumes/buskill-1711913377/buskill-1711
913377.app/Contents/Frameworks/buskill.kv", line 109, in <module>
20:49:01,613 kivy WARNING stderr:     root.manager.current = 'settings'
20:49:01,613 kivy WARNING stderr: ^^^^^^^^^^^^^^^^^
20:49:01,613 kivy WARNING stderr:   File "kivy/properties.pyx", line 520, in kivy.properties.Property.__set__
20:49:01,613 kivy WARNING stderr:   File "kivy/properties.pyx", line 567, in kivy.properties.Property.set
20:49:01,614 kivy WARNING stderr:   File "kivy/properties.pyx", line 606, in kivy.properties.Property._dispatch
20:49:01,614 kivy WARNING stderr:   File "kivy/_event.pyx", line 1307, in kivy._event.EventObservers.dispatch
20:49:01,614 kivy WARNING stderr:   File "kivy/_event.pyx", line 1213, in kivy._event.EventObservers._dispatch
20:49:01,615 kivy WARNING stderr:   File "kivy/uix/screenmanager.py", line 1063, in on_current
20:49:01,615 kivy WARNING stderr:   File "kivy/uix/screenmanager.py", line 377, in start
20:49:01,615 kivy WARNING stderr:   File "kivy/_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
20:49:01,615 kivy WARNING stderr:   File "buskill_gui.py", line 966, in on_pre_enter
20:49:01,616 kivy WARNING stderr:     s.add_json_panel( 'buskill', Config, os.path.join(self.bk.SRC_DIR, 'packages', 'buskill', 'settings_buskill.json') )
20:49:01,616 kivy WARNING stderr:   File "kivy/uix/settings.py", line 1044, in add_json_panel
20:49:01,617 kivy WARNING stderr:   File "kivy/uix/settings.py", line 1059, in create_json_panel
20:49:01,617 kivy WARNING stderr: FileNotFoundError: [Errno 2] No such file or directory: '/Volumes/buskill-1711913377/buskill-1711913377.app/Contents/MacOS/packages/buskill/settings_buskill.json'

The files are, in fact, present. And they're the same both for new builds and the last stable release (v0.7.0).

maltfield@5129 ~ % find /Volumes/buskill-* -name settings_buskill.json            /Volumes/buskill-1711913377/buskill-1711913377.app/Contents/Resources/packages/buskill/settings_buskill.json
/Volumes/buskill-v0.7/buskill-v0.7.0.app/Contents/Resources/packages/buskill/settings_buskill.json
maltfield@5129 ~ % 

It appears that this change somehow broke paths

maltfield commented 7 months ago

ok, so the issue is that my old MacOS release had a bunch of symlinks from Contents/MacOS/ -> Contents/Resources/ but the new one does not

maltfield@host ~ % ls -lah /Volumes/buskill-1711913377/buskill-1711913377.app/Contents/MacOS
total 20440
drwxr-xr-x  4 maltfield  staff   128B Mar 31 20:35 .
drwxr-xr-x  7 maltfield  staff   224B Mar 31 20:35 ..
-rwxr-xr-x  1 maltfield  staff   8.6M Mar 31 20:35 buskill
-r-x------  1 maltfield  staff   1.3M Mar 31 20:35 root_child_mac
maltfield@host ~ %

maltfield@host ~ % ls -lah /Volumes/buskill-v0.7/buskill-v0.7.0.app/Contents/MacOS 
total 29512
drwxr-xr-x  46 maltfield  staff   1.4K Jun 16  2023 .
drwxr-xr-x   7 maltfield  staff   224B Jun 16  2023 ..
-rwxr-xr-x   1 maltfield  staff   271K Jun 16  2023 FLAC
-rwxr-xr-x   1 maltfield  staff   727K Jun 16  2023 FreeType
lrwxr-xr-x   1 maltfield  staff    17B Jun 16  2023 KEYS -> ../Resources/KEYS
-rwxr-xr-x   1 maltfield  staff    47K Jun 16  2023 Ogg
-rwxr-xr-x   1 maltfield  staff   2.3M Jun 16  2023 Python
-rwxr-xr-x   1 maltfield  staff   1.3M Jun 16  2023 SDL2
-rwxr-xr-x   1 maltfield  staff   136K Jun 16  2023 SDL2_image
-rwxr-xr-x   1 maltfield  staff   133K Jun 16  2023 SDL2_mixer
-rwxr-xr-x   1 maltfield  staff    48K Jun 16  2023 SDL2_ttf
-rwxr-xr-x   1 maltfield  staff   931K Jun 16  2023 Vorbis
lrwxr-xr-x   1 maltfield  staff    24B Jun 16  2023 __pycache__ -> ../Resources/__pycache__
lrwxr-xr-x   1 maltfield  staff    29B Jun 16  2023 base_library.zip -> ../Resources/base_library.zip
-rwxr-xr-x   1 maltfield  staff   4.2M Jun 16  2023 buskill
lrwxr-xr-x   1 maltfield  staff    33B Jun 16  2023 buskill-icon-150.png -> ../Resources/buskill-icon-150.png
lrwxr-xr-x   1 maltfield  staff    30B Jun 16  2023 buskill-icon.icns -> ../Resources/buskill-icon.icns
lrwxr-xr-x   1 maltfield  staff    23B Jun 16  2023 buskill.kv -> ../Resources/buskill.kv
lrwxr-xr-x   1 maltfield  staff    27B Jun 16  2023 buskill_cli.py -> ../Resources/buskill_cli.py
lrwxr-xr-x   1 maltfield  staff    27B Jun 16  2023 buskill_gui.py -> ../Resources/buskill_gui.py
lrwxr-xr-x   1 maltfield  staff    31B Jun 16  2023 buskill_version.py -> ../Resources/buskill_version.py
lrwxr-xr-x   1 maltfield  staff    20B Jun 16  2023 certifi -> ../Resources/certifi
lrwxr-xr-x   1 maltfield  staff    21B Jun 16  2023 docutils -> ../Resources/docutils
lrwxr-xr-x   1 maltfield  staff    18B Jun 16  2023 fonts -> ../Resources/fonts
lrwxr-xr-x   1 maltfield  staff    16B Jun 16  2023 gpg -> ../Resources/gpg
lrwxr-xr-x   1 maltfield  staff    19B Jun 16  2023 images -> ../Resources/images
drwxr-xr-x   9 maltfield  staff   288B Jun 16  2023 kivy
lrwxr-xr-x   1 maltfield  staff    25B Jun 16  2023 kivy_install -> ../Resources/kivy_install
drwxr-xr-x  51 maltfield  staff   1.6K Jun 16  2023 lib-dynload
lrwxr-xr-x   1 maltfield  staff    30B Jun 16  2023 libassuan.0.dylib -> ../Resources/libassuan.0.dylib
-rwxr-xr-x   1 maltfield  staff   2.3M Jun 16  2023 libcrypto.1.1.dylib
lrwxr-xr-x   1 maltfield  staff    31B Jun 16  2023 libgcrypt.20.dylib -> ../Resources/libgcrypt.20.dylib
lrwxr-xr-x   1 maltfield  staff    33B Jun 16  2023 libgpg-error.0.dylib -> ../Resources/libgpg-error.0.dylib
lrwxr-xr-x   1 maltfield  staff    28B Jun 16  2023 libintl.8.dylib -> ../Resources/libintl.8.dylib  
lrwxr-xr-x   1 maltfield  staff    22B Jun 16  2023 libintl.a -> ../Resources/libi
lrwxr-xr-x   1 maltfield  staff    26B Jun 16  2023 libintl.dylib -> ../Resources/libintl.dylib
-rwxr-xr-x   1 maltfield  staff   197K Jun 16  2023 liblzma.5.dylib
lrwxr-xr-x   1 maltfield  staff    28B Jun 16  2023 libnpth.0.dylib -> ../Resources/libnpth.0.dylib
-rwxr-xr-x   1 maltfield  staff   288K Jun 16  2023 libreadline.8.dylib
-rwxr-xr-x   1 maltfield  staff   510K Jun 16  2023 libssl.1.1.dylib
lrwxr-xr-x   1 maltfield  staff    29B Jun 16  2023 libusb-1.0.dylib -> ../Resources/libusb-1.0.dylib
lrwxr-xr-x   1 maltfield  staff    20B Jun 16  2023 main.py -> ../Resources/main.py
-rwxr-xr-x   1 maltfield  staff   226K Jun 16  2023 modplug
-rwxr-xr-x   1 maltfield  staff   271K Jun 16  2023 mpg123
lrwxr-xr-x   1 maltfield  staff    21B Jun 16  2023 packages -> ../Resources/packages
-r-x------   1 maltfield  staff   609K Jun 16  2023 root_child_mac
maltfield@host ~ %