commit 55fd59840559477841d6fcbcbc7b0c9aea3d34a9 Author: lafleur Date: Thu Oct 8 23:07:35 2020 +0200 imported posts from jekyll diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6b38827 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "harbor"] + path = themes/harbor + url = https://github.com/matsuyoshi30/harbor diff --git a/archetypes/default.md b/archetypes/default.md new file mode 100644 index 0000000..00e77bd --- /dev/null +++ b/archetypes/default.md @@ -0,0 +1,6 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +date: {{ .Date }} +draft: true +--- + diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..b8ebb0f --- /dev/null +++ b/config.toml @@ -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" + diff --git a/content/about.md b/content/about.md new file mode 100644 index 0000000..7a88edf --- /dev/null +++ b/content/about.md @@ -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. diff --git a/content/posts/arch-on-rpi.md b/content/posts/arch-on-rpi.md new file mode 100644 index 0000000..20d802d --- /dev/null +++ b/content/posts/arch-on-rpi.md @@ -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 diff --git a/content/posts/local-name-resolution.md b/content/posts/local-name-resolution.md new file mode 100644 index 0000000..881971e --- /dev/null +++ b/content/posts/local-name-resolution.md @@ -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 diff --git a/content/posts/midi-on-rpi.md b/content/posts/midi-on-rpi.md new file mode 100644 index 0000000..7d1bd70 --- /dev/null +++ b/content/posts/midi-on-rpi.md @@ -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/ diff --git a/content/posts/partitions-backup.md b/content/posts/partitions-backup.md new file mode 100644 index 0000000..5b40470 --- /dev/null +++ b/content/posts/partitions-backup.md @@ -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 + diff --git a/content/posts/rpi-access-point.md b/content/posts/rpi-access-point.md new file mode 100644 index 0000000..24c8954 --- /dev/null +++ b/content/posts/rpi-access-point.md @@ -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 + diff --git a/content/posts/rpi-alt-review.md b/content/posts/rpi-alt-review.md new file mode 100644 index 0000000..0586ade --- /dev/null +++ b/content/posts/rpi-alt-review.md @@ -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 diff --git a/content/posts/svg.md b/content/posts/svg.md new file mode 100644 index 0000000..7d9560e --- /dev/null +++ b/content/posts/svg.md @@ -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 + + + + + +``` +Centrage vertical : + +``` html + + + + + +``` + +Si on veut qu'il "stretche" sur son conteneur, on peut ajouter l'option +`preserveAspectRatio = none` : + +``` html + + + + + +``` + +# 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 + + + + + +``` + +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 ``. + +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 `` ; +svgContainer est donc vide. + +``` html + + + + + +``` + diff --git a/content/posts/transition-2.md b/content/posts/transition-2.md new file mode 100644 index 0000000..215c51c --- /dev/null +++ b/content/posts/transition-2.md @@ -0,0 +1,135 @@ ++++ +title = "transitions II - javascript" +date = 2020-04-13 +# layout: code ++++ + + +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 + +
+ +
+ texte dans la cible +
+
+
+
+ un div pour prendre un peu de place +
+
+ texte dans la flèche +
+
+``` + +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 %}) + diff --git a/content/posts/transition-3.md b/content/posts/transition-3.md new file mode 100644 index 0000000..7b6f989 --- /dev/null +++ b/content/posts/transition-3.md @@ -0,0 +1,93 @@ ++++ +title = "transitions III - css" +date = 2020-04-13 +# layout: code ++++ + + +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 + +
+ +
+ texte de la cible +
+
+ un div pour prendre un peu de place +
+
+ texte de la flèche +
+
+``` +[la suite]({% post_url html/2020-04-13-transition-4 %}) + diff --git a/content/posts/transition-4.md b/content/posts/transition-4.md new file mode 100644 index 0000000..6265b18 --- /dev/null +++ b/content/posts/transition-4.md @@ -0,0 +1,103 @@ ++++ +title = "transition IV - SVG" +date = 2020-04-13 +# layout: code ++++ + + +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 + +
+ + + un span pour prendre un peu de place + + + texte du span d'arrivée + + + + +
+``` +[js]: {% link html/transition-4.js %} diff --git a/content/posts/transition.md b/content/posts/transition.md new file mode 100644 index 0000000..6352d11 --- /dev/null +++ b/content/posts/transition.md @@ -0,0 +1,60 @@ ++++ +tite = "transition" +date = 2020-04-13 +# layout: code ++++ + + +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 + + +
+ + + texte du span 1 + + + texte du span 2 + +
+``` +[la suite]({% post_url html/2020-04-13-transition-2 %}) + diff --git a/content/posts/vim-language-servers.md b/content/posts/vim-language-servers.md new file mode 100644 index 0000000..99bed9a --- /dev/null +++ b/content/posts/vim-language-servers.md @@ -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 pumvisible() ? "\" : "\" +inoremap pumvisible() ? "\" : "\" +inoremap pumvisible() ? "\" : "\" + +" 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 diff --git a/content/posts/vim-links.md b/content/posts/vim-links.md new file mode 100644 index 0000000..20d3477 --- /dev/null +++ b/content/posts/vim-links.md @@ -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 diff --git a/content/search.md b/content/search.md new file mode 100644 index 0000000..12a2d21 --- /dev/null +++ b/content/search.md @@ -0,0 +1,5 @@ +--- +title: "Search" +--- + +{{}} diff --git a/data/transition-3.js b/data/transition-3.js new file mode 100644 index 0000000..08f74ff --- /dev/null +++ b/data/transition-3.js @@ -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); + } +} + diff --git a/data/transition-4.js b/data/transition-4.js new file mode 100644 index 0000000..dd2ae90 --- /dev/null +++ b/data/transition-4.js @@ -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); + } +} + diff --git a/static/images/icon.jpg b/static/images/icon.jpg new file mode 100644 index 0000000..ced8483 Binary files /dev/null and b/static/images/icon.jpg differ diff --git a/themes/harbor b/themes/harbor new file mode 160000 index 0000000..e7af87b --- /dev/null +++ b/themes/harbor @@ -0,0 +1 @@ +Subproject commit e7af87b290f47acb2a2463c3cb8bcbed4405713c