imported posts from jekyll
This commit is contained in:
commit
55fd598405
|
@ -0,0 +1,3 @@
|
|||
[submodule "harbor"]
|
||||
path = themes/harbor
|
||||
url = https://github.com/matsuyoshi30/harbor
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
baseURL = "http://example.org/"
|
||||
languageCode = "en-us"
|
||||
title = "wanderings"
|
||||
theme = "harbor"
|
||||
|
||||
[Author]
|
||||
name = "la Fleur"
|
||||
|
||||
[outputs]
|
||||
section = ["JSON", "HTML"]
|
||||
|
||||
[[params.nav]]
|
||||
identifier = "about"
|
||||
name = "About"
|
||||
icon = "fas fa-user fa-lg"
|
||||
url = "/about/"
|
||||
weight = 3
|
||||
|
||||
[[params.nav]]
|
||||
identifier = "tags"
|
||||
name = "Tags"
|
||||
icon = "fas fa-tag fa-lg"
|
||||
url = "tags"
|
||||
weight = 3
|
||||
|
||||
[[params.nav]]
|
||||
identifier = "categories"
|
||||
name = "Category"
|
||||
icon = "fas fa-folder-open fa-lg"
|
||||
url = "categories"
|
||||
weight = 3
|
||||
|
||||
[[params.nav]]
|
||||
identifier = "search"
|
||||
name = "Search"
|
||||
icon = "fas fa-search fa-lg"
|
||||
url = "search"
|
||||
weight = 3
|
||||
|
||||
[params.logo]
|
||||
url = "icon.jpg" # static/images/icon.jpg
|
||||
width = 50
|
||||
height = 50
|
||||
alt = "Logo"
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: About
|
||||
permalink: /about/
|
||||
---
|
||||
I am la Fleur, login `lafleur`. I'm a free software enthousiast. Else about me,
|
||||
you will discover by scrolling this site.
|
|
@ -0,0 +1,181 @@
|
|||
+++
|
||||
title = "Arch on a Raspberry Pi - the basics"
|
||||
date = 2020-09-26
|
||||
tags = ["admin"]
|
||||
toc = true
|
||||
+++
|
||||
Installing Arch on a Raspberry Pi is amazingly easy. the Arch ARM project gives
|
||||
instructions for the basic installation [here][aw-arm-install].
|
||||
|
||||
## Chroot into the SD card
|
||||
|
||||
When you finished installing the basic system to the SD card, you may wish to
|
||||
install the following packages in your host system (we'll suppose your host
|
||||
system is already an Arch linux) : `arch-install-script` that provides the
|
||||
`arch-chroot` command, and `qemu-user-static-bin`, that provides the means to
|
||||
run ARM binaries from the host system. Then you can mount the SD card on e.g.
|
||||
`/mnt` and do :
|
||||
```
|
||||
# arch-chroot /mnt
|
||||
```
|
||||
`arch-chroot` will mount all useful mounts like `/mnt/proc`, `/mnt/sys`,
|
||||
`/mnt/dev`, and event `/mnt/boot` if it's in `/mnt/etc/fstab`. When you're done,
|
||||
it will clean everything and hopefully let you unmount the drive cleanly.
|
||||
|
||||
## Setup SSH
|
||||
|
||||
Over there you can modify your target system, which provides a user named `pi`.
|
||||
You might use that or add a user. Make sure you set up ssh login credentials. If
|
||||
in doubt check [Archlinux wiki's OpenSSh page][aw-ssh] ; I believe it's
|
||||
installed by default on archlinuxarm.
|
||||
|
||||
When you're done just hit `Ctrl-d` to unbind the chroot.
|
||||
|
||||
## Networking
|
||||
|
||||
The tricky bit is to be able to log into it on a headless config. You will have
|
||||
to make sure it's reachable on the network so you can ssh into it. There are a
|
||||
few options here : if the RPi provides an ethernet plug, connect over ethernet.
|
||||
If not (as for the RPi zero series), connect with USB over ethernet. If you need
|
||||
(or crave) to connect wirelessly, the wifi leaves you options to either make it
|
||||
connect to your local network, or turn the RPi into a wifi hotspot.
|
||||
|
||||
Note that wireless networking does imply a certain overhead ; you might consider
|
||||
turning it off when in production use, specially when running JACK (I read it
|
||||
somewhere in a JACK tutorial page but I can't remember where).
|
||||
|
||||
### Wired over USB
|
||||
|
||||
Skip this part if your RPi has an ethernet socket. Else chroot in the SD card.
|
||||
Add the following to `/boot/cmdline.txt` :
|
||||
|
||||
modules_load=dwc2,g_ether
|
||||
|
||||
Now into `/boot/config.txt`, add the following line :
|
||||
|
||||
dtoverlay=dwc2
|
||||
|
||||
This will let the system load an ethernet over USB module at boot-time. To have
|
||||
it setup at boot-time run :
|
||||
```
|
||||
# systemctl enable systemd-networkd
|
||||
```
|
||||
And create or edit `/etc/systemd/network/01-usb.network` so that it reads :
|
||||
``` systemd
|
||||
[Match]
|
||||
Name=usb*
|
||||
|
||||
[Network]
|
||||
LinkLocalAddressing=ipv4
|
||||
IPv4LLRoute=yes
|
||||
```
|
||||
get out of the chroot, unmount the SD card and boot.
|
||||
|
||||
### Wireless - connect to a local network
|
||||
|
||||
You will need the `wpa_supplicant` package, and a systemd service to launch it.
|
||||
Chroot into the SD card, and run as root :
|
||||
```
|
||||
# pacman -S wpa_supplicant
|
||||
```
|
||||
`wpa_supplicant` can be started by `systemd-networkd` : edit/create
|
||||
`/etc/systemd/network/05-wlan.network` with the following contents :
|
||||
``` systemd
|
||||
[Match]
|
||||
Name=wlan*
|
||||
|
||||
# Run as a client with wpa_supplicant
|
||||
[Network]
|
||||
DHCP=ipv4
|
||||
MulticastDNS=yes
|
||||
```
|
||||
Now edit or create `/etc/wpa_supplicant/wpa_supplicant-wlan0.conf`, and add your
|
||||
local network credentials. Refer to `man wpa_supplicant.conf` for details. The
|
||||
syntax is :
|
||||
``` wpa-supplicant
|
||||
network={
|
||||
ssid="THE_NAME_OF_YOUR_ACCESS_POINT"
|
||||
psk="THE_ACCESS_POINT_PASSWORD"
|
||||
}
|
||||
```
|
||||
`systemd-networkd` will start `wpa_supplicant@wlan0.service`, who will in turn
|
||||
use the latter config file to run. You can quit the chroot, unmount the SD card
|
||||
and boot it ; the system should connect to the local network at boot-time. You
|
||||
can add Access Points at run-time with the `wpa_passphrase` utility, like in :
|
||||
```
|
||||
wpa_passphrase AP_NAME AP_PASSWORD | sudo tee --append
|
||||
/etc/wpa_supplicant/wpa_supplicant-wlan0.conf
|
||||
```
|
||||
|
||||
### Wireless - fallback to hotspot
|
||||
|
||||
It can be very handy to fall back to setting up an Access Point if we can't
|
||||
connect to any known network. This way you're still able to `ssh` back into the
|
||||
RPi and for instance register a new Access Point to connect to. This trick was
|
||||
derived from [this post][replace-hostapd].
|
||||
|
||||
`wpa_supplicant` provides this feature with a rather ... cryptic manner ; see by
|
||||
yourself :
|
||||
```
|
||||
# AP configuration as a fallback :
|
||||
network={
|
||||
ssid="RPi"
|
||||
mode=2
|
||||
priority=-999
|
||||
key_mgmt=WPA-PSK
|
||||
psk="a rather long raspberry pi password"
|
||||
frequency=2412
|
||||
}
|
||||
```
|
||||
You just insert this snippet in `/etc/wpa_supplicant/wpa_supplicant-wlan0.conf`
|
||||
so that it gets loaded only if the other networks are not found. It doesn't have
|
||||
to sit in the bottom, so you can still append more attractive APs with
|
||||
`wpa_passphrase`. Even if you don't use the `priority` option, `wpa_supplicant`
|
||||
will be smart enough to load it only if no known host is found. The RPi Zero W
|
||||
doesn't do 802.11g, it's only capable of 802.11b, but you don't need to tell it,
|
||||
it will handle that. I actually tested all these options, because I couldn't
|
||||
find documentation on these features.
|
||||
|
||||
But it won't give you or your client an IP address - for this a DHCP server is
|
||||
needed. The RPi can act like one with the `dnsmasq` package, who will in turn
|
||||
require the interface to be setup with a static address. As stated in this
|
||||
[StackExchange discussion][se-static-addr], you can have several addresses on
|
||||
the same interface at the same moment. So in
|
||||
`/etc/systemd/network/05-wlan0.network`, append this line to the `[Network]`
|
||||
section :
|
||||
```
|
||||
Address=192.168.2.1/24
|
||||
```
|
||||
We just want `dnsmasq` to run if the RPi is in Access Point mode. Systemd can
|
||||
help here, you may add a conditional on `dnsmasq.service`'s execution with the
|
||||
following snippet :
|
||||
|
||||
file: /etc/systemd/system/dnsmasq.service.d/only-if-we-are-an-AP.conf
|
||||
|
||||
``` systemd
|
||||
# Insert a test that fails if the device is not an Access Point.
|
||||
[Unit]
|
||||
After=wpa_supplicant@wlan0.service
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/bin/sh -c 'eval $(wpa_cli -i wlan0 status) && test "$mode" == "AP"'
|
||||
|
||||
[Install]
|
||||
WantedBy=wpa_supplicant@wlan0.service
|
||||
```
|
||||
If you want to know what it does, try `sudo wpa_cli -i wlan0 status` ; you'll
|
||||
see it outputs various informations about the network device, including its
|
||||
mode, with possible values `AP` and `managed`. The above test succeeds only if
|
||||
the interface is configured as an Access Point. And Systemd services fail if any
|
||||
of the ExecStartPre instructions fail. The drawback of this setup is that the
|
||||
service will simply appear as failed in journalctl, with no explanation.
|
||||
|
||||
Copy the snippet to the given location, reboot. If you can't ping the RPi on the
|
||||
local network, look for it in the available APs. Connect to it, ssh it. You're
|
||||
done !
|
||||
|
||||
|
||||
[aw-arm-install]: https://archlinuxarm.org/platforms/armv6/raspberry-pi
|
||||
[aw-ssh]: https://wiki.archlinux.org/index.php/OpenSSH
|
||||
[replace-hostapd]: https://raspberrypi.stackexchange.com/questions/94970/replacing-hostapd-with-wpa-supplicant
|
||||
[se-static-addr]: https://unix.stackexchange.com/questions/308944/systemd-networkd-default-static-address-if-dhcp-fails
|
|
@ -0,0 +1,62 @@
|
|||
+++
|
||||
title = "Local Name Resolution in Arch"
|
||||
date = 2020-09-26
|
||||
tags = ["admin"]
|
||||
+++
|
||||
It's quite handy to be able to access clients connected at the same Access Point
|
||||
as you using their hostname. There are a few technologies to do that : mostly,
|
||||
Apple's Bonjour translates as mDNS in the Linux world, then there is Microsoft's
|
||||
NetBIOS protocol, that is provided by samba under Linux but not running as a
|
||||
default. And Microsoft also created the LLMNR resolution protocol, that ships
|
||||
with Windows starting at Vista (and with 7, 8 and 10).
|
||||
|
||||
To have local name resolution, your best option is probably `systemd-resolved`,
|
||||
since it does both mDNS and LLMNR. On the other side, avahi does mDNS and lets
|
||||
you advertise specific services on the network. If you're interested in this you
|
||||
might want to go for it ; check [Archwiki's page on network
|
||||
configuration][aw-net-conf]. Here we will deal with `systemd-resolved`.
|
||||
|
||||
The first thing to do is to disable avahi services, since they interfere with
|
||||
`systemd-networkd`. Go on and
|
||||
```
|
||||
# systemctl disable avahi-daemon
|
||||
```
|
||||
Now we need to check how you do domain resolution on your computer. Under Linux
|
||||
this is dependent on who manages `/etc/resolv.conf`. If it's a symlink to some
|
||||
NetworkManager folder, it's NetworkManager. If it's linked to some Systemd
|
||||
folder, then it's `systemd-resolved`. If it is NetworkManager and you want to
|
||||
switch to `systemd-resolved` and keep NetworkManager to manage connections, just
|
||||
do :
|
||||
```
|
||||
# ln -sf /run/systemd/resolve/stub-resolve.conf /etc/resolv.conf
|
||||
```
|
||||
It may also be a real file, meaning your computer uses static IP addresses for
|
||||
DNS servers. In this case you can do the same (back it up if you will).
|
||||
|
||||
Edit `/etc/systemd/resolved.conf` so that inside of the `[Resolve]` section, you
|
||||
have `MulticastDNS=yes` and `LLMNR=yes`. After that, you will have to
|
||||
```
|
||||
# systemctl enable systemd-resolved
|
||||
```
|
||||
And reboot.
|
||||
|
||||
Now run `resolvectl` to see how it goes. If your network interface doesn't
|
||||
specifically allow MulticastDNS, you will have to tell the network manager to do
|
||||
it. If it's `systemd-networkd`, you will need to add the very same option in the
|
||||
relevant `.network` files in `/etc/systemd/network/`. If it's NetworkManager, no
|
||||
luck, you will have to update each and every connection by adding `mdns=2` to
|
||||
the `[connection]` section of the connection definition in
|
||||
`/etc/NetworkManager/system-connections`.
|
||||
|
||||
If you changed anything, you might want to reboot again. Then try
|
||||
```
|
||||
$ resolvectl query SOMEHOSTNAME.local
|
||||
```
|
||||
SOMEHOSTNAME being the name of a device you know is connected. It should answer
|
||||
through mDNS. You may also try `resolvectl query SOMEHOSTNAME`, skipping the
|
||||
`.local` extension ; it should answer with LLMNR. Do note that Android devices
|
||||
don't seem to show up on the local network. But they do see declared neighbours
|
||||
; I suspect they ship with LLMNR since they're only able to `ping` names without
|
||||
the `.local` extension.
|
||||
|
||||
[aw-net-conf]: https://wiki.archlinux.org/index.php/Network_configuration#Local_network_hostname_resolution
|
|
@ -0,0 +1,137 @@
|
|||
+++
|
||||
title = "Arch on a Raspberry Pi - sound and MIDI"
|
||||
date = 2020-09-26
|
||||
tags = ["admin"]
|
||||
toc = true
|
||||
+++
|
||||
This post follows a first one on [basics of Arch onto a Raspberry
|
||||
Pi][arch-basics]. Now we will see how to have high quality, low latency sound
|
||||
and a MIDI keyboard on this beautiful but still useless system.
|
||||
|
||||
## MIDI keyboard with JACK
|
||||
|
||||
To reliably output sound using a MIDI keyboard and have a reasonable latency,
|
||||
consider dropping pulseaudio, installing and setting up JACK, and setting up
|
||||
fluidsynth to use JACK.
|
||||
|
||||
### Disable pulseaudio's autostart with systemd
|
||||
Run as the relevant user :
|
||||
|
||||
$ systemctl --user mask pulseaudio.socket
|
||||
|
||||
### Setup JACK
|
||||
|
||||
First install the `jack2` package, and enable it at login with :
|
||||
|
||||
$ systemctl --user enable jack@lowlatency.service
|
||||
|
||||
The `@` argument sets a JACK configuration file that will be searched in the
|
||||
following places, first match wins :
|
||||
|
||||
~/.config/jack/lowlatency.conf
|
||||
/etc/jack/lowlatency.conf
|
||||
|
||||
If you use a DAC, drop a second conf file in `~/.config/jack/MYDAC.conf`. For
|
||||
instance HifiBerry-style DACs are identified as device `hw:sndrpihifiberry`.
|
||||
In this case create `~/.config/jack/hifiberry.conf` as follows, and instead of
|
||||
the former, enable `jack@hifiberry.service` :
|
||||
|
||||
# The name of the JACK server
|
||||
JACK_DEFAULT_SERVER="default"
|
||||
# Options to JACK (e.g. -m, -n, -p, -r, -P, -t, -C, -u, -v)
|
||||
JACK_OPTIONS=""
|
||||
|
||||
# Audio backend (e.g. alsa, dummy, firewire, netone, oss, portaudio)
|
||||
DRIVER="alsa"
|
||||
# Device name (used by the audio backend) - defaults to "hw:0"
|
||||
DEVICE="hw:sndrpihifiberry"
|
||||
|
||||
# Specific settings for the audio backend in use
|
||||
DRIVER_SETTINGS="-n2 -p128 -r48000"
|
||||
|
||||
Note that `JACK_OPTIONS` is empty ; all the useful options go in
|
||||
`DEVICE_SETTINGS`. I suppose it's a bug of the JACK service file.
|
||||
|
||||
More about DACs at [the end of the post](#use-an-external-sound-card).
|
||||
|
||||
### Setup fluidsynth
|
||||
|
||||
Install the `fluidsynth` package ; it comes with a default user service that
|
||||
will connect to the JACK service if you tell it to in its conf file
|
||||
`~/.config/fluidsynth`. First do :
|
||||
```
|
||||
$ systemctl --user enable fluidsynth
|
||||
```
|
||||
You will need a soundfont in `/usr/share/soundfonts/default.sf2`. I actually
|
||||
made it a symbolical link to a soundfont file in my home dir. If you're
|
||||
desperate about soundfonts install the `soundfont-fluid` package.
|
||||
|
||||
JACK2 is said to need package `a2jmidid` to detect MIDI keyboards. I've noticed
|
||||
that in fact `fluidsynth` was very capable of detecting them on its own with the
|
||||
option `-o midi.autoconnect=1`, be it at boot or hotplugged.
|
||||
|
||||
A useful feature is that it can read a command file with the -f option ; an
|
||||
arguably preferable strategy is to create and load one in
|
||||
|
||||
/usr/share/soundfonts/fluidsynth.command
|
||||
|
||||
Finally my `~/.config/fluidsynth` reads as follows :
|
||||
|
||||
# Mandatory parameters (uncomment and edit)
|
||||
SOUND_FONT=/usr/share/soundfonts/default.sf2
|
||||
|
||||
# Additional optional parameters (may be useful, see 'man fluidsynth' for further info)
|
||||
# -C0 disables chorus
|
||||
# -R0 disables reverb
|
||||
# -g0.2 would be the default gain
|
||||
# -m can be jack, alsa, alsa_seq or raw ? jack should be more efficient
|
||||
# -K number of MIDI channels (minimum 16)
|
||||
# -f command file input
|
||||
OTHER_OPTS="-a jack -j -f /usr/share/soundfonts/fluidsynth.command -o midi.autoconnect=1 -o synth.polyphony=64"
|
||||
|
||||
Its systemd service's default target used to be "multi-user" whereas it should
|
||||
have been "default" (bug resolved as of version 2.1.5-2). One can now enable it
|
||||
with
|
||||
On older versions of the program, you had to do instead : `systemctl --user
|
||||
add-wants default fluidsynth`
|
||||
|
||||
## Autostart
|
||||
|
||||
If you want the systemd user services started at boot do _as root_ :
|
||||
```
|
||||
# loginctl enable-linger YOURUSERNAME
|
||||
```
|
||||
|
||||
## Deal with latency
|
||||
|
||||
To optimise latency, you will want to use a realtime process for jackd.
|
||||
|
||||
You can follow [instructions for Arch][aw-instr] to have the right to make your
|
||||
process prioritary on system tasks. Eventually, you should belong to the
|
||||
`realtime` group. After that jackd should detect it's able to run a realtime
|
||||
process and proceed. You can check that it does with
|
||||
```
|
||||
journalctl --user -u jack@hifiberry -e
|
||||
```
|
||||
(replace `hifiberry` with the config name you gave at the beginning of this
|
||||
post). The `-e` option jumps at the end of the logs.
|
||||
|
||||
I was only able to use the alsa-seq MIDI plugin with JACK, but some people
|
||||
report more effectiveness with jack's own plugin. You can check [Archwiki's
|
||||
JACK page][aw-jack-midi] for detailed instructions.
|
||||
|
||||
Another [Archwiki page on professional audio][aw-pro-audio] mentions a
|
||||
`-Xalsarawmidi` option in JACK used with a2jmidid to enhance MIDI jitter.
|
||||
|
||||
## Use an external sound card
|
||||
|
||||
For Phat DACs, follow these [instructions][dac-instr]. What's a Phat ? It's a
|
||||
small cardboard that fits above a Raspberry Pi, plugged into its IO pins. And a
|
||||
DAC ? Digital to Analog Converter - in other words, an audio card.
|
||||
|
||||
|
||||
[arch-basics]: {% post_url computing/2020-09-26-Arch-on-RPi %}
|
||||
[aw-instr]: https://wiki.archlinux.org/index.php/Realtime_process_management#PAM-enabled_login
|
||||
[aw-jack-midi]: https://wiki.archlinux.org/index.php/JACK_Audio_Connection_Kit#MIDI
|
||||
[aw-pro-audio]: https://wiki.archlinux.org/index.php/Professional_audio#MIDI
|
||||
[dac-instr]: https://www.hifiberry.com/docs/software/configuring-linux-3-18-x/
|
|
@ -0,0 +1,55 @@
|
|||
+++
|
||||
title = "disks backup"
|
||||
date = 2020-09-16
|
||||
tags = ["admin"]
|
||||
+++
|
||||
Backing up a whole disk is always quite sensible ; you rarely have the occasion
|
||||
to test if your backup really works. Besides, the logical architecture of a disk
|
||||
and its partitions can be quite sophisticated, with numerous different options,
|
||||
that vary much between operating systems. Here we will describe how to backup
|
||||
and restore a disk with some primary partitions. We will assume it is a bootable
|
||||
disk with a Master Boot Record at the very beginning of it (MBR is limited to
|
||||
446 bytes by conception). Note that most recent machines boot in UEFI mode,
|
||||
which is not covered here.
|
||||
|
||||
## Backup a bootable disk
|
||||
|
||||
Let's say your disk is called sdX.
|
||||
|
||||
### Backup Master Boot Record
|
||||
The MBR is a portion of bootable binary code. Do not try to read it with any
|
||||
text editor ! They may slightly modify it, rendering it unbootable.
|
||||
|
||||
$ dd if=/dev/sdX of=path/to/disk_backup.mbr bs=446 count=1
|
||||
|
||||
### Backup partition table
|
||||
|
||||
$ sfdisk -d /dev/sdX > path/to/disk_backup.partition_table.txt
|
||||
|
||||
### Backup useful partitions
|
||||
|
||||
For each partition (numbered Y) :
|
||||
|
||||
$ dd if=/dev/sdXY of=path/to/partition_backup.dd status=progress
|
||||
|
||||
Compressed variant :
|
||||
|
||||
$ dd if=/dev/sdXY status=progress | gzip > path/to/partition_backup.dd.gz
|
||||
|
||||
## Restore a backed up disk into sdX
|
||||
|
||||
Edit `path/to/disk_backup.partition_table.txt` to reflect the partitions you
|
||||
will actually restore. Then :
|
||||
|
||||
$ dd if=path/to/disk_backup.mbr of=/dev/sdX
|
||||
$ sfdisk /dev/sdX < path/to/disk_backup.partition_table.txt
|
||||
|
||||
You might have to eject and re-plug the disk to see the added
|
||||
partitions. Then for each of those :
|
||||
|
||||
$ zcat if=arch_zero_pY.dd.gz | dd of=/dev/sdXY
|
||||
|
||||
And finally restore MBR :
|
||||
|
||||
$ dd if=path/to/disk_backup.mbr of=/dev/sdX bs=446 count=1
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
+++
|
||||
title = "Access Point on a Raspberry Pi"
|
||||
date = 2020-09-26
|
||||
tags = ["admin"]
|
||||
+++
|
||||
The Raspberry Pi family who have wireless capabilities can all serve as wifi
|
||||
Access Points - meaning some other devices connect to it, perhaps accessing
|
||||
internet through it. We'll leave the "forward internet" part for another
|
||||
time, and concentrate on serving wifi connections as a hotspot.
|
||||
|
||||
If what you need is a "rescue" Access Point to be able to log back into the
|
||||
machine, you might rather check my other article on [RPi basics][arch-on-rpi],
|
||||
where you will see how to let `wpa_supplicant` fall back to being an Access
|
||||
Point if it can't connect to any.
|
||||
|
||||
And for now let's use `hostapd`, a package to implement and advertise a wifi
|
||||
access point. It might be a better option for production use since it's a
|
||||
dedicated tool, whereas `wpa_supplicant` is a bit out of its league here.
|
||||
|
||||
`hostapd` needs the interface to be up and configured with an IP. So we'll
|
||||
configure `systemd-networkd` to do that, and by the way propose an IP address to
|
||||
clients that get connected to our AP. Change
|
||||
`/etc/systemd/network/05-wlan.network` so that it reads :
|
||||
``` systemd
|
||||
[Network]
|
||||
Address=192.168.1.1
|
||||
DHCPServer=true
|
||||
IPMasquerade=true
|
||||
IPForward=true
|
||||
|
||||
[DHCPServer]
|
||||
PoolOffset=100
|
||||
PoolSize=20
|
||||
EmitDNS=true
|
||||
```
|
||||
Now `hostapd` won't work (at least in this configuration) if you have
|
||||
`wpa_supplicant` installed. Go on and
|
||||
```
|
||||
# pacman -Rs wpa_supplicant
|
||||
```
|
||||
And last, you will need to tweak `/etc/hostapd/hostapd.conf`. This one was taken
|
||||
from [Archwiki's][aw-ap], and curated for RPi zero W :
|
||||
```
|
||||
interface=wlan0
|
||||
#bridge=br0
|
||||
# Country code (ISO/IEC 3166-1)
|
||||
country_code=FR
|
||||
|
||||
# SSID to be used in IEEE 802.11 management frames
|
||||
ssid=zero
|
||||
wpa_passphrase=YOUR_PASSWORD_HERE
|
||||
|
||||
# Driver interface type (hostap/wired/none/nl80211/bsd) - default hostapd
|
||||
#driver=rtl871xdrv
|
||||
#driver=nl80211
|
||||
|
||||
# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz)
|
||||
# RPi zero W only supports b
|
||||
hw_mode=b
|
||||
# Channel number
|
||||
channel=5
|
||||
# Maximum number of stations allowed
|
||||
max_num_sta=5
|
||||
|
||||
# Bit field: bit0 = WPA, bit1 = WPA2
|
||||
wpa=2
|
||||
# Bit field: 1=wpa, 2=wep, 3=both
|
||||
auth_algs=1
|
||||
|
||||
# Set of accepted cipher suites; disabling insecure TKIP
|
||||
wpa_pairwise=CCMP
|
||||
# Set of accepted key management algorithms
|
||||
wpa_key_mgmt=WPA-PSK
|
||||
|
||||
# hostapd event logger configuration
|
||||
logger_stdout=-1
|
||||
# 0 = verbose debugging
|
||||
# 1 = debugging
|
||||
# 2 = informational messages
|
||||
# 3 = notification
|
||||
# 4 = warning
|
||||
logger_stdout_level=2
|
||||
|
||||
## QoS support
|
||||
#wmm_enabled=1
|
||||
## Use "iw list" to show device capabilities and modify ht_capab accordingly
|
||||
#ht_capab=[HT40+][SHORT-GI-40][TX-STBC][RX-STBC1][DSSS_CCK-40]
|
||||
ht_capab=[HT20][TX-STBC1][DSSS_CCK-40]
|
||||
```
|
||||
|
||||
If you use a Realtek-based wifi adapter, there is a specific hostapd package for
|
||||
hosts using rtl871 hardware. You will have to install it from the AUR ; you will
|
||||
have to
|
||||
```
|
||||
$ yay -S hostapd-rtl871xrdv
|
||||
```
|
||||
_And_ modify `/etc/hostapd/hostapd.conf` to mention `driver=rtl871xdrv`.
|
||||
|
||||
[arch-on-rpi]: {% post_url computing/2020-09-26-Arch-on-RPi %}
|
||||
[aw-ap]: https://wiki.archlinux.org/index.php/Software_access_point
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
+++
|
||||
title = "2020 review of Raspberry Pi alternatives"
|
||||
date = 2020-10-06
|
||||
tags = ["admin"]
|
||||
toc = true
|
||||
+++
|
||||
The Raspberry Pi is a cardboard-computer that lets you run your own operating
|
||||
system. Or put it the other way around, an open computer the size of a
|
||||
cigarettes pack (depending on what kind of cigarettes you refer to). But it's
|
||||
quite a barebone system, mostly lacking proper ADC and DAC, and battery
|
||||
management. Extensions allow to stack those features, but they're overall quite
|
||||
expensive. It might be time to consider alternatives to the rPi Zero in the same
|
||||
... hem league ?
|
||||
|
||||
## rPi Zero features
|
||||
The smallest form factor is the Raspberry Pi Zero at 60x32mm. The RPi Zero w is
|
||||
a wifi+bluetooth model for a 40% increase in price, topping at ... €11.
|
||||
|
||||
The rPi Zero roughly shares its processor with the initial rPi, that is a
|
||||
Broadcom BCM2835 SoC, with an ARMv6 core. Those were the first ARMs to support
|
||||
floating point calculations.
|
||||
|
||||
The various available models all lack an ADC and a proper DAC, although most do
|
||||
have an audio output that can do CD-like audio quality using PWM modulation -
|
||||
that is, pulsing alternative 0s and 1s so quickly it feels like an analog output
|
||||
on an average (see [this rPi post][rpi-audio] on rPi audio quality).
|
||||
|
||||
Power management is also quite barebone : you power them through a micro-USB
|
||||
port and they can't control a battery's charging state. You can plug a battery
|
||||
rescue pack to feed it, but then you will have to charge it separately, and if
|
||||
you ever want to craft a case too put it in, this will hardly fit into the same
|
||||
proportions.
|
||||
|
||||
Various constructors provide extensions called pHats that stack on the rPi using
|
||||
its GPIO pins. If you need a self-contained system with a battery and power
|
||||
control, you will need a pHat specialized in power management, like
|
||||
[pibat][pibat] that bills $26 for 1600mAh, or [pisugar2][pisugar2], very neat
|
||||
but $40 worth for 900mAh. Another interesting extension is the [UPS power
|
||||
expansion board][ups], hosting 1Ah for $20. The PiHut also sells a [LiPo
|
||||
shim][shim] to keep the functionality packed.
|
||||
|
||||
## Banana Pi M2
|
||||
Banana Pi obviously tries to challenge the features of the Raspberry Pi. The
|
||||
firm developed the [bPi M2][bpi-m2-wiki] that's a revamped version of the rPi
|
||||
Zero, featuring a ARMv7 quad-core processor, and a soft power-on button. It's
|
||||
$18 worth at [Aliexpress][bpi-m2].
|
||||
|
||||
## Arietta
|
||||
Acme Systems propose the [Arietta][acme-arietta]. This board sports the
|
||||
Microchip AT91SAM9G25 processor, based on the ARM926 core, which belongs to the
|
||||
ARMv5 series. Acme assures customers that it is guaranteed to run the latest
|
||||
linux kernel for quite long. It is supported by the ArchARM distribution. Acme's
|
||||
site offers a €25 basic model.
|
||||
|
||||
## NanoPi
|
||||
The [NanoPi NEO][nanopi] features an Allwinner H3 processor, with a quad-core
|
||||
cortex-A7 ARM core. It's 40x40mm large, and has an ethernet and a USB-A socket.
|
||||
It seems able to do basic power management, according to notes in
|
||||
[nanopi.io][nanopi.io]. [friendlyARM's wiki][nanopi-wiki] states that the nanopi
|
||||
has a builtin audio codec and that it should output sound on the lineout pins.
|
||||
Its price is reported between $10 and $50.
|
||||
|
||||
There also is a NanoPi AIR with wifi instead of ethernet and USB sockets,
|
||||
available for $20 at [friendlyARM][nanopi-air]. Specs are otherwise similar,
|
||||
it's just slimmer.
|
||||
|
||||
## Banana Pi D1
|
||||
But then there is the [bPi D1][bpi-d1-wiki]. This challenger is 38x38mm big, and
|
||||
ships with an HD camera, analog audio in and out pins, power control management
|
||||
and a 3.7V li-ion battery socket, a power switch and a user-definable soft
|
||||
button, RTC (basically, a clock that remembers time between boots), a
|
||||
microphone, and a CMOS image sensor (I don't even know what this means). I saw
|
||||
it at $20 at [Aliexpress][bpi-d1].
|
||||
|
||||
This is really a huge feature pack for such a small package. The question that
|
||||
rises is : what will we be able to run on this platform ? The processor is an
|
||||
Anyka AK3918, with an ARM chipset the generation just before rPi Zero, and a
|
||||
bunch of peripherals as stated in the [specs][ak3918] pdf description. Looking
|
||||
into it, I found out it uses the same ARM core (ARM926EJ is an ARM9, which is
|
||||
part of ARMv5) as the [Olinuxino][olinuxino] board from Olimex, and the same
|
||||
amount of RAM (64MB). The latter is supported in [ArchARM][olinuxino-archarm],
|
||||
with a caveat due to the limited RAM : localegen would need swap, so there's no
|
||||
UTF-8 support. I wonder if distcc would help here. Mainline Arch linux does
|
||||
provide a cross-platform distcc installation : `distccd-alarm-armv5`. But this
|
||||
doesn't prove Arch will run on Anyka's chip. ArchARM install instructions for
|
||||
various ARMv5 boards imply installing uboot in the boot sector. This should
|
||||
work, as it relies on pretty standard settings.
|
||||
|
||||
I don't know what to expect from the audio hardware capabilities either, since
|
||||
they're embedded into Anyka's chip, and probably differ from Olinuxino's i.MX233
|
||||
chip audio hardware. I'll keep this article up with any information I might get
|
||||
on this subject.
|
||||
|
||||
## Conclusion
|
||||
The bPi D1 seems to be the most fitted product, but there is no guarantee it
|
||||
will run your preferate OS. The NanoPi seems to be the next interesting choice,
|
||||
but then you will have to deal with power management. In the end, the rPi Zero
|
||||
is still a favorable choice, years and years after its design.
|
||||
|
||||
|
||||
[rpi-audio]: https://www.raspberrypi.org/forums/viewtopic.php?t=195178
|
||||
[pibat]: https://www.ebay.fr/itm/PiBat-Raspberry-Pi-Zero-W-1600mAh-LiPo-Battery-Shield-5-6-Hours-of-autonomy/283842482176
|
||||
[pisugar2]: https://www.tindie.com/products/pisugar/pisugar2-battery-for-raspberry-pi-zero/
|
||||
[ups]: https://www.alibaba.com/product-detail/Raspberry-Pi-Zero-UPS-Power-Expansion_62093794216.html
|
||||
[shim]: https://thepihut.com/products/lipo-shim?ref=isp_rel_prd&isp_ref_pos=2
|
||||
[nanopi]: https://www.alibaba.com/product-detail/Open-Source-H3-Quad-core-Cortex_62093676606.html
|
||||
[nanopi.io]: http://nanopi.io/nanopi-neo-air.html
|
||||
[nanopi-air]: https://www.friendlyarm.com/index.php?route=product/product&path=69&product_id=151
|
||||
[nanopi-wiki]: http://wiki.friendlyarm.com/wiki/index.php/NanoPi_NEO_Air#Custom_welcome_message
|
||||
[bpi-m2-wiki]: http://wiki.banana-pi.org/Getting_Started_with_M2_Zero
|
||||
[bpi-m2]: https://www.aliexpress.com/item/32839074880.html
|
||||
[bpi-d1-wiki]: http://wiki.banana-pi.org/Banana_PI_D1
|
||||
[bpi-d1]: https://www.aliexpress.com/item/32240699956.html
|
||||
[ak3918]: https://drive.google.com/file/d/0B4PAo2nW2KfnbGJpa19FWWpwWDQ/view
|
||||
[olinuxino]: https://www.olimex.com/wiki/IMX233
|
||||
[olinuxino-archarm]: https://archlinuxarm.org/platforms/armv5/olinuxino
|
||||
[acme-arietta]: https://www.acmesystems.it/arietta
|
|
@ -0,0 +1,75 @@
|
|||
+++
|
||||
title = "SVG"
|
||||
date = 2020-04-13
|
||||
tags = ["html"]
|
||||
+++
|
||||
Pour intégrer un fichier SVG et pouvoir le redimensionner, on peut le mettre
|
||||
dans un conteneur HTML. Il va s'arranger pour tout afficher et centrer le
|
||||
résultat, comme ça :
|
||||
|
||||
``` html
|
||||
<span class="svgContainer" style="background-color: darkgrey;">
|
||||
<svg viewbox="0 0 100 100" width="20em" height="10em">
|
||||
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
|
||||
</svg>
|
||||
</span>
|
||||
```
|
||||
Centrage vertical :
|
||||
|
||||
``` html
|
||||
<span class="svgContainer" style="background-color: darkgrey;">
|
||||
<svg viewbox="0 0 100 100" width="10em" height="20em">
|
||||
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
|
||||
</svg>
|
||||
</span>
|
||||
```
|
||||
|
||||
Si on veut qu'il "stretche" sur son conteneur, on peut ajouter l'option
|
||||
`preserveAspectRatio = none` :
|
||||
|
||||
``` html
|
||||
<span id="svgContainer">
|
||||
<svg viewbox="0 0 100 100" width="20em" height="10em"
|
||||
preserveAspectRatio="none">
|
||||
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
|
||||
</svg>
|
||||
</span>
|
||||
```
|
||||
|
||||
# Position et taille
|
||||
|
||||
L'élément SVG a des attributs transitionables, comme `svg.width` et `svg.height`,
|
||||
`svg.style.left` et `svg.style.top`. En utilisant le mode `position = relative`,
|
||||
on peut déplacer l'élément tout en gardant sa place initiale réservée, comme si
|
||||
rien n'avait changé :
|
||||
|
||||
``` html
|
||||
<span class="svgContainer" style="background-color: darkgrey;">
|
||||
<svg viewbox="0 0 100 100" width="10em" height="10em"
|
||||
style="position: relative; left: -3em; top: 1.5em;">
|
||||
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
|
||||
</svg>
|
||||
</span>
|
||||
```
|
||||
|
||||
On peut enfin utiliser le mode `position = absolute`, où le conteneur est
|
||||
arraché au flot du document. Il est positionné relativement à son plus proche
|
||||
parent positionné. La plupart des éléments ont par défaut `position = static`,
|
||||
qui la laisse à sa place dans le flot du document. Donc le positionnement se
|
||||
fait le plus souvent par rapport à l'élément `<body>`.
|
||||
|
||||
Ici l'élément est `absolute` et son parent direct `relative`. On remarque que le
|
||||
span svgContainer a maintenant une hauteur nulle ; en fait en position
|
||||
`absolute`, le svg n'est plus contenu dans svgContainer mais dans `<body>` ;
|
||||
svgContainer est donc vide.
|
||||
|
||||
``` html
|
||||
<span class="svgContainer"
|
||||
style="background-color: darkgrey; position: relative">
|
||||
<svg viewbox="0 0 100 100" width="10em" height="10em"
|
||||
style="position: absolute; left: 35vw; top: -4vw;">
|
||||
<path fill="magenta" stroke="black" d="M0,0 H100 V100 H0 Z">
|
||||
</svg>
|
||||
</span>
|
||||
```
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
+++
|
||||
title = "transitions II - javascript"
|
||||
date = 2020-04-13
|
||||
# layout: code
|
||||
+++
|
||||
<script>
|
||||
function move_into(el, destEl) {
|
||||
let destStyle = window.getComputedStyle(destEl);
|
||||
let destHeight = destStyle.height;
|
||||
let startPos = el.getBoundingClientRect();
|
||||
console.log('start point', startPos.left, startPos.top);
|
||||
|
||||
/*
|
||||
destEl.style.backgroundColor = "grey";
|
||||
destEl.style.position = "relative";
|
||||
destEl.style.height = destEl.offsetHeight + "px";
|
||||
destEl.style.width = destEl.offsetWidth + "px";
|
||||
destEl.style.transition = "all 1s";
|
||||
void destEl.offsetLeft;
|
||||
//destEl.focus();
|
||||
|
||||
// DEBUG Trigger transition on destination ?
|
||||
destEl.appendChild(el);
|
||||
*/
|
||||
|
||||
destEl.appendChild(el);
|
||||
destFinalHeight = destEl.offsetHeight;
|
||||
console.log(destHeight, destFinalHeight);
|
||||
destEl.style.transition = "0s";
|
||||
destEl.style.height = destHeight;
|
||||
void destEl.offsetHeight;
|
||||
destEl.style.transition = "1s";
|
||||
destEl.style.height = destFinalHeight + "px";
|
||||
|
||||
let endPos = el.getBoundingClientRect();
|
||||
console.log("way to go", endPos.left-startPos.left, endPos.top-startPos.top);
|
||||
|
||||
// We are in final position, we need to move back to inital pos.
|
||||
el.style.position = "relative";
|
||||
el.style.left = startPos.left - endPos.left + "px";
|
||||
el.style.top = startPos.top - endPos.top + "px";
|
||||
|
||||
// Let javascript notice current state :
|
||||
void el.offsetLeft;
|
||||
|
||||
el.addEventListener('transitionend', function log() {
|
||||
el.removeEventListener('transitionend', log);
|
||||
console.log("après", destEl.offsetHeight);
|
||||
});
|
||||
// Trigger transition : move to final position.
|
||||
el.style.transition = "all 1s";
|
||||
el.style.left = "0";
|
||||
el.style.top = "0";
|
||||
}
|
||||
</script>
|
||||
|
||||
On veut maintenant déplacer un div dans un autre avec une transition sur le
|
||||
déplacement. Ppur ça, on va dans l'ordre :
|
||||
- récupérer les coordonnées de départ par rapport à la racine du document
|
||||
- arracher l'élément et le mettre dans le conteneur d'arrivée
|
||||
- le basculer en position relative et lui passer les coodonnées de départ
|
||||
- déclencher le déplacement jusqu'à sa position dans le conteneur d'arrivée
|
||||
|
||||
{% highlight js linenos %}
|
||||
function move_into(el, destEl) {
|
||||
let startPos = el.getBoundingClientRect();
|
||||
console.log('start point', startPos.left, startPos.top);
|
||||
|
||||
destEl.appendChild(el);
|
||||
let endPos = el.getBoundingClientRect();
|
||||
console.log("way to go", endPos.left-startPos.left, endPos.top-startPos.top);
|
||||
|
||||
// We are in final position, we need to move back to inital pos.
|
||||
el.style.position = "relative";
|
||||
el.style.left = startPos.left - endPos.left + "px";
|
||||
el.style.top = startPos.top - endPos.top + "px";
|
||||
el.style.transition = "all 1s";
|
||||
|
||||
// Let javascript notice current state :
|
||||
void el.offsetLeft;
|
||||
|
||||
// Trigger transition : move to final position.
|
||||
el.style.left = "0";
|
||||
el.style.top = "0";
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Si vous lisez l'anglais, ce qu'on vient de voir devrait être assez lisible. Le
|
||||
plus obscur est certainement l'utilisation de `void`. Ligne 16, On utilise la
|
||||
commande
|
||||
|
||||
{% highlight js %}
|
||||
void el.offsetLeft;
|
||||
{% endhighlight %}
|
||||
|
||||
pour obliger javascript à constater l'état de
|
||||
départ, avant de rentrer les coordonnées d'arrivée. En effet, sinon javascript
|
||||
ne voit pas le changement de valeur des coordonnées, il ne voit que la valeur
|
||||
finale. Et dans ce cas, pas d'animation de la transition, on va directement aux
|
||||
valeurs finales. Voilà le fragment de html et sa prévisualisation :
|
||||
|
||||
``` html
|
||||
<style>
|
||||
.half {
|
||||
display: inline-block;
|
||||
width: 45%;
|
||||
}
|
||||
.large {
|
||||
height: 8em;
|
||||
}
|
||||
</style>
|
||||
<div class="half">
|
||||
<button onclick="move_into(fleche, cible)">
|
||||
bouger la flèche
|
||||
</button>
|
||||
<div id="cible">
|
||||
texte dans la cible
|
||||
</div>
|
||||
</div>
|
||||
<div class="half">
|
||||
<div class="large">
|
||||
un div pour prendre un peu de place
|
||||
</div>
|
||||
<div id="fleche" class="large">
|
||||
texte dans la flèche
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Je dois dire que je trouve assez frustrant de ne pas pouvoir déclencher les
|
||||
transitions sur les éléments impactés. J'ai essayé, je n'ai rien trouvé de
|
||||
concluant.
|
||||
|
||||
[la suite]({% post_url html/2020-04-13-transition-3 %})
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
+++
|
||||
title = "transitions III - css"
|
||||
date = 2020-04-13
|
||||
# layout: code
|
||||
+++
|
||||
<script src="{% link html/transition-3.js %}" type="text/javascript"></script>
|
||||
|
||||
On a la possibilité de donner des réglages par défaut à nos transitions en css ;
|
||||
ça permet d'alléger le javascript, et de contrôler les paramètres de la
|
||||
transition depuis le css.
|
||||
|
||||
Ici `move_into(el, destEl)` va utiliser une classe qui durera le temps de la
|
||||
transition, qui s'appelera `transition-deplacement`. On pourra y stocker des
|
||||
valeurs par défaut pour la transition. Par exemple,
|
||||
|
||||
{% highlight js %}
|
||||
el.style.top = "";
|
||||
{% endhighlight %}
|
||||
|
||||
donnera à l'élément `el` la valeur `top` décrite dans le css. Comme `el`
|
||||
appartient à la classe `transition-deplacement` qui dit que `top = 0;` eh ben
|
||||
voilà.
|
||||
|
||||
{% highlight js %}
|
||||
function move_into(el, destEl) {
|
||||
if(el.classList.contains('transition-deplacement')) {
|
||||
console.log('! already movin');
|
||||
} else {
|
||||
el.classList.add('transition-deplacement');
|
||||
|
||||
// Do the job
|
||||
|
||||
el.addEventListener('transitionend', function declasse() {
|
||||
el.removeEventListener('transitionend', declasse);
|
||||
el.classList.remove('transition-deplacement');
|
||||
console.log("élément", el, "déclassé");
|
||||
});
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Pour finir, `toggle(el, destEl)` utilisera une autre classe pour déterminer si
|
||||
`el` a déjà été ouvert, la classe `ouvert`. Si oui, il le mettra dans le parent
|
||||
direct de `destEl`.
|
||||
|
||||
Dans ce cas, on n'utilise même pas cette classe dans le css. Elle ne sert qu'à
|
||||
javascript.
|
||||
|
||||
{% highlight js %}
|
||||
function toggle(el, destEl) {
|
||||
if(el.classList.contains('ouvert')) {
|
||||
el.classList.remove('ouvert');
|
||||
move_into(el, destEl.parentElement);
|
||||
} else {
|
||||
el.classList.add('ouvert');
|
||||
move_into(el, destEl);
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Tous ces points sont dans le fichier javascript inclus dans la page à partir de
|
||||
[là]({% link html/transition-3.js %}). Et maintenant le html et la
|
||||
prévisualisation :
|
||||
``` html
|
||||
<style>
|
||||
.transition-deplacement {
|
||||
position: relative;
|
||||
transition: 1s;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.min {
|
||||
display: inline-block;
|
||||
width: 10em;
|
||||
height: 5em;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<button onclick="toggle(fleche, cible)">
|
||||
bouger le div 1
|
||||
</button>
|
||||
<div id="cible" class="min">
|
||||
texte de la cible
|
||||
</div>
|
||||
<div class="min">
|
||||
un div pour prendre un peu de place
|
||||
</div>
|
||||
<div id="fleche" class="min">
|
||||
texte de la flèche
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
[la suite]({% post_url html/2020-04-13-transition-4 %})
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
+++
|
||||
title = "transition IV - SVG"
|
||||
date = 2020-04-13
|
||||
# layout: code
|
||||
+++
|
||||
<script src="{% link html/transition-4.js %}" type="text/javascript"></script>
|
||||
|
||||
Et si c'est un objet SVG qu'on veut déplacer ? Ça fonctionne encore, en
|
||||
utilisant `svg.getBoundingClientRect()` pour récupérer la position, et la même
|
||||
fonction pour initialiser la position. Pour clarifier, on crée des fonctions qui
|
||||
font le tri en fonction du type de l'élément : `initialize_state(el)` et
|
||||
`tell_coords(el)`.
|
||||
|
||||
{% highlight js %}
|
||||
function tell_coords(el) {
|
||||
var coords = new Object();
|
||||
if(el.viewBox === undefined) {
|
||||
coords["x"] = el.offsetLeft;
|
||||
coords["y"] = el.offsetTop;
|
||||
} else {
|
||||
let geom = el.getBoundingClientRect();
|
||||
coords["x"] = geom.left;
|
||||
coords["y"] = geom.top;
|
||||
}
|
||||
return coords;
|
||||
}
|
||||
function initialize_state(el) {
|
||||
if(el.viewBox === undefined) {
|
||||
void el.offsetWidth;
|
||||
} else {
|
||||
el.getBoundingClientRect();
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Ensuite `move_into(el, destEl)` fait le même travail qu'avant, mais
|
||||
en appelant les fonctions qu'on a construites.
|
||||
|
||||
{% highlight js %}
|
||||
function move_into(el, destEl) {
|
||||
if(el.classList.contains('transition-deplacement')) {
|
||||
console.log('! already movin');
|
||||
} else {
|
||||
el.classList.add('transition-deplacement');
|
||||
let coords_depart = tell_coords(el);
|
||||
console.log('déplacement depuis', el.parentElement);
|
||||
//console.log('déplacement depuis ', coords_depart);
|
||||
|
||||
destEl.appendChild(el);
|
||||
el.style.position = "relative";
|
||||
let coords_arrivee = tell_coords(el);
|
||||
console.log("destination", destEl);
|
||||
//console.log("destination ", coords_arrivee);
|
||||
|
||||
el.style.transition = "all 0s";
|
||||
el.style.left = coords_depart["x"] - coords_arrivee["x"] + "px";
|
||||
el.style.top = coords_depart["y"] - coords_arrivee["y"] + "px";
|
||||
el.addEventListener('transitionend', function declasse() {
|
||||
el.removeEventListener('transitionend', declasse);
|
||||
el.classList.remove('transition-deplacement');
|
||||
console.log("élément", el, "déclassé");
|
||||
});
|
||||
initialize_state(el);
|
||||
el.style.transition = ""; // Reset to css value.
|
||||
el.style.left = "";
|
||||
el.style.top = "";
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Le javascript inclus complet est toujours [là][js].
|
||||
Enfin le html :
|
||||
|
||||
```html
|
||||
<style>
|
||||
span {
|
||||
display: inline-block;
|
||||
}
|
||||
.minimum {
|
||||
width: 10em;
|
||||
height: 3em;
|
||||
}
|
||||
.transition-deplacement {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: 1s;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<button onclick="toggle(fleche, cible)">bouger</button>
|
||||
<span class="minimum">
|
||||
un span pour prendre un peu de place
|
||||
</span>
|
||||
<span id="cible" class="minimum">
|
||||
texte du span d'arrivée
|
||||
</span>
|
||||
<svg id="fleche" viewBox="0 0 100 100">
|
||||
<path d="M0,0 H100 V100 H0 Z" fill="transparent" stroke="black" />
|
||||
</svg>
|
||||
</div>
|
||||
```
|
||||
[js]: {% link html/transition-4.js %}
|
|
@ -0,0 +1,60 @@
|
|||
+++
|
||||
tite = "transition"
|
||||
date = 2020-04-13
|
||||
# layout: code
|
||||
+++
|
||||
<script>
|
||||
function move_span() {
|
||||
let span = document.querySelector("#movableSpan");
|
||||
if(span.classList.contains('slided')) {
|
||||
span.classList.remove('slided');
|
||||
} else {
|
||||
span.classList.add('slided');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Voilà un sujet délicat. Les transitions permettent de changer un paramètre de
|
||||
manière progressive sur un temps donné.
|
||||
|
||||
Pour créer ces effets avec accélération matérielle, il faut les écrire comme
|
||||
effets css. Il y a pour ça les paramètres `transitionProperty` et
|
||||
`transitionDuration`, avec le paramètre abrégé `transition`. La transition est
|
||||
déclenchée la prochaine fois qu'un élément prend la classe impliquée. Par
|
||||
exemple :
|
||||
|
||||
``` html
|
||||
<script>
|
||||
function move_span(span) {
|
||||
let span = document.querySelector("#movableSpan");
|
||||
if(span.classList.contains('slided')) {
|
||||
span.classList.remove('slided');
|
||||
} else {
|
||||
span.classList.add('slided');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
#movableSpan {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: 1s;
|
||||
}
|
||||
.slided {
|
||||
transition: 1s;
|
||||
top: 5em !important;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<button onclick="move_span()">bouger le span 1</button>
|
||||
<span id="movableSpan" >
|
||||
texte du span 1
|
||||
</span>
|
||||
<span>
|
||||
texte du span 2
|
||||
</span>
|
||||
</div>
|
||||
```
|
||||
[la suite]({% post_url html/2020-04-13-transition-2 %})
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
+++
|
||||
title = "Language Server Protocols in Vim"
|
||||
date = 2020-09-16
|
||||
tags = ["admin", "vim"]
|
||||
+++
|
||||
Language Server Protocol aka LSP is an open source initiative of ... Microsoft.
|
||||
Each "language server" defines every detail of a programming language's syntax
|
||||
and grammar, like its keywords, and how you put them together. This knowledge
|
||||
base is in turn used by editor helpers who can for instance lint your code or
|
||||
suggest completion, a field of features labeled "Intellisense".
|
||||
|
||||
Since Vim 8, programmers may run asynchronous code in vimscript. This feature is
|
||||
leveraged in some plugins that use LSP to lint or suggest while you type. Let's
|
||||
name [vim-lsp][vim-lsp], [coc.vim][coc.vim] and [vim-ale][vim-ale].
|
||||
|
||||
## Vim plugins on your own
|
||||
|
||||
It is worth noting that coc.vim provides a language server installer (vim-lsp
|
||||
also has `vim-lsp-settings` that _should_ do the job, although I was not able to
|
||||
run it on my system). It is very likely that your operating system chips some,
|
||||
at least if it's a linux. _But_ if you already have Vim running on your OS, it
|
||||
should be quite simple to use one of the formers with a plugin manager. If you
|
||||
don't have any plugin manager, be advised that Vim 8 ships its own plugin system
|
||||
; you just need to unpack the plugin's main directory in
|
||||
`~/.vim/pack/PACK_NAME/start/`. `PACK_NAME` can be anything you want, and its
|
||||
subdirectory `start` can be filled with as many plugins as you please. In this
|
||||
case, you could do for instance :
|
||||
```
|
||||
$ mkdir -p ~/.vim/pack/LSP/start
|
||||
$ cd ~/.vim/pack/LSP/start
|
||||
$ git clone https://github.com/neoclide/coc.nvim
|
||||
```
|
||||
And that's it ! (If you don't have git neither a terminal but still have Vim,
|
||||
you can still go at [coc.vim][coc.vim] and manually unpack the latest version's
|
||||
tarball you Vim's config dir - `pack` should lie next to `ftplugin` et al).
|
||||
|
||||
## Vim plugins in your package manager
|
||||
|
||||
You might prefer to use your system's package manager to install plugins, so
|
||||
that they get upgraded with the rest of the system. In this case you could do
|
||||
for instance in [Arch linux](http://archlinux.org) :
|
||||
```
|
||||
# pacman -S vim-ale
|
||||
```
|
||||
Once properly set up, vim-ale will lint the file you are editing in the
|
||||
background (using Vim 8 async processes). It will send all errors and warnings
|
||||
found while you type to the location window, a temporary "subwindow" that you
|
||||
can check back with `:lw`. See `:help location-list-window` to get more info on
|
||||
this feature. You can jump to the last diagnosed error with `:ll`, and navigate
|
||||
with `:lla` and `:lne`.
|
||||
|
||||
The nice bonus is the completion option. It lists all matching expressions with
|
||||
what you began typing, and as you scroll the list, Vim's `preview-window` pops
|
||||
up and gives a description of whatever you are coding - objects in python,
|
||||
functions in bash, etc.
|
||||
|
||||
I'm afraid vim-ale on its own doesn't know much about any language at all. You
|
||||
will have to feed it with a knowledge provider for each of the languages you
|
||||
care for. See the next chapter about this.
|
||||
|
||||
Meanwhile, I tweaked vim-ale to populate Vim's default completion sequence,
|
||||
remap completion shortcuts, and let the preview window be opened at the bottom
|
||||
with the following content into `~/.vim/plugin/ale.vim` :
|
||||
``` vimscript
|
||||
" vim-ale settings
|
||||
|
||||
" Enable ale completion where available.
|
||||
" This setting must be set before ALE is loaded.
|
||||
let g:ale_completion_enabled = 1
|
||||
let g:ale_completion_autoimport = 1
|
||||
|
||||
" Lets ctrl-x ctrl-o open ale completion
|
||||
set omnifunc=ale#completion#OmniFunc
|
||||
|
||||
" Remap tab, shift-tab and enter to useful completion shortcuts when
|
||||
" completion already began :
|
||||
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"
|
||||
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
|
||||
inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<cr>"
|
||||
|
||||
" Let preview window open on the bottom (J)
|
||||
augroup previewWindowPosition
|
||||
au!
|
||||
autocmd BufWinEnter * call PreviewWindowPosition()
|
||||
augroup END
|
||||
function! PreviewWindowPosition()
|
||||
if &previewwindow
|
||||
wincmd J
|
||||
endif
|
||||
endfunction
|
||||
```
|
||||
|
||||
## Installing language servers
|
||||
|
||||
You could install your favourite language's language server. For example under
|
||||
Arch :
|
||||
|
||||
# pacman -S bash-language-server
|
||||
|
||||
And then let Vim know about it. Create `~/.vim/plugin/language-servers.vim` and
|
||||
paste :
|
||||
|
||||
``` vimscript
|
||||
" Support for bash
|
||||
if executable('bash-language-server')
|
||||
augroup LspBash
|
||||
autocmd!
|
||||
autocmd User lsp_setup call lsp#register_server({
|
||||
\ 'name': 'bash-language-server',
|
||||
\ 'cmd': {server_info->[&shell, &shellcmdflag, 'bash-language-server start']},
|
||||
\ 'allowlist': ['sh'],
|
||||
\ })
|
||||
augroup END
|
||||
endif
|
||||
```
|
||||
Arch users also get python, C/C++ (in the `ccls` package), and LaTeX language
|
||||
servers in community. I also found `rls-git` for Rust language server, HTML from
|
||||
Microsoft's VSCode, and javascript with `typescript-language-server-bin` in the
|
||||
AUR.
|
||||
|
||||
vim-lsp maintains a list of usual Vim LSP support scripts [on its
|
||||
wiki][vim-lsp-support]. Once you've picked up your choice and installed it it's
|
||||
mostly a matter of copy and paste.
|
||||
|
||||
Well, all this sounded quite systematic ! But ... vim-ale also maintains a list
|
||||
of working linters [here][vim-ale-linters]. And it turns out that before
|
||||
language servers, there was quite a bunch of linters doing the job all right.
|
||||
And vim-ale is able to run those "traditional" linters while you type. And they
|
||||
allow completion while you type. They also bear a smaller footprint. This
|
||||
discussion could go more into the details of each language support, but finally
|
||||
why go for the big overhead of this open source infrastructure ?
|
||||
|
||||
I ended up replacing language servers as follows :
|
||||
|
||||
| language | LSP implementation | linter |
|
||||
|---|---|---|
|
||||
| bash | `bash-language-server` | `shellcheck` |
|
||||
| C/C++ | ? | `ccls` |
|
||||
| css | `vscode-css-languageserver-bin` | `prettier` |
|
||||
| javascript | `typescript-language-server-bin` | `typescript`'s tsserver |
|
||||
| json | `vscode-json-language-server` | `prettier` |
|
||||
| html | `vscode-html-languageserver-bin` | `htmlhint` |
|
||||
| markdown | ? | `mdl` |
|
||||
| python | `python-language-server` | `autopep8` |
|
||||
| vala | `vala-language-server` | `uncrustify` |
|
||||
| vimscript | `vim-language-server` | `vint` |
|
||||
|
||||
[vim-ale]: https://github.com/dense-analysis/ale
|
||||
[vim-ale-linters]: https://github.com/dense-analysis/ale/blob/master/supported-tools.md
|
||||
[vim-lsp]: https://github.com/prabirshrestha/vim-lsp
|
||||
[vim-lsp-support]: https://github.com/prabirshrestha/vim-lsp/wiki/Servers
|
||||
[coc.vim]: https://github.com/neoclide/coc.nvim
|
|
@ -0,0 +1,30 @@
|
|||
+++
|
||||
title = "Vim resources"
|
||||
date = 2020-09-17
|
||||
tags = ["admin", "vim"]
|
||||
+++
|
||||
I'll be dropping links to pages proposing interesting insights of Vim in here.
|
||||
|
||||
Let's start with a very classical one : [Learn Vimscript the Hard Way][hardway]
|
||||
by Steve Losh, that I find very useful to give hints when you know precisely
|
||||
what you want to do. I also dig the spirit.
|
||||
|
||||
Then there is the cheatsheet approach. I bookmarked Rico's
|
||||
[devhints.io][devhints] page on Vimscript, that I come back to when there's a
|
||||
need. There's also this [regex tutorial][regex] that is nice to bookmark, as
|
||||
regex are really something you're not in a hurry to master.
|
||||
|
||||
Oh and then there's vimways.org's [quickfix shortcuts][quickfix] that introduces
|
||||
the thing with its sarcastic tone, quite a journey.
|
||||
|
||||
Lastly I also found [this useful post][jakobgm] on integrating git into vim.
|
||||
Then I remembered I had found this wizard's way of teaching
|
||||
[operators][operators] in Vim, coming out with comments on orthogonality between
|
||||
operators and motions.
|
||||
|
||||
[hardway]: https://learnvimscriptthehardway.stevelosh.com/
|
||||
[devhints]: https://devhints.io/vimscript-functions
|
||||
[regex]: https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
|
||||
[quickfix]: https://vimways.org/2018/colder-quickfix-lists/
|
||||
[jakobgm]: https://jakobgm.com/posts/vim/git-integration/
|
||||
[operators]: https://whileimautomaton.net/2008/11/vimm3/operator
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: "Search"
|
||||
---
|
||||
|
||||
{{<search>}}
|
|
@ -0,0 +1,48 @@
|
|||
function move_into(el, destEl) {
|
||||
if(el.classList.contains('transition-deplacement')) {
|
||||
console.log('! already movin');
|
||||
} else {
|
||||
|
||||
el.classList.add('transition-deplacement');
|
||||
|
||||
let startPos = el.getBoundingClientRect();
|
||||
console.log('déplacement depuis', startPos.left, startPos.top);
|
||||
|
||||
destEl.appendChild(el);
|
||||
let endPos = el.getBoundingClientRect();
|
||||
console.log("destination", endPos.left, endPos.top);
|
||||
|
||||
// Remove transitional class on completion.
|
||||
el.addEventListener('transitionend', function declasse() {
|
||||
console.log("élément", el, "déclassé");
|
||||
el.removeEventListener('transitionend', declasse);
|
||||
el.classList.remove('transition-deplacement');
|
||||
});
|
||||
|
||||
// Move to initial position, coords from final position :
|
||||
console.log(
|
||||
startPos.left - endPos.left + "px",
|
||||
startPos.top - endPos.top + "px");
|
||||
el.style.transition = "0s";
|
||||
el.style.left = startPos.left - endPos.left + "px";
|
||||
el.style.top = startPos.top - endPos.top + "px";
|
||||
|
||||
// Let javascript notice current state.
|
||||
void el.offsetLeft;
|
||||
|
||||
// Trigger transition : move to final position.
|
||||
el.style.transition = "";
|
||||
el.style.left = "";
|
||||
el.style.top = "";
|
||||
}
|
||||
}
|
||||
function toggle(el, destEl) {
|
||||
if(el.classList.contains('ouvert')) {
|
||||
el.classList.remove('ouvert');
|
||||
move_into(el, destEl.parentElement);
|
||||
} else {
|
||||
el.classList.add('ouvert');
|
||||
move_into(el, destEl);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
function tell_coords(el) {
|
||||
var coords = new Object();
|
||||
if(el.viewBox === undefined) {
|
||||
coords.left = el.offsetLeft;
|
||||
coords.top = el.offsetTop;
|
||||
} else {
|
||||
let geom = el.getBoundingClientRect();
|
||||
coords.left = geom.left;
|
||||
coords.top = geom.top;
|
||||
}
|
||||
return coords;
|
||||
}
|
||||
function initialize_state(el) {
|
||||
if(el.viewBox === undefined) {
|
||||
void el.offsetWidth;
|
||||
} else {
|
||||
el.getBoundingClientRect();
|
||||
}
|
||||
}
|
||||
function move_into(el, destEl) {
|
||||
if(el.classList.contains('transition-deplacement')) {
|
||||
console.log('! already movin');
|
||||
} else {
|
||||
el.classList.add('transition-deplacement');
|
||||
let startPos = tell_coords(el);
|
||||
console.log('déplacement depuis', el.parentElement);
|
||||
//console.log('déplacement depuis ', startPos);
|
||||
|
||||
destEl.appendChild(el);
|
||||
let endPos = tell_coords(el);
|
||||
console.log("destination", destEl);
|
||||
//console.log("destination ", endPos);
|
||||
|
||||
el.style.transition = "all 0s";
|
||||
el.style.left = startPos.left - endPos.left + "px";
|
||||
el.style.top = startPos.top - endPos.top + "px";
|
||||
el.addEventListener('transitionend', function declasse() {
|
||||
el.removeEventListener('transitionend', declasse);
|
||||
el.classList.remove('transition-deplacement');
|
||||
console.log("élément", el, "déclassé");
|
||||
});
|
||||
|
||||
initialize_state(el);
|
||||
el.style.transition = ""; // Reset to css value.
|
||||
el.style.left = "";
|
||||
el.style.top = "";
|
||||
}
|
||||
}
|
||||
function toggle(el, destEl) {
|
||||
if(el.classList.contains('ouvert')) {
|
||||
//move_into(depart, arrivee.parentElement);
|
||||
el.classList.remove('ouvert');
|
||||
move_into(el, destEl.parentElement);
|
||||
} else {
|
||||
el.classList.add('ouvert');
|
||||
move_into(el, destEl);
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -0,0 +1 @@
|
|||
Subproject commit e7af87b290f47acb2a2463c3cb8bcbed4405713c
|
Loading…
Reference in New Issue