...
 
Commits (55)
*.pyc
__pycache__
/.venv
/.idea
\ No newline at end of file
# postmarketos.github.io
Source files of postmarketOS website
# postmarketos.org
## Dev
### Python Requirements Setup
Python 3.4+ is supported. Install all requirements, preferably within a virtualenv:
```bash
$ python -m venv .venv
$ source .venv/bin/activate
(venv)$ pip install -r requirements.txt
```
### New Blog Content
Blog content is written in markdown format with metadata in the file header. Filename syntax is `yyyy-mm-dd-slug.md`.
```bash
$ cat >content/blog/2017-12-31-happy-new-year.md << EOF
> ---
> title: Happy New Year!
> ---
>
> This is a *markdown* **formatted** post.
> EOF
```
### Dev Server
Run the dev server during local development, changes are auto reloaded:
```bash
(venv)$ FLASK_DEBUG=1 FLASK_APP=app.py flask run
```
### CSS
Not much CSS is used due to heavy usage of [Tachyons](http://tachyons.io/) toolkit classes. Please use CSS judiciously only when needed.
Any CSS used should be compiled via `lessc`:
```bash
$ npm install -g less
$ lessc static/css/main.less static/css/main.css
```
### Build
To run a static site build, run:
```bash
(venv)$ python freeze.py
```
This will generate a static version in `docs/`. Any manual changes to the `docs/` directory will be overridden in the next build.
import logo
import markdown
import os
import re
import yaml
from datetime import datetime
from flask import Flask, render_template, url_for, Response
from os import listdir
app = Flask(__name__)
BLOG_CONTENT_DIR = 'content/blog'
REGEX_SPLIT_FRONTMATTER = re.compile(r'^---$', re.MULTILINE)
WIKI_REDIRECTS = {
"chat": "Matrix_and_IRC",
"deviceinfo": "Deviceinfo_reference",
"devices": "Supported_devices",
"irc": "Matrix_and_IRC",
"matrix": "Matrix_and_IRC",
"troubleshooting": "Troubleshooting",
"usbhook": "Inspecting_the_initramfs",
"warning-repo": "Troubleshooting#Installed_version_newer_than_the_version_in_the_repositories",
"warning-repo2": "Troubleshooting#Newer_version_in_binary_package_repositories_than_in_aports_folder",
"wiki": "Main_page",
}
@app.route('/')
def home():
return render_template('index.html')
def reading_time(content):
content = re.sub('<[^<]+?>', '', content)
words_per_minute = 200
words = content.split(" ")
return int(len(words) / words_per_minute)
@app.route('/logo.svg')
def logo_svg():
return Response(response=logo.create(phone=False), mimetype="image/svg+xml")
def parse_post(post):
with open(os.path.join(BLOG_CONTENT_DIR, post)) as handle:
raw = handle.read()
frontmatter, content = REGEX_SPLIT_FRONTMATTER.split(raw, 2)
data = yaml.load(frontmatter)
y, m, d, *title = post[:-3].split('-')
slug = '-'.join(title)
data['url'] = url_for('blog_post', y=y, m=m, d=d, slug=slug)
data['reading_time'] = reading_time(content)
return data
@app.route('/blog/')
def blog():
posts = sorted(listdir(BLOG_CONTENT_DIR), reverse=True)
posts = map(parse_post, posts)
return render_template('blog.html', posts=posts)
@app.route('/blog/<y>/<m>/<d>/<slug>/')
def blog_post(y, m, d, slug):
date_str = '-'.join([y, m, d])
post_path = '-'.join([date_str, slug])
with open('{}/{}.md'.format(BLOG_CONTENT_DIR, post_path.lower()), 'r') as f:
text = f.read()
frontmatter, body = REGEX_SPLIT_FRONTMATTER.split(text, 2)
data = yaml.load(frontmatter)
rt = reading_time(body)
date = datetime.strptime(date_str, '%Y-%m-%d')
html = markdown.markdown(body, extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc'
])
return render_template('blog-post.html', title=data['title'], html=html, reading_time=rt, date=date)
@app.route('/<slug>/')
def wiki_redirect(slug):
""" WARNING: This must be the last route! """
return render_template('redirect.html', url='https://wiki.postmarketos.org/wiki/' + WIKI_REDIRECTS[slug])
title: "Aiming for a 10 year life-cycle for smartphones"
date: 2017-05-26
---
_Introduction post to [postmarketOS](https://github.com/postmarketOS/), a touch-optimized, pre-configured [Alpine Linux](https://alpinelinux.org/) with own packages, that can be installed on smartphones. (Not usable for most people yet!)_
## Minimalistic Linux distributions run fine on ten year old PCs.
It is 2017\. Pick an average PC from 2007 and install a minimal Linux based operating system. You will be able to do basic computing tasks (eg. surfing the web, reading E-Mails, listening to music, chatting) just like on an _expensive_ modern PC. You will even get security updates, so your old computer is protected, just like as a new one.
## Why are Android/Linux phones different?
Androids architecture is based on **forking** (one might as well say _copy-pasting_) **the entire code-base for each and every device _and_ Android version.** And then working on that independent, basically instantly incompatible version. Especially adding device-specific drivers plays an important role.
This workflow makes it next to impossible to patch all Android devices with security updates in time or at all (_Stagefright_ vulnerabilities for example rendered [one billion devices](https://threatpost.com/stagefright-2-0-vulnerabilities-affect-1-billion-android-devices/114863/) vulnerable). And even if the vendor provides updates, it will only be for a limited time and then you must buy a new device to get security updates or the latest Android version. How convenient!
Alright, so there is the [LineageOS](https://lineageos.org/) community, which provides weekly updates for an impressive number of smartphones. They provide a practical solution today, and we are very grateful for that. However, such Android based projects will always run behind Google and the phone industry, fixing only symptoms but never the root-cause.
_This is just the tip of the iceberg. Android has way more problems, read Cascardo's [GNU on Smartphones (part II)](https://cascardo.eti.br/blog/GNU_on_Smartphones_part_II/) for more nightmares._
## We can fix this as a community.
Here is the solution: Bend an existing Linux distribution to run on smartphones. Apply all necessary changes as small patches and upstream them, where it makes sense.
Of course we are not the only ones, that came to this conclusion - especially in the last few weeks with the [Halium](https://halium.org/) project rising _(greetings!)_. We are all-in for working together — sharing udev rules, merging Android kernels together, whatever makes sense!
[![Unlocking encrypted rootfs via telnet](https://ollieparanoid.github.io/img/2017-05-26/i9100/telnet-thumb.jpg "Unlocking encrypted rootfs via telnet")](https://ollieparanoid.github.io/img/2017-05-26/i9100/telnet.jpg)
## postmarketOS architecture
We're working on an Alpine Linux based distribution called postmarketOS where each phone will have **only one [unique](https://github.com/postmarketOS/pmbootstrap/tree/master/aports/device/device-samsung-i9100/) [package](https://github.com/postmarketOS/pmbootstrap/tree/master/aports/device/device-lg-mako)** — all other packages are shared among all devices.
These `device-$vendor-$name` packages contain a so-called `/etc/deviceinfo` file, which [describes](https://github.com/postmarketOS/pmbootstrap/blob/master/aports/device/device-samsung-i9100/deviceinfo) [what](https://github.com/postmarketOS/pmbootstrap/blob/master/aports/device/device-lg-mako/deviceinfo) makes the device special: SD card availability, which flash software to use and other information. The file format is not stable yet, and once we have common kernels for multiple devices, I'd like to include the required modules and `dtb` name.
And just to make it clear, postmarketOS does not fit the Halium model, as it avoids the Android build system entirely and does _not_ run any part of the Android userspace next to its more or less typical Linux userspace. _(At least not in the regular install, but it could come at some point in the future as optional compatibility layer for Android applications if someone wants to work on it.)_
## pmbootstrap
_Technical details incoming! If you're not into that, skip this section._
Alpine Linux is _really small_. A base installation is only about 6 MB in size and takes not more than a few seconds to extract! Thanks to this characteristic, we can have a bootstrap program that abstracts everything in [chroots](https://en.wikipedia.org/wiki/chroot) and therefore basically runs on top of any Linux distribution, which has Python 3 and the `openssl` command line program available.
Consequently, the host system does not get touched when installing the required programs ([`fastboot`](https://en.wikipedia.org/wiki/Android_software_development#Fastboot) etc.) and your distribution doesn't even need to have them packaged.
Quick feature rundown:
* Chroot setup (with distro-independent [QEMU user emulation](https://wiki.debian.org/QemuUserEmulation)):
* `x86_64`* (building, flashing, ...)
* `armhf`* (building)
* `armhf`* (target rootfs)
* Clean chroot shutdown (`umount`) and zapping
* Build software as packages:
* Wraps `abuild`, the ["light version of `makepkg`"](https://github.com/alpinelinux/abuild/blob/master/abuild.in#L3)
* Alpine Linux' [`APKBUILD`s](https://wiki.alpinelinux.org/wiki/APKBUILD_Reference) are very similar to Arch Linux' `PKGBUILD`s
* [Cross-compile](https://en.wikipedia.org/wiki/Cross_compiler) **all** `armhf`-packages:
* Linux Kernel: build with cross-compiler in `x86_64` chroot
* Other: build in `armhf` chroot, use cross-compiler with [`distcc`](https://en.wikipedia.org/wiki/distcc) from `x86_64` chroot ([alarm-style](https://archlinuxarm.org/wiki/Distcc_Cross-Compiling))
* Use Alpine Linux' shipped modern `gcc`, no pre-built Android toolchain
* Effective caching out of the box (survives chroot zaps):
* [`ccache`](https://ccache.samba.org/) (also works with `distcc`/cross-compiler)
* Alpine Linux package cache
* Installation targets:
* Raw image file (flash as "system" partition)
* SD card
* Flasher abstraction:
* [`fastboot`](https://en.wikipedia.org/wiki/Android_software_development#Fastboot)
* [`heimdall`](http://glassechidna.com.au/heimdall/)
* ... really easy to add more!
* Logging:
* all shell commands executed are logged in an extra file
* readable overview is displayed on the screen
* Security:
* Initial package manager download
* Signature verification with `openssl` against keys shipped with pmbootstrap
* Minimum installed version check (for downloaded package and version reported by the extracted binary)
* All executed shell commands get properly escaped with Python's built-in [shlex](https://docs.python.org/3/library/shlex.html)
* Working testcases for the above two points
* Only using root rights where necessary (through sudo)
* No default passwords in the installation: The `install` action asks for the _user's_ and for the _root partition_ password.
<small>* `x86_64`/`armhf`: Example architectures for host/target. The code is generic, so it should work with any architectures supported by Alpine Linux.</small>
## Future goals and where *you* could help
Rough direction of where we'd like postmarketOS to head to. In case you're a hacker who wants to help, feel free to do so. But please write into the [tracker](https://github.com/postmarketOS/pmbootstrap/issues) before starting serious work. This way we can make sure, that we do not have redundant work.
### Devices
Pick an old Android device, that you don't need anymore and start porting postmarketOS for it. It should be pretty straight forward. One `device-*` package, one for the kernel, calibrate the touchscreen, and the demos should more or less work already!
If you're feeling adventurous, try a non-Android device. [How about iPhones](https://en.wikipedia.org/wiki/OpeniBoot)?
### Drivers
Make the following peripherals work:
* WiFi
* Audio
* Modem (Phone calls, mobile internet)
* Hardware buttons (Volume keys, home button)
* Camera
* ...
In most cases, the drivers are already provided by the Android/LineageOS kernels, that we currently use and only need to be configured in the userspace (for example with [udev rules](https://en.wikipedia.org/wiki/Udev)).
The long time goal is using the [mainline kernel](https://lwn.net/Articles/680109/).
### Phone interface
postmarketOS is developed in the spirit of regular Linux distributions, so there's no problem in having multiple phone interfaces (just like KDE/Gnome/XFCE/...) and let the user choose.
See the current status in [#62](https://github.com/postmarketOS/pmbootstrap/issues/62).
### Security
Great care has been taken to make pmbootstrap safe, as it will run on productive systems of postmarketOS developers. This is not the case for postmarketOS in its current proof-of-concept state (Weston runs as root, ...) so we must work on that before it can be used in real life. Even better would be [privilege separation](https://en.wikipedia.org/wiki/Privilege_separation) throughout the entire OS.
*This post was originally published [here](https://ollieparanoid.github.io/post/postmarketOS). It has been slightly modified to remove outdated information and to reflect the community efforts.*
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
<!doctype html>
<head>
<meta http-equiv="refresh" content="0; url=https://wiki.postmarketos.org/wiki/Deviceinfo_reference"/>
</head>
This diff is collapsed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>postmarketOS</title>
<meta name="description" content="Aiming for a 10 year life-cycle for smartphones">
<meta name="keywords" content="postmarketOS">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="all">
<link rel="canonical" href="https://postmarketos.org/blog/">
<link href="/static/css/tachyons.min.css" rel="stylesheet">
<link href="/static/css/main.css" rel="stylesheet">
</head>
<body>
<main class="mw8 center">
<div class="cf ph2-ns">
<header>
<a class="title" href="/">
<h1 class="f2 lh-title branding">
<img src="/static/img/recycle.png">
<span>postmarketOS</span>
</h1>
</a>
<div class="menu">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/wiki/">Wiki</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
</div>
</header>
<ul>
<li><a href="/blog/2017/05/26/intro/">intro</a></li>
</ul>
</div>
</main>
</body>
</html>
\ No newline at end of file
<!doctype html>
<html>
<head>
<meta http-equiv="refresh" content="0;URL=https://github.com/postmarketOS/pmbootstrap/wiki/deviceinfo-reference"
</head>
<body>Redirecting to <a href="https://github.com/postmarketOS/pmbootstrap/wiki/deviceinfo-reference">https://github.com/postmarketOS/pmbootstrap/wiki/deviceinfo-reference</a>...</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>postmarketOS</title>
<meta name="description" content="Aiming for a 10 year life-cycle for smartphones">
<meta name="keywords" content="postmarketOS">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="all">
<link rel="canonical" href="https://postmarketos.org/">
<link href="/static/css/tachyons.min.css" rel="stylesheet">
<link href="/static/css/main.css" rel="stylesheet">
</head>
<body class="homepage">
<main class="mw8 center">
<div class="cf ph2-ns">
<header>
<a class="title" href="/">
<h1 class="f2 lh-title branding">
<img src="/static/img/recycle.png">
<span>postmarketOS</span>
</h1>
</a>
<div class="menu">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/wiki/">Wiki</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
</div>
</header>
</div>
</main>
<section class=" img-text">
<div class="mw8 center">
<div class="img-valign">
<img src="/static/img/banner.jpg">
</div>
<div class="text">
<h2>A real Linux distribution on the phone</h2>
<p>postmarketOS (<i>pmOS</i>), is a touch-optimized, pre-configured <a
href="https://alpinelinux.org">Alpine Linux</a> that can be installed on smartphones and other mobile
devices. The project is at very early stages of development and is not yet usable for most users.</p>
</div>
</div>
</section>
<section class="inverted img-text">
<div class="mw8 center">
<div class="text">
<h2>We develop a <em>sustainable</em> mobile OS</h2>
<p>It is 2017. Pick an average PC from 2007 and install a minimal Linux based operating system. You will be able
to do basic computing tasks (eg. surfing the web, reading E-Mails, listening to music, chatting) just like on an
expensive modern PC. You will even get security updates, so your old computer is protected, just like as a new
one.
</p>
<p>
On the current mobile landscape you get none of this. Even if you buy a very expensive phone you will only get
two years of support if you're lucky. But if you treat your smartphone as a real Linux system and use a
distribution instead of a "ROM" and use a package manager instead of a "store" you can just keep using your
devices until they break.
</p>
</div>
<div class="img-valign">
<img src="/static/img/phonepile.jpg">
</div>
</div>
</section>
<section class="img-text">
<div class="mw8 center">
<div class="img-valign fl w-third">
<img src="/static/img/cmus.jpg">
</div>
<div class="text fl w-two-thirds">
<h2>Package and run anything you want</h2>
<p>
With postmarketOS you will have the freedom to customize your Linux experience. There are multiple open options
available in the current Linux community like KDE Plasma Mobile, UBports, Enlightenment or you can just use a
plain shell.
</p>
<p>
postmarketOS will not impose arbitrary restrictions on you. Use the apps from any ecosystem you want! even run
desktop software if you want. If your favorite option is not available then help us improving postmarketOS.
</p>
</div>
</div>
</section>
<section class="inverted img-text">
<div class="mw8 center">
<div class="text">
<h2>Getting better security</h2>
<p>
Current Android devices run on old Linux kernels. This is mainly because SoC vendors modify the current kernel
on introduction of the chipset with their own code. This code doesn't get mainlined and thus doesn't benefit from
security updates that are made by Linus's kernel team. Only on select devices you will get some backported security
patches.
</p>
<p>
postmarketOS aims to get as much devices as possible running on a mainline linux kernel. Which is the most we
can currently do to get more security on the devices. For a better overview of the security in the mobile world
see the <a href="https://ollieparanoid.github.io/post/security-warning/">security warning blog post</a>.
</p>
</div>
<div class="img-valign">
<img src="/static/img/backlit.jpg">
</div>
</div>
</section>
<section class="call-to-action">
<div class="mw8 center">
<h2>Come help us</h2>
<ul>
<li><a href="https://github.com/postmarketOS">GitHub</a></li>
<li><a href="https://reddit.com/r/postmarketOS">/r/postmarketOS</a></li>
<li><a href="https://chat.disroot.org/#/room/#postmarketos:disroot.org">#postmarketos:disroot.org</a></li>
<li>#postmarketos on Freenode IRC</li>
</ul>
</div>
</section>
</body>
</html>
\ No newline at end of file
body {
background: #eee;
}
a {
color: #0d8000;
}
a:hover {
color: #084d00;
}
a.title {
text-decoration: none;
color: black;
}
a.title h1 img {
height: 1.4em;
}
a.title h1 span {
vertical-align: top;
}
body,
html {
height: 100%;
}
header::after {
display: block;
content: " ";
clear: both;
}
main {
background: white;
min-height: 100%;
box-shadow: 0 0 16px 0 rgba(0, 0, 0, 0.2);
}
body.homepage main {
min-height: initial;
border-radius: 0 0 8px 8px;
margin-bottom: 32px;
}
body.homepage section {
margin-bottom: 32px;
}
body.homepage section .img-valign {
display: inline-block;
height: 100%;
vertical-align: middle;
}
body.homepage section .img-valign img {
vertical-align: middle;
}
body.homepage section.inverted {
color: white;
background: #333;
border-top: 4px solid #0d8000;
border-bottom: 4px solid #0d8000;
}
body.homepage section.img-text > div {
display: flex;
align-items: center;
justify-content: center;
}
body.homepage section.img-text .img-valign {
flex: 1;
width: 33.3%;
}
body.homepage section.img-text .text {
flex: 2;
width: 66%;
}
body.homepage section.img-text img {
box-shadow: 0 0 16px 0 rgba(0, 0, 0, 0.2);
}
body.homepage section.call-to-action {
text-align: center;
}
body.homepage section.call-to-action ul {
list-style: none;
padding: 0;
margin: 0;
}
body.homepage section.call-to-action ul li {
margin: 0;
padding: 6px;
list-style: none;
}
body.homepage section::after {
display: block;
content: " ";
clear: both;
}
body.homepage section .text {
padding: 32px;
}
div.menu ul {
float: right;
list-style: none;
padding-left: 7px;
}
div.menu ul li {
display: inline-block;
padding-right: 1.3em;
}
div.menu ul li a {
font-size: 1.2em;
color: #0d8000;
text-decoration: none;
}
div.menu ul li a:hover {
color: #0a6700;
}
h1.branding {
float: left;
}
@branding-color: #0d8000;
body {
background: #eee;
}
a {
color: @branding-color;
&:hover {
color: darken(@branding-color, 10%);
}
}
a.title {
text-decoration: none;
color: black;
h1 {
img {
height: 1.4em;
}
span {
vertical-align: top;
}
}
}
body, html {
height: 100%;
}
header {
&::after {
display: block;
content: " ";
clear: both;
}
}
main {
background: white;
min-height: 100%;
box-shadow: 0 0 16px 0 rgba(0, 0, 0, 0.2);
}
body.homepage {
main {
min-height: initial;
border-radius: 0 0 8px 8px;
margin-bottom: 32px;
}
section {
margin-bottom: 32px;
.img-valign {
display: inline-block;
height: 100%;
vertical-align: middle;
img {
vertical-align: middle;
}
}
&.inverted {
color: white;
background: #333;
border-top: 4px solid @branding-color;
border-bottom: 4px solid @branding-color;
}
&.img-text {
& > div {
display: flex;
align-items: center;
justify-content: center;
}
.img-valign {
flex: 1;
width: 33.3%;
}
.text {
flex: 2;
width: 66%;
}
img {
box-shadow: 0 0 16px 0 rgba(0, 0, 0, 0.2);
}
}
&.call-to-action {
text-align: center;
ul {
list-style: none;
padding: 0;
margin: 0;
li {
margin: 0;
padding: 6px;
list-style: none;
}
}
}
&::after {
display: block;
content: " ";
clear: both;
}
.text {
padding: 32px;
}
}
}
div.menu ul {
float: right;
list-style: none;
padding-left: 7px;
li {
display: inline-block;
padding-right: 1.3em;
a {
font-size: 1.2em;
color: @branding-color;
text-decoration: none;
&:hover {
color: darken(@branding-color, 5%);
}
}
}
}
h1.branding {
float: left;
}
\ No newline at end of file
This diff is collapsed.
<!doctype html>
<html>
<head>
<meta http-equiv="refresh" content="0;URL=https://github.com/postmarketOS/pmbootstrap/wiki/Troubleshooting"
</head>
<body>Redirecting to <a href="https://github.com/postmarketOS/pmbootstrap/wiki/Troubleshooting">https://github.com/postmarketOS/pmbootstrap/wiki/Troubleshooting</a>...</body>
</html>
\ No newline at end of file
from flask_frozen import Freezer
from app import app, BLOG_CONTENT_DIR, WIKI_REDIRECTS
from os import listdir
freezer = Freezer(app)
app.config['FREEZER_DESTINATION'] = 'docs'
app.config['FREEZER_BASE_URL'] = 'https://postmarketos.org/'
@freezer.register_generator
def blog_post():
for f in listdir(BLOG_CONTENT_DIR):
y, m, d, *title = f[:-3].split('-')
slug = '-'.join(title)
yield { 'y': y, 'm': m, 'd': d, 'slug': slug }
@freezer.register_generator
def wiki_redirect():
for slug, redirect in WIKI_REDIRECTS.items():
yield {'slug': slug }
if __name__ == '__main__':
freezer.freeze()
<!doctype html>
<head>
<title>postmarketOS - Aiming for a 10 year life-cycle for smartphones</title>
<meta name="description" content="Official Website of postmarketOS"/>
<meta name="keywords" content="postmarketos, pmos, linux on mobile, alpine linux"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="img/favicon.png" type="image/png">
</head>
<body>
<style>div.sns > a {margin:10px}</style>
<link rel="stylesheet" href="css/bootstrap.min.css">
<div class="container-fluid text-center">
</div>
<h1 class="text-center" style="font-size:2.5em;">postmarketOS</h1>
<br>
<br>
<br>
<div class="container-fluid">
<div class="container-fluid text-center">
<div class="col-md-5">
<img src="img/banner.jpg" style="max-height: 300px; max-width:100%;">
</div>
<div class="col-md-6">
<p class="text-left" style="font-size:20px; line-height: 50px;">
postmarketOS, or <i>pmOS</i> in short, is a touch-optimized, pre-configured <a href="https://alpinelinux.org">Alpine Linux</a> with own packages, that can be installed on smartphones (Not usable for most people yet!)
</p>
<div class="text-left" style="font-size:1.2em;margin-top:5%;">
<p><a href="https://ollieparanoid.github.io/post/postmarketOS/">Introduction</a></p>
<p><a href="https://ollieparanoid.github.io/post/security-warning/">Security warning</a></p>
<p><a href="https://wiki.postmarketos.org/wiki/Supported_devices">Supported devices</a></p>
<p><a href="https://wiki.postmarketos.org">Wiki</a></p>
</div>
</div>
</div>
<br>
<br>
<div class="row text-center sns" style="margin-bottom:5%;font-size:1.1em;">
<a href="https://github.com/postmarketOS"><u>GitHub</u></a>
<a href="https://reddit.com/r/postmarketOS"><u>/r/postmarketOS</u></a>
<a href="https://wiki.postmarketos.org/wiki/Matrix_and_IRC"><u>IRC / Matrix</u></a>
</div>
</body>
import math
class Point:
def __init__(self, x=0.0, y=0.0):
self.x = x
self.y = y
def __str__(self):
return '{0:.1f},{1:.1f}'.format(self.x, self.y)
def _triangle_height(base):
return (math.sqrt(3) / 2.0) * base
def _point_between(a, b, offset):
angle = _point_angle(a, b)
distance = _distance(a, b)
distance *= offset
return _move(a, angle, distance)
def _distance(a, b):
return math.sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2))
def _point_angle(a, b):
return math.atan2(b.y - a.y, b.x - a.x) * 180 / math.pi
def _move(start, angle, distance):
x = start.x
y = start.y
angle = math.radians(angle)
x += distance * math.cos(angle)
y += distance * math.sin(angle)
return Point(x, y)
def _midpoint(a, b):
return _point_between(a, b, 0.5)
def _create_polygon(points):
pointstring = ""
for p in points:
pointstring += str(p) + " "
return '<polygon points="{}" fill="#090" />'.format(pointstring.strip())
def _create_triangle_side(thickness, offset, space, pointyness):
height = _triangle_height(100)
angle = _point_angle(Point(0, 0), Point(50, height))
outer_angle_point = _point_between(Point(0, 0), Point(50, height), offset / 100.0)
inner_angle_point = _move(outer_angle_point, angle - 90, thickness)
midpoint = _midpoint(inner_angle_point, outer_angle_point)
tip = _move(midpoint, angle, pointyness)
polygon = [
inner_angle_point,
tip,
outer_angle_point,
Point(0, 0),
Point(100 - offset - (space / 2), 0),
Point(100 - offset - (space / 2) - pointyness, thickness / 2),
Point(100 - offset - (space / 2), thickness),
Point(thickness * 1.75, thickness)
]
return _create_polygon(polygon)
def _create_square_side(thickness, offset, space, pointyness, alength, blength):
blength += thickness
midpoint_a = _midpoint(Point(space, 0), Point(space, thickness))
arraw_a = _move(midpoint_a, 0, pointyness)
midpoint_b = _midpoint(Point(alength, blength), Point(alength - thickness, blength))
arraw_b = _move(midpoint_b, 90, pointyness)
points = [
Point(space, 0),
Point(alength, 0),
Point(alength, blength),
arraw_b,
Point(alength - thickness, blength),
Point(alength - thickness, thickness),
Point(space, thickness),
arraw_a
]
return _create_polygon(points)
def _create_path(commands, color):
d = ""
for command in commands:
d += str(command) + " "
return '<path d="{}" fill="{}" />'.format(d.strip(), color)
def _create_phone(width, height, radius, bezel):
result = ""
commands = []
# starting point
commands.append('M 0 {}'.format(radius))
# top left corner
commands.append('A {radius} {radius} 0 0 1 {x} {y}'.format(radius=radius, x=radius, y=0))
# top line
commands.append('h {}'.format(width - radius - radius))
# top right corner
commands.append('a {radius} {radius} 0 0 1 {x} {y}'.format(radius=radius, x=radius, y=radius))
# right line
commands.append('v {}'.format(height - radius - radius))
# bottom right corner
commands.append('a {radius} {radius} 0 0 1 {x} {y}'.format(radius=radius, x=-radius, y=radius))
# bottom line
commands.append('h {}'.format(-(width - radius - radius)))
# bottom left corner
commands.append('a {radius} {radius} 0 0 1 {x} {y}'.format(radius=radius, x=-radius, y=-radius))
# close shape
commands.append('Z')
result += _create_path(commands, '#000')
# Draw the screen
result += '<rect x="{}" y="{}" width="{}" height="{}" fill="{}"/>'.format(bezel, radius, width - bezel - bezel,
height - radius - radius, '#fff')
hole_radius = radius / 2
hole_width = width / 3
commands = []
commands.append('M {} {}'.format(hole_width + hole_radius, hole_radius * 1.5))
commands.append('a {radius} {radius} 0 1 1 {x} {y}'.format(radius=hole_radius / 2, x=0, y=-hole_radius))
commands.append('h {}'.format(hole_width - hole_radius - hole_radius))
commands.append('a {radius} {radius} 0 1 1 {x} {y}'.format(radius=hole_radius / 2, x=0, y=hole_radius))
commands.append('Z')
result += _create_path(commands, '#fff')
commands = []
commands.append('M {} {}'.format(hole_width + hole_radius, height - (hole_radius * 0.5)))
commands.append('a {radius} {radius} 0 1 1 {x} {y}'.format(radius=hole_radius / 2, x=0, y=-hole_radius))
commands.append('h {}'.format(hole_width - hole_radius - hole_radius))
commands.append('a {radius} {radius} 0 1 1 {x} {y}'.format(radius=hole_radius / 2, x=0, y=hole_radius))
commands.append('Z')
result += _create_path(commands, '#fff')
return result
def create(thickness=17, offset=30, space=10, pointedness=7, phone=True):
""" Create logo svg image
:param thickness: Thickness of the line
:param offset: Distance from begin of segment to begin arrowhead
:param space: Whitespace inside arrowhead
:param pointedness: Angle of the arrowhead
:return: SVG string
"""
logo_height = _triangle_height(100)
result = '<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">'
logo_translate = ""
if phone:
result += '<g transform="translate(20,0)">'
result += _create_phone(60, 100, 10, 5)
result += '</g>'
logo_translate = "scale(0.5) translate(50,50)"
# Shift whole logo downward since a equilateral triangle doesn't fit in a square box
result += '<g transform="translate(100,100) rotate(180) translate(0, {}){}">'.format((100 - logo_height) / 2,
logo_translate)
# Create one side
result += _create_triangle_side(thickness, offset, space, pointedness)
# Create rotated second side
result += '<g transform="translate(100,0) rotate(120) ">'
result += _create_triangle_side(thickness, offset, space, pointedness)
result += '</g>'
# Create the final piece
result += '<g transform="translate(50,{}) rotate(240) ">'.format(logo_height)
result += _create_triangle_side(thickness, offset, space, pointedness)
result += '</g>'
result += "</g></svg>"
return result
def create_rectangular(thickness=17, width=50, offset=17, space=5, pointedness=7):
result = '<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">'
result += '<g transform="translate({}, 0)">'.format((100 - width) / 2)
result += _create_square_side(thickness, offset, space, pointedness, width, 0)
# Create rotated second side
result += '<g transform="translate({},{}) rotate(90) ">'.format(width, thickness)
result += _create_square_side(thickness, offset, space, pointedness, 100 - thickness, 0)
result += '</g>'
# Create rotated second side
result += '<g transform="translate({},{}) rotate(180) ">'.format(2 * thickness - 1, 100)
result += _create_square_side(thickness, offset, space, pointedness, width, 0)
result += '</g>'
# Create rotated second side
result += '<g transform="translate({},{}) rotate(270) ">'.format(-thickness, 100 - thickness)
result += _create_square_side(thickness, offset, space, pointedness, 100 - thickness, 0)
result += '</g>'
result += "</g></svg>"
return result
if __name__ == '__main__':
print(create())
click==6.7
Flask==0.12.2
Frozen-Flask==0.15
itsdangerous==0.24
Jinja2==2.9.6
Markdown==2.6.8
MarkupSafe==1.0
Pygments==2.2.0
PyYAML==3.12
Werkzeug==0.12.2
.codehilite .hll { background-color: #ffffcc }
.codehilite {
background: #f5f5f5;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
.codehilite pre { padding: 0; margin: 0; }
.codehilite .c { color: #60a0b0; font-style: italic } /* Comment */
.codehilite .err { border: 1px solid #FF0000 } /* Error */
.codehilite .k { color: #007020; font-weight: bold } /* Keyword */
.codehilite .o { color: #666666 } /* Operator */
.codehilite .ch { color: #60a0b0; font-style: italic } /* Comment.Hashbang */
.codehilite .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */
.codehilite .cp { color: #007020 } /* Comment.Preproc */
.codehilite .cpf { color: #60a0b0; font-style: italic } /* Comment.PreprocFile */
.codehilite .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */
.codehilite .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
.codehilite .ge { font-style: italic } /* Generic.Emph */
.codehilite .gr { color: #FF0000 } /* Generic.Error */
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.codehilite .gi { color: #00A000 } /* Generic.Inserted */
.codehilite .go { color: #888888 } /* Generic.Output */
.codehilite .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
.codehilite .gs { font-weight: bold } /* Generic.Strong */
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.codehilite .gt { color: #0044DD } /* Generic.Traceback */
.codehilite .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
.codehilite .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
.codehilite .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
.codehilite .kp { color: #007020 } /* Keyword.Pseudo */
.codehilite .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
.codehilite .kt { color: #902000 } /* Keyword.Type */
.codehilite .m { color: #40a070 } /* Literal.Number */
.codehilite .s { color: #4070a0 } /* Literal.String */
.codehilite .na { color: #4070a0 } /* Name.Attribute */
.codehilite .nb { color: #007020 } /* Name.Builtin */
.codehilite .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
.codehilite .no { color: #60add5 } /* Name.Constant */
.codehilite .nd { color: #555555; font-weight: bold } /* Name.Decorator */
.codehilite .ni { color: #d55537; font-weight: bold } /* Name.Entity */
.codehilite .ne { color: #007020 } /* Name.Exception */
.codehilite .nf { color: #06287e } /* Name.Function */
.codehilite .nl { color: #002070; font-weight: bold } /* Name.Label */
.codehilite .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
.codehilite .nt { color: #062873; font-weight: bold } /* Name.Tag */
.codehilite .nv { color: #bb60d5 } /* Name.Variable */
.codehilite .ow { color: #007020; font-weight: bold } /* Operator.Word */
.codehilite .w { color: #bbbbbb } /* Text.Whitespace */
.codehilite .mb { color: #40a070 } /* Literal.Number.Bin */
.codehilite .mf { color: #40a070 } /* Literal.Number.Float */
.codehilite .mh { color: #40a070 } /* Literal.Number.Hex */
.codehilite .mi { color: #40a070 } /* Literal.Number.Integer */
.codehilite .mo { color: #40a070 } /* Literal.Number.Oct */
.codehilite .sa { color: #4070a0 } /* Literal.String.Affix */
.codehilite .sb { color: #4070a0 } /* Literal.String.Backtick */
.codehilite .sc { color: #4070a0 } /* Literal.String.Char */
.codehilite .dl { color: #4070a0 } /* Literal.String.Delimiter */
.codehilite .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
.codehilite .s2 { color: #4070a0 } /* Literal.String.Double */
.codehilite .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
.codehilite .sh { color: #4070a0 } /* Literal.String.Heredoc */
.codehilite .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
.codehilite .sx { color: #c65d09 } /* Literal.String.Other */
.codehilite .sr { color: #235388 } /* Literal.String.Regex */
.codehilite .s1 { color: #4070a0 } /* Literal.String.Single */
.codehilite .ss { color: #517918 } /* Literal.String.Symbol */
.codehilite .bp { color: #007020 } /* Name.Builtin.Pseudo */
.codehilite .fm { color: #06287e } /* Name.Function.Magic */
.codehilite .vc { color: #bb60d5 } /* Name.Variable.Class */
.codehilite .vg { color: #bb60d5 } /* Name.Variable.Global */
.codehilite .vi { color: #bb60d5 } /* Name.Variable.Instance */
.codehilite .vm { color: #bb60d5 } /* Name.Variable.Magic */
.codehilite .il { color: #40a070 } /* Literal.Number.Integer.Long */
main {
background: #eee;
}
a {
color: #0d8000;
}
.inverted {
color: white;
background: #333;
border-top: 4px solid #0d8000;
border-bottom: 4px solid #0d8000;
}
@branding-color: #0d8000;
main {
background: #eee;
}
a {
color: @branding-color;
}
.inverted {
color: white;
background: #333;
border-top: 4px solid @branding-color;
border-bottom: 4px solid @branding-color;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% block title %}postmarketOS{% endblock %}</title>
<meta name="description" content="Aiming for a 10 year life-cycle for smartphones">
<meta name="keywords" content="postmarketOS">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="all">
<link rel="canonical" href="https://postmarketos.org{{ request.path }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.png') }}">
<link href="{{ url_for('static', filename='css/tachyons.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/codehilite.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet">
</head>
<body{%- if request.path == "/" %} class="homepage"{% endif %}>
{% include 'header.html' %}
<main class="cf pv4">
<div class="mw8 center">
{% block body %}
{% endblock %}
</div>
</main>
{% block footer %}
<footer class="bg-white">
<div class="mw8 center w-100 pv4 tc">
<h2>Come help us!</h2>
<ul class="list center tc pl0 pl3-ns">
<li class="dib pa3"><a href="https://wiki.postmarketos.org">Wiki</a></li>
<li class="dib pa3"><a href="https://github.com/postmarketOS">GitHub</a></li>
<li class="dib pa3"><a href="https://reddit.com/r/postmarketOS">/r/postmarketOS</a></li>
<li class="dib pa3"><a href="https://chat.disroot.org/#/room/#postmarketos:disroot.org">#postmarketos:disroot.org</a></li>
<li class="dib pa3">#postmarketos on Freenode IRC</li>
</ul>
</div>
</footer>
{% endblock %}
</body>
</html>
{% extends 'base.html' %}
{% block title %}{{ title }}{% endblock %}
{% block body %}
<article class="pa3 lh-copy">
<div class="meta gray">
<span class="f4">{{ date.strftime('%B %d, %Y') }}</span>
<span class="f5 ph3">{{ reading_time }} min. read</span>
</div>
<h1 class="f2 f2-ns">{{ title }}</h1>
{{ html|safe }}
</article>
{% endblock %}