mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-02-03 23:40:14 +08:00
Merge remote-tracking branch 'LCTT/master'
This commit is contained in:
commit
2145f14dba
@ -0,0 +1,482 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (ezio)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10676-1.html)
|
||||
[#]: subject: (Computer Laboratory – Raspberry Pi: Lesson 10 Input01)
|
||||
[#]: via: (https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input01.html)
|
||||
[#]: author: (Alex Chadwick https://www.cl.cam.ac.uk)
|
||||
|
||||
计算机实验室之树莓派:课程 10 输入01
|
||||
======
|
||||
|
||||
欢迎进入输入课程系列。在本系列,你将会学会如何使用键盘接收输入给树莓派。我们将会从揭示输入开始本课,然后转向更传统的文本提示符。
|
||||
|
||||
这是第一堂输入课,会教授一些关于驱动和链接的理论,同样也包含键盘的知识,最后以在屏幕上显示文本结束。
|
||||
|
||||
### 1、开始
|
||||
|
||||
希望你已经完成了 OK 系列课程,这会对你完成屏幕系列课程很有帮助。很多 OK 课程上的文件会被使用而不会做解释。如果你没有这些文件,或者希望使用一个正确的实现,可以从该堂课的[下载页][1]下载模板。如果你使用你自己的实现,请删除调用了 `SetGraphicsAddress` 之后全部的代码。
|
||||
|
||||
### 2、USB
|
||||
|
||||
如你所知,树莓派 B 型有两个 USB 接口,通常用来连接一个鼠标和一个键盘。这是一个非常好的设计决策,USB 是一个非常通用的接口,很多种设备都可以使用它。这就很容易为它设计新外设,很容易为它编写设备驱动,而且通过 USB 集线器可以非常容易扩展。还能更好吗?当然是不能,实际上对一个操作系统开发者来说,这就是我们的噩梦。USB 标准太大了。我是说真的,在你思考如何连接设备之前,它的文档将近 700 页。
|
||||
|
||||
> USB 标准的设计目的是通过复杂的软件来简化硬件交互。
|
||||
|
||||
我和很多爱好操作系统的开发者谈过这些,而他们全部都说几句话:不要抱怨。“实现这个需要花费很久时间”,“你不可能写出关于 USB 的教程”,“收益太小了”。在很多方面,他们是对的,我不可能写出一个关于 USB 标准的教程,那得花费几周时间。我同样不能教授如何为全部所有的设备编写外设驱动,所以使用自己写的驱动是没什么用的。然而,即便不能做到最好,我仍然可以获取一个正常工作的 USB 驱动,拿一个键盘驱动,然后教授如何在操作系统中使用它们。我开始寻找可以运行在一个甚至不知道文件是什么的操作系统的自由驱动,但是我一个都找不到,它们都太高层了,所以我尝试写一个。大家说的都对,这耗费了我几周时间。然而我可以高兴的说我做的这些工作没有获取操作系统以外的帮助,并且可以和鼠标和键盘通信。这绝不是完整的、高效的,或者正确的,但是它能工作。驱动是以 C 编写的,而且有兴趣的可以在下载页找到全部源代码。
|
||||
|
||||
所以,这一个教程不会是 USB 标准的课程(一点也没有)。实际上我们将会看到如何使用其他人的代码。
|
||||
|
||||
### 3、链接
|
||||
|
||||
既然我们要引进外部代码到操作系统,我们就需要谈一谈<ruby>链接<rt>linking</rt></ruby>。链接是一种过程,可以在程序或者操作系统中链接函数。这意味着当一个程序生成之后,我们不必要编写每一个函数(几乎可以肯定,实际上并非如此)。链接就是我们做的用来把我们程序和别人代码中的函数连结在一起。这个实际上已经在我们的操作系统进行了,因为链接器把所有不同的文件链接在一起,每个都是分开编译的。
|
||||
|
||||
> 链接允许我们制作可重用的代码库,所有人都可以在他们的程序中使用。
|
||||
|
||||
有两种链接方式:静态和动态。静态链接就像我们在制作自己的操作系统时进行的。链接器找到全部函数的地址,然后在链接结束前,将这些地址都写入代码中。动态链接是在程序“完成”之后。当程序加载后,动态链接器检查程序,然后在操作系统的库找到所有不在程序里的函数。这就是我们的操作系统最终应该能够完成的一项工作,但是现在所有东西都将是静态链接的。
|
||||
|
||||
> 程序经常调用调用库,这些库会调用其它的库,直到最终调用了我们写的操作系统的库。
|
||||
|
||||
我编写的 USB 驱动程序适合静态编译。这意味着我给你的是每个文件的编译后的代码,然后链接器找到你的代码中的那些没有实现的函数,就将这些函数链接到我的代码。在本课的 [下载页][1] 是一个 makefile 和我的 USB 驱动,这是接下来需要的。下载并使用这个 makefile 替换你的代码中的 makefile, 同事将驱动放在和这个 makefile 相同的文件夹。
|
||||
|
||||
### 4、键盘
|
||||
|
||||
为了将输入传给我们的操作系统,我们需要在某种程度上理解键盘是如何实际工作的。键盘有两种按键:普通键和修饰键。普通按键是字母、数字、功能键,等等。它们构成了键盘上几乎全部按键。修饰键是多达 8 个的特殊键。它们是左 shift、右 shift、左 ctrl、右 ctrl、左 alt、右 alt、左 GUI 和右 GUI。键盘可以检测出所有的组合中那个修饰键被按下了,以及最多 6 个普通键。每次一个按钮变化了(例如,是按下了还是释放了),键盘就会报告给电脑。通常,键盘也会有 3 个 LED 灯,分别指示大写锁定,数字键锁定,和滚动锁定,这些都是由电脑控制的,而不是键盘自己。键盘也可能有更多的灯,比如电源、静音,等等。
|
||||
|
||||
对于标准 USB 键盘,有一个按键值的表,每个键盘按键都一个唯一的数字,每个可能的 LED 也类似。下面的表格列出了前 126 个值。
|
||||
|
||||
表 4.1 USB 键盘值
|
||||
|
||||
| 序号 | 描述 | 序号 | 描述 | 序号 | 描述 | 序号 | 描述 |
|
||||
| ------ | ---------------------- | ------ | -------------------- | ----------- | ----------------------- | -------- | ---------------------- |
|
||||
| 4 | `a` 和 `A` | 5 | `b` 和 `B` | 6 | `c` 和 `C` | 7 | `d` 和 `D` |
|
||||
| 8 | `e` 和 `E` | 9 | `f` 和 `F` | 10 | `g` 和 `G` | 11 | `h` 和 `H` |
|
||||
| 12 | `i` 和 `I` | 13 | `j` 和 `J` | 14 | `k` 和 `K` | 15 | `l` 和 `L` |
|
||||
| 16 | `m` 和 `M` | 17 | `n` 和 `N` | 18 | `o` 和 `O` | 19 | `p` 和 `P` |
|
||||
| 20 | `q` 和 `Q` | 21 | `r` 和 `R` | 22 | `s` 和 `S` | 23 | `t` 和 `T` |
|
||||
| 24 | `u` 和 `U` | 25 | `v` 和 `V` | 26 | `w` 和 `W` | 27 | `x` 和 `X` |
|
||||
| 28 | `y` 和 `Y` | 29 | `z` 和 `Z` | 30 | `1` 和 `!` | 31 | `2` 和 `@` |
|
||||
| 32 | `3` 和 `#` | 33 | `4` 和 `$` | 34 | `5` 和 `%` | 35 | `6` 和 `^` |
|
||||
| 36 | `7` 和 `&` | 37 | `8` 和 `*` | 38 | `9` 和 `(` | 39 | `0` 和 `)` |
|
||||
| 40 | `Return`(`Enter`) | 41 | `Escape` | 42 | `Delete`(`Backspace`) | 43 | `Tab` |
|
||||
| 44 | `Spacebar` | 45 | `-` 和 `_` | 46 | `=` 和 `+` | 47 | `[` 和 `{` |
|
||||
| 48 | `]` 和 `}` | 49 | `\` 和 `|` | 50 | `#` 和 `~` | 51 | `;` 和 `:` |
|
||||
| 52 | `'` 和 `"` | 53 | \` 和 `~` | 54 | `,` 和 `<` | 55 | `.` 和 `>` |
|
||||
| 56 | `/` 和 `?` | 57 | `Caps Lock` | 58 | `F1` | 59 | `F2` |
|
||||
| 60 | `F3` | 61 | `F4` | 62 | `F5` | 63 | `F6` |
|
||||
| 64 | `F7` | 65 | `F8` | 66 | `F9` | 67 | `F10` |
|
||||
| 68 | `F11` | 69 | `F12` | 70 | `Print Screen` | 71 | `Scroll Lock` |
|
||||
| 72 | `Pause` | 73 | `Insert` | 74 | `Home` | 75 | `Page Up` |
|
||||
| 76 | `Delete forward` | 77 | `End` | 78 | `Page Down` | 79 | `Right Arrow` |
|
||||
| 80 | `Left Arrow` | 81 | `Down Arrow` | 82 | `Up Arrow` | 83 | `Num Lock` |
|
||||
| 84 | 小键盘 `/` | 85 | 小键盘 `*` | 86 | 小键盘 `-` | 87 | 小键盘 `+` |
|
||||
| 88 | 小键盘 `Enter` | 89 | 小键盘 `1` 和 `End` | 90 | 小键盘 `2` 和 `Down Arrow` | 91 | 小键盘 `3` 和 `Page Down` |
|
||||
| 92 | 小键盘 `4` 和 `Left Arrow` | 93 | 小键盘 `5` | 94 | 小键盘 `6` 和 `Right Arrow` | 95 | 小键盘 `7` 和 `Home` |
|
||||
| 96 | 小键盘 `8` 和 `Up Arrow` | 97 | 小键盘 `9` 和 `Page Up` | 98 | 小键盘 `0` 和 `Insert` | 99 | 小键盘 `.` 和 `Delete` |
|
||||
| 100 | `\` 和 `|` | 101 | `Application` | 102 | `Power` | 103 | 小键盘 `=` |
|
||||
| 104 | `F13` | 105 | `F14` | 106 | `F15` | 107 | `F16` |
|
||||
| 108 | `F17` | 109 | `F18` | 110 | `F19` | 111 | `F20` |
|
||||
| 112 | `F21` | 113 | `F22` | 114 | `F23` | 115 | `F24` |
|
||||
| 116 | `Execute` | 117 | `Help` | 118 | `Menu` | 119 | `Select` |
|
||||
| 120 | `Stop` | 121 | `Again` | 122 | `Undo` | 123 | `Cut` |
|
||||
| 124 | `Copy` | 125 | `Paste` | 126 | `Find` | 127 | `Mute` |
|
||||
| 128 | `Volume Up` | 129 | `Volume Down` | | | | |
|
||||
|
||||
完全列表可以在[HID 页表 1.12][2]的 53 页,第 10 节找到。
|
||||
|
||||
### 5、车轮后的螺母
|
||||
|
||||
通常,当你使用其他人的代码,他们会提供一份自己代码的总结,描述代码都做了什么,粗略介绍了是如何工作的,以及什么情况下会出错。下面是一个使用我的 USB 驱动的相关步骤要求。
|
||||
|
||||
> 这些总结和代码的描述组成了一个 API - 应用程序产品接口。
|
||||
|
||||
表 5.1 CSUD 中和键盘相关的函数
|
||||
|
||||
| 函数 | 参数 | 返回值 | 描述 |
|
||||
| ----------------------- | ----------------------- | ----------------------- | -----------------------|
|
||||
| `UsbInitialise` | 无 | `r0` 是结果码 | 这个方法是一个集多种功能于一身的方法,它加载 USB 驱动程序,枚举所有设备并尝试与它们通信。这种方法通常需要大约一秒钟的时间来执行,但是如果插入几个 USB 集线器,执行时间会明显更长。在此方法完成之后,键盘驱动程序中的方法就可用了,不管是否确实插入了键盘。返回代码如下解释。|
|
||||
| `UsbCheckForChange` | 无 | 无 | 本质上提供与 `UsbInitialise` 相同的效果,但不提供相同的一次初始化。该方法递归地检查每个连接的集线器上的每个端口,如果已经添加了新设备,则添加它们。如果没有更改,这应该是非常快的,但是如果连接了多个设备的集线器,则可能需要几秒钟的时间。|
|
||||
| `KeyboardCount` | 无 | `r0` 是计数 | 返回当前连接并检测到的键盘数量。`UsbCheckForChange` 可能会对此进行更新。默认情况下最多支持 4 个键盘。可以通过这个驱动程序访问多达这么多的键盘。|
|
||||
| `KeyboardGetAddress` | `r0` 是索引 | `r0` 是地址 | 检索给定键盘的地址。所有其他函数都需要一个键盘地址,以便知道要访问哪个键盘。因此,要与键盘通信,首先要检查计数,然后检索地址,然后使用其他方法。注意,在调用 `UsbCheckForChange` 之后,此方法返回的键盘顺序可能会改变。|
|
||||
| `KeyboardPoll` | `r0` 是地址 | `r0` 是结果码 | 从键盘读取当前键状态。这是通过直接轮询设备来操作的,与最佳实践相反。这意味着,如果没有频繁地调用此方法,可能会错过一个按键。所有读取方法只返回上次轮询时的值。|
|
||||
| `KeyboardGetModifiers` | `r0` 是地址 | `r0` 是修饰键状态 | 检索上次轮询时修饰键的状态。这是两边的 `shift` 键、`alt` 键和 `GUI` 键。这回作为一个位字段返回,这样,位 0 中的 1 表示左控件被保留,位 1 表示左 `shift`,位 2 表示左 `alt` ,位 3 表示左 `GUI`,位 4 到 7 表示前面几个键的右版本。如果有问题,`r0` 包含 0。|
|
||||
| `KeyboardGetKeyDownCount` | `r0` 是地址 | `r0` 是计数 | 检索当前按下键盘的键数。这排除了修饰键。这通常不能超过 6。如果有错误,这个方法返回 0。|
|
||||
| `KeyboardGetKeyDown` | `r0` 是地址,`r1` 键号 | `r0` 是扫描码 | 检索特定按下键的扫描码(见表 4.1)。通常,要计算出哪些键是按下的,可以调用 `KeyboardGetKeyDownCount`,然后多次调用 `KeyboardGetKeyDown` ,将 `r1` 的值递增,以确定哪些键是按下的。如果有问题,返回 0。可以(但不建议这样做)在不调用 `KeyboardGetKeyDownCount` 的情况下调用此方法将 0 解释为没有按下的键。注意,顺序或扫描代码可以随机更改(有些键盘按数字排序,有些键盘按时间排序,没有任何保证)。|
|
||||
| `KeyboardGetKeyIsDown` | `r0` 是地址,`r1` 扫描码 | `r0` 是状态 | 除了 `KeyboardGetKeyDown` 之外,还可以检查按下的键中是否有特定的扫描码。如果没有,返回 0;如果有,返回一个非零值。当检测特定的扫描码(例如寻找 `ctrl+c`)时更快。出错时,返回 0。|
|
||||
| `KeyboardGetLedSupport` | `r0` 是地址 | `r0` 是 LED | 检查特定键盘支持哪些 LED。第 0 位代表数字锁定,第 1 位代表大写锁定,第 2 位代表滚动锁定,第 3 位代表合成,第 4 位代表假名,第 5 位代表电源,第 6 位代表 Shift ,第 7 位代表静音。根据 USB 标准,这些 LED 都不是自动更新的(例如,当检测到大写锁定扫描代码时,必须手动设置大写锁定 LED)。|
|
||||
| `KeyboardSetLeds` | `r0` 是地址, `r1` 是 LED | `r0` 是结果码 | 试图打开/关闭键盘上指定的 LED 灯。查看下面的结果代码值。参见 `KeyboardGetLedSupport` 获取 LED 的值。|
|
||||
|
||||
有几种方法返回“返回值”。这些都是 C 代码的老生常谈了,就是用数字代表函数调用发生了什么。通常情况, 0 总是代表操作成功。下面的是驱动用到的返回值。
|
||||
|
||||
> 返回值是一种处理错误的简单方法,但是通常更优雅的解决途径会出现于更高层次的代码。
|
||||
|
||||
表 5.2 - CSUD 返回值
|
||||
|
||||
| 代码 | 描述 |
|
||||
| ---- | ----------------------------------------------------------------------- |
|
||||
| 0 | 方法成功完成。 |
|
||||
| -2 | 参数:函数调用了无效参数。 |
|
||||
| -4 | 设备:设备没有正确响应请求。 |
|
||||
| -5 | 不匹配:驱动不适用于这个请求或者设备。 |
|
||||
| -6 | 编译器:驱动没有正确编译,或者被破坏了。 |
|
||||
| -7 | 内存:驱动用尽了内存。 |
|
||||
| -8 | 超时:设备没有在预期的时间内响应请求。 |
|
||||
| -9 | 断开连接:被请求的设备断开连接,或者不能使用。 |
|
||||
|
||||
驱动的通常用法如下:
|
||||
|
||||
1. 调用 `UsbInitialise`
|
||||
2. 调用 `UsbCheckForChange`
|
||||
3. 调用 `KeyboardCount`
|
||||
4. 如果返回 0,重复步骤 2。
|
||||
5. 针对你支持的每个键盘:
|
||||
1. 调用 `KeyboardGetAddress`
|
||||
2. 调用 `KeybordGetKeyDownCount`
|
||||
3. 针对每个按下的按键:
|
||||
1. 检查它是否已经被按下了
|
||||
2. 保存按下的按键
|
||||
4. 针对每个保存的按键:
|
||||
3. 检查按键是否被释放了
|
||||
4. 如果释放了就删除
|
||||
6. 根据按下/释放的案件执行操作
|
||||
7. 重复步骤 2
|
||||
|
||||
最后,你可以对键盘做所有你想做的任何事了,而这些方法应该允许你访问键盘的全部功能。在接下来的两节课,我们将会着眼于完成文本终端的输入部分,类似于大部分的命令行电脑,以及命令的解释。为了做这些,我们将需要在更有用的形式下得到一个键盘输入。你可能注意到我的驱动是(故意的)没有太大帮助,因为它并没有方法来判断是否一个按键刚刚按下或释放了,它只有方法来判断当前那个按键是按下的。这就意味着我们需要自己编写这些方法。
|
||||
|
||||
### 6、可用更新
|
||||
|
||||
首先,让我们实现一个 `KeyboardUpdate` 方法,检查第一个键盘,并使用轮询方法来获取当前的输入,以及保存最后一个输入来对比。然后我们可以使用这个数据和其它方法来将扫描码转换成按键。这个方法应该按照下面的说明准确操作:
|
||||
|
||||
> 重复检查更新被称为“轮询”。这是针对驱动 IO 中断而言的,这种情况下设备在准备好后会发一个信号。
|
||||
|
||||
1. 提取一个保存好的键盘地址(初始值为 0)。
|
||||
2. 如果不是 0 ,进入步骤 9.
|
||||
3. 调用 `UsbCheckForChange` 检测新键盘。
|
||||
4. 调用 `KeyboardCount` 检测有几个键盘在线。
|
||||
5. 如果返回 0,意味着没有键盘可以让我们操作,只能退出了。
|
||||
6. 调用 `KeyboardGetAddress` 参数是 0,获取第一个键盘的地址。
|
||||
7. 保存这个地址。
|
||||
8. 如果这个值是 0,那么退出,这里应该有些问题。
|
||||
9. 调用 `KeyboardGetKeyDown` 6 次,获取每次按键按下的值并保存。
|
||||
10. 调用 `KeyboardPoll`
|
||||
11. 如果返回值非 0,进入步骤 3。这里应该有些问题(比如键盘断开连接)。
|
||||
|
||||
要保存上面提到的值,我们将需要下面 `.data` 段的值。
|
||||
|
||||
```
|
||||
.section .data
|
||||
.align 2
|
||||
KeyboardAddress:
|
||||
.int 0
|
||||
KeyboardOldDown:
|
||||
.rept 6
|
||||
.hword 0
|
||||
.endr
|
||||
```
|
||||
|
||||
```
|
||||
.hword num 直接将半字的常数插入文件。
|
||||
```
|
||||
|
||||
```
|
||||
.rept num [commands] .endr 复制 `commands` 命令到输出 num 次。
|
||||
```
|
||||
|
||||
试着自己实现这个方法。对此,我的实现如下:
|
||||
|
||||
1、我们加载键盘的地址。
|
||||
|
||||
```
|
||||
.section .text
|
||||
.globl KeyboardUpdate
|
||||
KeyboardUpdate:
|
||||
push {r4,r5,lr}
|
||||
|
||||
kbd .req r4
|
||||
ldr r0,=KeyboardAddress
|
||||
ldr kbd,[r0]
|
||||
```
|
||||
|
||||
2、如果地址非 0,就说明我们有一个键盘。调用 `UsbCheckForChanges` 慢,所以如果一切正常,我们要避免调用这个函数。
|
||||
|
||||
```
|
||||
teq kbd,#0
|
||||
bne haveKeyboard$
|
||||
```
|
||||
|
||||
3、如果我们一个键盘都没有,我们就必须检查新设备。
|
||||
|
||||
```
|
||||
getKeyboard$:
|
||||
bl UsbCheckForChange
|
||||
```
|
||||
|
||||
4、如果有新键盘添加,我们就会看到这个。
|
||||
|
||||
```
|
||||
bl KeyboardCount
|
||||
```
|
||||
|
||||
5、如果没有键盘,我们就没有键盘地址。
|
||||
|
||||
```
|
||||
teq r0,#0
|
||||
ldreq r1,=KeyboardAddress
|
||||
streq r0,[r1]
|
||||
beq return$
|
||||
```
|
||||
|
||||
6、让我们获取第一个键盘的地址。你可能想要支持更多键盘。
|
||||
|
||||
```
|
||||
mov r0,#0
|
||||
bl KeyboardGetAddress
|
||||
```
|
||||
|
||||
7、保存键盘地址。
|
||||
|
||||
```
|
||||
ldr r1,=KeyboardAddress
|
||||
str r0,[r1]
|
||||
```
|
||||
|
||||
8、如果我们没有键盘地址,这里就没有其它活要做了。
|
||||
|
||||
```
|
||||
teq r0,#0
|
||||
beq return$
|
||||
mov kbd,r0
|
||||
```
|
||||
|
||||
9、循环查询全部按键,在 `KeyboardOldDown` 保存下来。如果我们询问的太多了,返回 0 也是正确的。
|
||||
|
||||
```
|
||||
saveKeys$:
|
||||
mov r0,kbd
|
||||
mov r1,r5
|
||||
bl KeyboardGetKeyDown
|
||||
|
||||
ldr r1,=KeyboardOldDown
|
||||
add r1,r5,lsl #1
|
||||
strh r0,[r1]
|
||||
add r5,#1
|
||||
cmp r5,#6
|
||||
blt saveKeys$
|
||||
```
|
||||
|
||||
10、现在我们得到了新的按键。
|
||||
|
||||
```
|
||||
mov r0,kbd
|
||||
bl KeyboardPoll
|
||||
```
|
||||
|
||||
11、最后我们要检查 `KeyboardOldDown` 是否工作了。如果没工作,那么我们可能是断开连接了。
|
||||
|
||||
```
|
||||
teq r0,#0
|
||||
bne getKeyboard$
|
||||
|
||||
return$:
|
||||
pop {r4,r5,pc}
|
||||
.unreq kbd
|
||||
```
|
||||
|
||||
有了我们新的 `KeyboardUpdate` 方法,检查输入变得简单,固定周期调用这个方法就行,而它甚至可以检查键盘是否断开连接,等等。这是一个有用的方法,因为我们实际的按键处理会根据条件不同而有所差别,所以能够用一个函数调以它的原始方式获取当前的输入是可行的。下一个方法我们希望它是 `KeyboardGetChar`,简单的返回下一个按下的按钮的 ASCII 字符,或者如果没有按键按下就返回 0。这可以扩展到支持如果它按下一个特定时间当做多次按下按键,也支持锁定键和修饰键。
|
||||
|
||||
如果我们有一个 `KeyWasDown` 方法可以使这个方法有用起来,如果给定的扫描代码不在 `KeyboardOldDown` 值中,它只返回 0,否则返回一个非零值。你可以自己尝试一下。与往常一样,可以在下载页面找到解决方案。
|
||||
|
||||
### 7、查找表
|
||||
|
||||
`KeyboardGetChar` 方法如果写得不好,可能会非常复杂。有 100 多种扫描码,每种代码都有不同的效果,这取决于 shift 键或其他修饰符的存在与否。并不是所有的键都可以转换成一个字符。对于一些字符,多个键可以生成相同的字符。在有如此多可能性的情况下,一个有用的技巧是查找表。查找表与物理意义上的查找表非常相似,它是一个值及其结果的表。对于一些有限的函数,推导出答案的最简单方法就是预先计算每个答案,然后通过检索返回正确的答案。在这种情况下,我们可以在内存中建立一个序列的值,序列中第 n 个值就是扫描代码 n 的 ASCII 字符代码。这意味着如果一个键被按下,我们的方法只需要检测到,然后从表中检索它的值。此外,我们可以为当按住 shift 键时的值单独创建一个表,这样按下 shift 键就可以简单地换个我们用的表。
|
||||
|
||||
> 在编程的许多领域,程序越大,速度越快。查找表很大,但是速度很快。有些问题可以通过查找表和普通函数的组合来解决。
|
||||
|
||||
在 `.section .data` 命令之后,复制下面的表:
|
||||
|
||||
```
|
||||
.align 3
|
||||
KeysNormal:
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 'a', 'b', 'c', 'd'
|
||||
.byte 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'
|
||||
.byte 'm', 'n', 'o', 'p', 'q', 'r', 's', 't'
|
||||
.byte 'u', 'v', 'w', 'x', 'y', 'z', '1', '2'
|
||||
.byte '3', '4', '5', '6', '7', '8', '9', '0'
|
||||
.byte '\n', 0x0, '\b', '\t', ' ', '-', '=', '['
|
||||
.byte ']', '\\\', '#', ';', '\'', '`', ',', '.'
|
||||
.byte '/', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, '/', '*', '-', '+'
|
||||
.byte '\n', '1', '2', '3', '4', '5', '6', '7'
|
||||
.byte '8', '9', '0', '.', '\\\', 0x0, 0x0, '='
|
||||
|
||||
.align 3
|
||||
KeysShift:
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 'A', 'B', 'C', 'D'
|
||||
.byte 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'
|
||||
.byte 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T'
|
||||
.byte 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '"'
|
||||
.byte '£', '$', '%', '^', '&', '*', '(', ')'
|
||||
.byte '\n', 0x0, '\b', '\t', ' ', '_', '+', '{'
|
||||
.byte '}', '|', '~', ':', '@', '¬', '<', '>'
|
||||
.byte '?', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, '/', '*', '-', '+'
|
||||
.byte '\n', '1', '2', '3', '4', '5', '6', '7'
|
||||
.byte '8', '9', '0', '.', '|', 0x0, 0x0, '='
|
||||
```
|
||||
|
||||
这些表直接将前 104 个扫描码映射到 ASCII 字符作为一个字节表。我们还有一个单独的表来描述 `shift` 键对这些扫描码的影响。我使用 ASCII `null` 字符(`0`)表示所有没有直接映射的 ASCII 键(例如功能键)。退格映射到 ASCII 退格字符(8 表示 `\b`),`enter` 映射到 ASCII 新行字符(10 表示 `\n`), `tab` 映射到 ASCII 水平制表符(9 表示 `\t`)。
|
||||
|
||||
> `.byte num` 直接插入字节常量 num 到文件。
|
||||
|
||||
.
|
||||
|
||||
> 大部分的汇编器和编译器识别转义序列;如 `\t` 这样的字符序列会插入该特殊字符。
|
||||
|
||||
`KeyboardGetChar` 方法需要做以下工作:
|
||||
|
||||
1. 检查 `KeyboardAddress` 是否返回 `0`。如果是,则返回 0。
|
||||
2. 调用 `KeyboardGetKeyDown` 最多 6 次。每次:
|
||||
1. 如果按键是 0,跳出循环。
|
||||
2. 调用 `KeyWasDown`。 如果返回是,处理下一个按键。
|
||||
3. 如果扫描码超过 103,进入下一个按键。
|
||||
4. 调用 `KeyboardGetModifiers`
|
||||
5. 如果 `shift` 是被按着的,就加载 `KeysShift` 的地址,否则加载 `KeysNormal` 的地址。
|
||||
6. 从表中读出 ASCII 码值。
|
||||
7. 如果是 0,进行下一个按键,否则返回 ASCII 码值并退出。
|
||||
3. 返回 0。
|
||||
|
||||
|
||||
试着自己实现。我的实现展示在下面:
|
||||
|
||||
1、简单的检查我们是否有键盘。
|
||||
|
||||
```
|
||||
.globl KeyboardGetChar
|
||||
KeyboardGetChar:
|
||||
ldr r0,=KeyboardAddress
|
||||
ldr r1,[r0]
|
||||
teq r1,#0
|
||||
moveq r0,#0
|
||||
moveq pc,lr
|
||||
```
|
||||
|
||||
2、`r5` 将会保存按键的索引,`r4` 保存键盘的地址。
|
||||
|
||||
```
|
||||
push {r4,r5,r6,lr}
|
||||
kbd .req r4
|
||||
key .req r6
|
||||
mov r4,r1
|
||||
mov r5,#0
|
||||
keyLoop$:
|
||||
mov r0,kbd
|
||||
mov r1,r5
|
||||
bl KeyboardGetKeyDown
|
||||
```
|
||||
|
||||
2.1、 如果扫描码是 0,它要么意味着有错,要么说明没有更多按键了。
|
||||
|
||||
```
|
||||
teq r0,#0
|
||||
beq keyLoopBreak$
|
||||
```
|
||||
|
||||
2.2、如果按键已经按下了,那么他就没意义了,我们只想知道按下的按键。
|
||||
|
||||
```
|
||||
mov key,r0
|
||||
bl KeyWasDown
|
||||
teq r0,#0
|
||||
bne keyLoopContinue$
|
||||
```
|
||||
|
||||
|
||||
2.3、如果一个按键有个超过 104 的扫描码,它将会超出我们的表,所以它是无关的按键。
|
||||
|
||||
```
|
||||
cmp key,#104
|
||||
bge keyLoopContinue$
|
||||
```
|
||||
|
||||
2.4、我们需要知道修饰键来推断字符。
|
||||
|
||||
```
|
||||
mov r0,kbd
|
||||
bl KeyboardGetModifiers
|
||||
```
|
||||
|
||||
5. 当将字符更改为其 shift 变体时,我们要同时检测左 `shift` 键和右 `shift` 键。记住,`tst` 指令计算的是逻辑和,然后将其与 0 进行比较,所以当且仅当移位位都为 0 时,它才等于 0。
|
||||
|
||||
```
|
||||
tst r0,#0b00100010
|
||||
ldreq r0,=KeysNormal
|
||||
ldrne r0,=KeysShift
|
||||
```
|
||||
|
||||
2.6、现在我们可以从查找表加载按键了。
|
||||
|
||||
```
|
||||
ldrb r0,[r0,key]
|
||||
```
|
||||
|
||||
2.7、如果查找码包含一个 0,我们必须继续。为了继续,我们要增加索引,并检查是否到 6 次了。
|
||||
|
||||
```
|
||||
teq r0,#0
|
||||
bne keyboardGetCharReturn$
|
||||
keyLoopContinue$:
|
||||
add r5,#1
|
||||
cmp r5,#6
|
||||
blt keyLoop$
|
||||
```
|
||||
|
||||
|
||||
3、在这里我们返回我们的按键,如果我们到达 `keyLoopBreak$` ,然后我们就知道这里没有按键被握住,所以返回 0。
|
||||
|
||||
```
|
||||
keyLoopBreak$:
|
||||
mov r0,#0
|
||||
keyboardGetCharReturn$:
|
||||
pop {r4,r5,r6,pc}
|
||||
.unreq kbd
|
||||
.unreq key
|
||||
```
|
||||
|
||||
### 8、记事本操作系统
|
||||
|
||||
现在我们有了 `KeyboardGetChar` 方法,可以创建一个操作系统,只打印出用户对着屏幕所写的内容。为了简单起见,我们将忽略所有非常规的键。在 `main.s`,删除 `bl SetGraphicsAddress` 之后的所有代码。调用 `UsbInitialise`,将 `r4` 和 `r5` 设置为 0,然后循环执行以下命令:
|
||||
|
||||
1. 调用 `KeyboardUpdate`
|
||||
2. 调用 `KeyboardGetChar`
|
||||
3. 如果返回 0,跳转到步骤 1
|
||||
4. 复制 `r4` 和 `r5` 到 `r1` 和 `r2` ,然后调用 `DrawCharacter`
|
||||
5. 把 `r0` 加到 `r4`
|
||||
6. 如果 `r4` 是 1024,将 `r1` 加到 `r5`,然后设置 `r4` 为 0。
|
||||
7. 如果 `r5` 是 768,设置 `r5` 为0
|
||||
8. 跳转到步骤 1
|
||||
|
||||
现在编译,然后在树莓派上测试。你几乎可以立即开始在屏幕上输入文本。如果没有工作,请参阅我们的故障排除页面。
|
||||
|
||||
当它工作时,祝贺你,你已经实现了与计算机的接口。现在你应该开始意识到,你几乎已经拥有了一个原始的操作系统。现在,你可以与计算机交互、发出命令,并在屏幕上接收反馈。在下一篇教程[输入02][3]中,我们将研究如何生成一个全文本终端,用户在其中输入命令,然后计算机执行这些命令。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input01.html
|
||||
|
||||
作者:[Alex Chadwick][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[ezio](https://github.com/oska874)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.cl.cam.ac.uk
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/downloads.html
|
||||
[2]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/downloads/hut1_12v2.pdf
|
||||
[3]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input02.html
|
@ -1,191 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (LuuMing)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (My Google-free Android life)
|
||||
[#]: via: (https://lushka.al/my-android-setup/)
|
||||
[#]: author: (Anxhelo Lushka https://lushka.al/)
|
||||
|
||||
My Google-free Android life
|
||||
======
|
||||
|
||||
People have been asking me a lot lately about my phone, my Android setup and how I manage to use my smartphone without Google Services. Well, this is a post that aims to address precisely that. I would like to make this article really beginner-friendly so I’ll try to go slow, going through things one by one and including screenshots so you can have a better view on how things happen and work like.
|
||||
|
||||
At first I’ll start with why Google Services are (imo) bad for your device. I could cut it short and guide you to this [post][1] by [Richard Stallman][2], but I’m grabbing a few main points from it and adding them here.
|
||||
|
||||
* Nonfree software required
|
||||
* In general, most Google services require running nonfree Javascript code. Nowadays, nothing whatsoever appears if Javascript is disabled, even making a Google account requires running nonfree software (Javascript sent by the site), same thing for logging in.
|
||||
* Surveillance
|
||||
* Google quietly combines its ad-tracking profiles with its browsing profiles and stores a huge amount of data on each user.
|
||||
* Terms of Service
|
||||
* Google cuts off accounts for users that resell Pixel phones. They lose access to all of their mail and documents stored in Google servers under that account.
|
||||
* Censorship
|
||||
* Amazon and Google have cut off domain-fronting, a feature used to enable people in tyrannical countries to reach communication systems that are banned there.
|
||||
* Google has agreed to perform special censorship of Youtube for the government of Pakistan, deleting views that the state opposes. This will help the illiberal Pakistani state suppress dissent.
|
||||
* Youtube’s “content ID” automatically deletes posted videos in a way copyright law does not require.
|
||||
|
||||
|
||||
|
||||
These are just a few reasons, but you can read the post by RMS I linked above in which he tries to explain these points in detail. Although it may look like a tinfoil hat reaction to you, all these actions already happen everyday in real life.
|
||||
|
||||
### Next on the list, my setup and a tutorial on how I achieved it
|
||||
|
||||
I own a **[Xiaomi Redmi Note 5 Pro][3]** smartphone (codename **whyred** ), produced in China by [Xiaomi][4], which I bought for around 185 EUR 4 months ago (from the time of writing this post).
|
||||
|
||||
Now you might be thinking, ‘but why did you buy a Chinese brand, they are not reliable’. Yes, it is not made from the usuals as you would expect, such as Samsung (which people often associate with Android, which is plain wrong), OnePlus, Nokia etc, but you should know almost every phone is produced in China.
|
||||
|
||||
There were a few reasons I chose this phone, first one of course being the price. It is a quite **budget-friendly** device, so most people are able to afford it. Next one would be the specs, which on paper (not only) are pretty decents for the price tag. With a 6 inch screen (Full HD resolution), a **4000 mAh battery** (superb battery life), 4GB of RAM, 64GB of storage, dual back cameras (12MP + 5MP), a front camera with flash (13MP) and a decent efficient Snapdragon 636, it was probably the best choice at that moment.
|
||||
|
||||
The issue with it was that it came with [MIUI][5], the Android skin that Xiaomi ships with most of its devices (except the Android One project devices). Yes, it is not that horrible, it has some extra features, but the problems lie deeper within. One of the reasons these devices from Xiaomi are so cheap (afaik they only have 5-10% win margin from sales) is that **they include data mining and ads in the system altogether with MIUI**. In this way, the system apps requires extra unnecessary permissions that mine your data and bombard you with ads, from which Xiaomi earns money.
|
||||
|
||||
Funnily enough, the Weather app included wanted access to my contacts and to make calls, why would it need that if it would just show the weather? Another case was with the Recorder app, it also required contacts and internet permissions, probably to send those recordings back to Xiaomi.
|
||||
|
||||
To fix this, I’d have to format the phone and get rid of MIUI. This has become increasingly difficult with the latest phones in the market.
|
||||
|
||||
The concept of formatting a phone is simple, you remove the existing system and install a new one of your preference (Android-only in this case). To do that, you have to have your [bootloader][6] unlocked.
|
||||
|
||||
> A bootloader is a computer program that loads an operating system (OS) or runtime environment for the computer after completion of the self-tests. — [Wikipedia][7]
|
||||
|
||||
The problem here is that Xiaomi has a specific policy about the bootloader unlocking. A few months ago, the process was like this. You would have to [make a request][8] to Xiaomi to obtain an unlock code for your phone, by giving a valid reason, but this would not always work, as they could just refuse your request without reason and explanation.
|
||||
|
||||
Now, that process has changed. You’ll have to download a specific software from Xiaomi, called [Mi Unlock][9], install it in your Windows PC, [activate Debugging Settings in Developer Options][10] on your phone, reboot to the bootloader mode (by holding the Volume Down + Power button while the phone is off) and connect the phone to your computer to start a process called “Approval”. This process starts a timer on the Xiaomi servers that will allow you to **unlock the phone only after a period of 15 days** (or a month in some rare cases, totally random) goes by.
|
||||
|
||||
![Mi Unlock app][11]
|
||||
|
||||
After this period of 15 days has passed, you have to re-connect your phone and do the same procedure as above, then by pressing the Unlock button your bootloader will be unlocked and this will allow you to install other ROM-s (systems). **Careful, make sure to backup your data because unlocking the bootloader deletes everything in the phone**.
|
||||
|
||||
The next step would be finding a system ([ROM][12]) that works for your device. I searched through the [XDA Developers Forum][13], which is a place where Android developers and users exchange ideas, apps etc. Fortunately, my phone is quite popular so it had [its own forum category][14]. There, I skimmed through some popular ROM-s for my device and decided to use the [AOSiP ROM][15] (AOSiP standing for Android Open Source illusion Project).
|
||||
|
||||
**EDIT** : Someone emailed me to say that my article is exactly what [/e/][16] does and is targeted to. I wanted to say thank you for reaching out but that is not true at all. The reasoning behind my opinion about /e/ can also be found in this [website][17], but I’ll list a few of the reasons here.
|
||||
|
||||
eelo is a “foundation” that got over 200K € in funding from Kickstarter and IndieGoGo, promising to create a mobile OS and web services that are open and secure and protect your privacy.
|
||||
|
||||
1. Their OS is based on LineageOS 14.1 (Android 7.1) with microG and other open source apps with it, which already exists for a long time now and it’s called [Lineage for microG][18].
|
||||
2. Instead of building all apps from the source code, they download the APKs from [APKPure][19] and put them in the ROM, without knowing if those APKs contain proprietary code/malware in them.
|
||||
3. At one point, they were literally just removing the Lineage copyright header from their code and adding theirs.
|
||||
4. They love to delete negative feedback and censor their users’ opinions in their Telegram group chat.
|
||||
|
||||
|
||||
|
||||
In conclusion, I **don’t recommend using /e/** ROM-s (at least until now).
|
||||
|
||||
Another thing you would likely want to do is have [root access][20] to your phone, to make it truly yours and modify files in the system, such as use a system-wide adblocker etc. To do this, I decided to use [Magisk][21], a godsend app developed by a student to help you gain root access on your device and install what are called [modules][22], basically software.
|
||||
|
||||
After downloading the ROM and Magisk, I had to install them on my phone. To do that, I moved the files to my SD card on the phone. Now, to install the system, I had to use something called a [recovery system][23]. The one I use is called [TWRP][24] (standing for TeamWin Recovery Project), a popular solution.
|
||||
|
||||
To install the recovery system (sounds hard, I know), I had to [flash][20] the file on the phone. To do that, I connected my phone with the computer (Fedora Linux system) and with something called [ADB Tools][25] I issued a command that overwrites the system recovery with the custom one I had.
|
||||
|
||||
> fastboot flash recovery twrp.img
|
||||
|
||||
After this was done, I turned off the phone and kept Volume Up + Power button pressed until I saw the TWRP screen show up. That meant I was good to go and it was ready to receive my commands.
|
||||
|
||||
![TWRP screen][26]
|
||||
|
||||
Next step was to **issue a Wipe command** , necessary when you first install a custom ROM on your phone. As you can see from the image above, the Wipe command clears the Data, Cache and Dalvik (there is also an advanced option that allows us to tick a box to delete the System one too, as we don’t need the old one anymore).
|
||||
|
||||
This takes a few moments and after that, your phone is basically clean. Now it’s time to **install the system**. By pressing the Install button on the main screen, we select the zip file we added there before (the ROM file) and swipe the screen to install it. Next, we have to install Magisk, which gives us root access to the device.
|
||||
|
||||
**EDIT** : As some more experienced/power Android users might have noticed until now, there is no [GApps][27] (Google Apps) included. This is what we call GApps-less in the Android world, not having those packages installed at all.
|
||||
|
||||
Note that one of the downsides of not having Google Services installed is that some of your apps might not work, for example their notifications might take longer to arrive or might not even work at all (this is what happens with Mattermost app for me). This happens because these apps use [Google Cloud Messaging][28] (now called [Firebase][29]) to wake the phone and push notifications to your phone.
|
||||
|
||||
You can solve this (partially) by installing and using [microG][30] which provides some features of Google Services but allows for more control on your side. I don’t recommend using this because it still helps Google Services and you don’t really give up on them, but it’s a good start if you want to quit Google slowly and not go cold turkey on it.
|
||||
|
||||
After successfully installing both, now we reboot the phone and **tada** 🎉, we are in the main screen.
|
||||
|
||||
### Next part, installing the apps and configuring everything
|
||||
|
||||
This is where things start to get easier. To install the apps, I use [F-Droid][31], an alternative app store that includes **only free and open source apps**. If you need apps that are not available there, you can use [Aurora Store][32], a client to download apps from the Play Store without using your Google account or getting tracked.
|
||||
|
||||
F-Droid has what are called repos, a “storehouse” that contains apps you can install. I use the default ones and have added another one from [IzzyOnDroid][33], that contains some more apps not available from the default F-Droid repo and is updated more often.
|
||||
|
||||
![My repos][34]
|
||||
|
||||
Below you will find a list of the apps I have installed, what they replace and their use.
|
||||
|
||||
This is pretty much **my list of the most useful F-Droid apps** I use, but unfortunately these are NOT the only apps I use. The proprietary apps I use (I know, I might sound a hypocrite, but not everything is replaceable, not yet at least) are as below:
|
||||
|
||||
* AliExpress
|
||||
* Boost for Reddit
|
||||
* Google Camera (coupled with Camera API 2, this app allows me to take wonderful pictures with a 185 EUR phone, it’s just too impressive)
|
||||
* Instagram
|
||||
* MediaBox HD (allows me to stream movies)
|
||||
* Mi Fit (an app that pairs with my Mi Band 2)
|
||||
* MyVodafoneAL (the carrier app)
|
||||
* ProtonMail (email app)
|
||||
* Shazam Encore (to find those songs you usually listen in coffee shops)
|
||||
* Snapseed (photo editing app, really simple, powerful and quite good)
|
||||
* Spotify (music streaming)
|
||||
* Titanium Backup (to backup my app data, wifi passwords, calls log etc.)
|
||||
* ViPER4Android FX (music equalizer)
|
||||
* VSCO (photo editing, never use it really)
|
||||
* WhatsApp (E2E proprietary messaging app, almost everyone I know has it)
|
||||
* WiFi Map (mapped hotspots that are available, handy when abroad)
|
||||
|
||||
|
||||
|
||||
This is pretty much it, all the apps I use on my phone. **The configs are then pretty simple and straightforward and I can give a few tips**.
|
||||
|
||||
1. Read and check the permissions of apps carefully, don’t click ‘Install’ mindlessly.
|
||||
2. Try to use as many open source apps as possible, they both respect your privacy and are free (as in both free beer and freedom).
|
||||
3. Use a VPN as much as you can, find a reputable one and don’t use free ones, otherwise you get to be the product and you’ll get your data harvested.
|
||||
4. Don’t keep your WiFi/mobile data/location on all the time, it might be a security risk.
|
||||
5. Try not to rely on fingerprint unlock only, or better yet use only PIN/password/pattern unlock, as biometric data can be cloned and used against you, for example to unlock your phone and steal your data.
|
||||
|
||||
|
||||
|
||||
And as a bonus for reading far down here, **a screenshot of my home screen** right now.
|
||||
|
||||
![Screenshot][35]
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://lushka.al/my-android-setup/
|
||||
|
||||
作者:[Anxhelo Lushka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://lushka.al/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://stallman.org/google.html
|
||||
[2]: https://en.wikipedia.org/wiki/Richard_Stallman
|
||||
[3]: https://www.gsmarena.com/xiaomi_redmi_note_5_pro-8893.php
|
||||
[4]: https://en.wikipedia.org/wiki/Xiaomi
|
||||
[5]: https://en.wikipedia.org/wiki/MIUI
|
||||
[6]: https://forum.xda-developers.com/wiki/Bootloader
|
||||
[7]: https://en.wikipedia.org/wiki/Booting
|
||||
[8]: https://en.miui.com/unlock/
|
||||
[9]: http://www.miui.com/unlock/apply.php
|
||||
[10]: https://www.youtube.com/watch?v=7zhEsJlivFA
|
||||
[11]: https://lushka.al//assets/img/posts/mi-unlock.png
|
||||
[12]: https://www.xda-developers.com/what-is-custom-rom-android/
|
||||
[13]: https://forum.xda-developers.com/
|
||||
[14]: https://forum.xda-developers.com/redmi-note-5-pro
|
||||
[15]: https://forum.xda-developers.com/redmi-note-5-pro/development/rom-aosip-8-1-t3804473
|
||||
[16]: https://e.foundation
|
||||
[17]: https://ewwlo.xyz/evil
|
||||
[18]: https://lineage.microg.org/
|
||||
[19]: https://apkpure.com/
|
||||
[20]: https://lifehacker.com/5789397/the-always-up-to-date-guide-to-rooting-any-android-phone
|
||||
[21]: https://forum.xda-developers.com/apps/magisk/official-magisk-v7-universal-systemless-t3473445
|
||||
[22]: https://forum.xda-developers.com/apps/magisk
|
||||
[23]: http://www.smartmobilephonesolutions.com/content/android-system-recovery
|
||||
[24]: https://dl.twrp.me/whyred/
|
||||
[25]: https://developer.android.com/studio/command-line/adb
|
||||
[26]: https://lushka.al//assets/img/posts/android-twrp.png
|
||||
[27]: https://opengapps.org/
|
||||
[28]: https://developers.google.com/cloud-messaging/
|
||||
[29]: https://firebase.google.com/docs/cloud-messaging/
|
||||
[30]: https://microg.org/
|
||||
[31]: https://f-droid.org/
|
||||
[32]: https://f-droid.org/en/packages/com.dragons.aurora/
|
||||
[33]: https://android.izzysoft.de/repo
|
||||
[34]: https://lushka.al//assets/img/posts/android-fdroid-repos.jpg
|
||||
[35]: https://lushka.al//assets/img/posts/android-screenshot.jpg
|
||||
[36]: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
@ -1,506 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (ezio )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Computer Laboratory – Raspberry Pi: Lesson 10 Input01)
|
||||
[#]: via: (https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input01.html)
|
||||
[#]: author: (Alex Chadwick https://www.cl.cam.ac.uk)
|
||||
|
||||
|
||||
计算机实验课 – 树莓派: 课程 10 输入01
|
||||
======
|
||||
|
||||
欢迎进入输入课程系列。在本系列,你将会学会如何使用键盘接收输入给树莓派。我们将会从揭示输入开始本课,然后开始传统的文本命令。
|
||||
|
||||
这是第一堂输入课,会教授一些关于驱动和链接的理论,同样也包含键盘的知识,最后以在屏幕上显示文本结束。
|
||||
|
||||
### 1 开始
|
||||
|
||||
希望你已经完成了 OK 系列课程, 这会对你完成屏幕系列课程很有帮助。很多 OK 课程上的文件会被使用而不会做解释。如果你没有这些文件,或者希望使用一个正确的实现, 可以从该堂课的[下载页][1]下载模板。如果你使用你自己的实现,请删除调用了 `SetGraphicsAddress` 之后全部的代码。
|
||||
|
||||
### 2 USB
|
||||
|
||||
```
|
||||
USB 标准的设计目的是通过复杂的硬件来简化硬件。
|
||||
```
|
||||
|
||||
如你所知,树莓派 B 型有两个 USB 接口,通常用来连接一个鼠标和一个键盘。这是一个非常好的设计决策,USB 是一个非常通用的接口, 很多种设备都可以使用它。这就很容易为它设计新外设,很容易为它编写设备驱动, 而且通过 USB 集线器可以非常容易扩展。还能更好吗?当然是不能,实际上对一个操作系统开发者来说,这就是我们的噩梦。USB 标准太大了。 我是真的,在你思考如何连接设备之前,它的文档将近 700 页。
|
||||
|
||||
我和很多爱好操作系统的开发者谈过这些,而他们全部都说几句话:不要抱怨。“实现这个需要花费很久时间”,“你不可能写出关于 USB 的教程”,“收益太小了”。在很多方面,他们是对的,我不可能写出一个关于 USB 标准的教程, 那得花费几周时间。我同样不能教授如何为全部所有的设备编写外设驱动,所以使用自己写的驱动是没什么用的。然而,我可以做仅次于最好的事情是获取一个正常工作的 USB 驱动,拿一个键盘驱动,然后教授如何在操作系统中使用它们。我开始寻找可以运行在一个甚至不知道文件是什么的操作系统的自由驱动,但是我一个都找不到。他们都太高层了。所以我尝试写一个。每个人都是对的,这耗费了我几周时间。然而我高兴的说我我做这些工作没有获取操作系统以外的帮助,并且可以和鼠标和键盘通信。这句不是完整的,高效的,或者正确的,但是它能工作。驱动是以 C 编写的,而且有兴趣的可以在下载页找到全部源代码。
|
||||
|
||||
所以,这一个教程不会是 USB 标准的课程(一点也没有)。实际上我们将会看到如何使用其他人的代码。
|
||||
|
||||
### 3 链接
|
||||
|
||||
```
|
||||
链接允许我们制作可重用的代码库,所有人都可以在他们的程序中使用。
|
||||
```
|
||||
|
||||
既然我们要引进外部代码到操作系统,我们就需要谈一谈链接。链接是一种过程,可以在程序或者操作系统中链接函数。这意味着当一个程序生成之后,我们不必要编写每一个函数(几乎可以肯定,实际上并非如此)。链接就是我们做的用来把我们程序和别人代码中的函数连结在一起。这个实际上已经在我们的操作系统进行了,因为链接器吧所有不同的文件链接在一起,每个都是分开编译的。
|
||||
|
||||
|
||||
```
|
||||
程序经常知识调用库,这些库会调用其它的库,知道最终调用了我们写的操作系统。
|
||||
```
|
||||
|
||||
有两种链接:静态和动态。静态链接就像我们在制作自己的操作系统时进行的。链接器找到全部函数的地址,然后在链接结束前,将这些地址都写入代码中。动态链接是在程序“完成”之后。当程序加载后,动态链接器检查程序,然后在操作系统的库找到所有不在程序里的函数。这就是我们的操作系统最终应该能够完成的一项工作,但是现在所有东西都将是静态链接的。
|
||||
|
||||
我编写的 USB 驱动程序适合静态编译。这意味着我给你我的每个文件的编译后的代码,然后链接器找到你的代码中的那些没有实现的函数,就将这些函数链接到我的代码。在本课的 [下载页][1] 是一个 makefile 和我的 USB 驱动,这是接下来需要的。下载并使用这 makefile 替换你的代码中的 makefile, 同事将驱动放在和这个 makefile 相同的文件夹。
|
||||
|
||||
### 4 键盘
|
||||
|
||||
为了将输入传给我们的操作系统,我们需要在某种程度上理解键盘是如何实际工作的。键盘有两种按键:普通键和修饰键。普通按键是字母、数字、功能键,等等。他们构成了键盘上几乎每一个按键。修饰键是最多 8 个特殊键。他们是左 shift , 右 shift, 左 ctrl,右 ctrl,左 alt, 右 alt,左 GUI 和右 GUI。键盘可以检测出所有的组合中那个修饰键被按下了,以及最多 6 个普通键。每次一个按钮变化了(i.e. 是按下了还是释放了),键盘就会报告给电脑。通常,键盘也会有 3 个 LED 灯,分别指示 Caps 锁定,Num 锁定,和 Scroll 锁定,这些都是由电脑控制的,而不是键盘自己。键盘也可能由更多的灯,比如电源、静音,等等。
|
||||
|
||||
为了帮助标准 USB 键盘,产生了一个按键值的表,每个键盘按键都一个唯一的数字,每个可能的 LED 也类似。下面的表格列出了前 126 个值。
|
||||
|
||||
Table 4.1 USB 键盘值
|
||||
|
||||
| 序号 | 描述 | 序号 | 描述 | 序号 | 描述 | 序号 | 描述 | |
|
||||
| ------ | ---------------- | ------- | ---------------------- | -------- | -------------- | --------------- | -------------------- |----|
|
||||
| 4 | a and A | 5 | b and B | 6 | c and C | 7 | d and D | |
|
||||
| 8 | e and E | 9 | f and F | 10 | g and G | 11 | h and H | |
|
||||
| 12 | i and I | 13 | j and J | 14 | k and K | 15 | l and L | |
|
||||
| 16 | m and M | 17 | n and N | 18 | o and O | 19 | p and P | |
|
||||
| 20 | q and Q | 21 | r and R | 22 | s and S | 23 | t and T | |
|
||||
| 24 | u and U | 25 | v and V | 26 | w and W | 27 | x and X | |
|
||||
| 28 | y and Y | 29 | z and Z | 30 | 1 and ! | 31 | 2 and @ | |
|
||||
| 32 | 3 and # | 33 | 4 and $ | 34 | 5 and % | 35 | 6 and ^ | |
|
||||
| 36 | 7 and & | 37 | 8 and * | 38 | 9 and ( | 39 | 0 and ) | |
|
||||
| 40 | Return (Enter) | 41 | Escape | 42 | Delete (Backspace) | 43 | Tab | |
|
||||
| 44 | Spacebar | 45 | - and _ | 46 | = and + | 47 | [ and { | |
|
||||
| 48 | ] and } | 49 | \ and | 50 | # and ~ | 51 | ; and : |
|
||||
| 52 | ' and " | 53 | ` and ~ | 54 | , and < | 55 | . and > | |
|
||||
| 56 | / and ? | 57 | Caps Lock | 58 | F1 | 59 | F2 | |
|
||||
| 60 | F3 | 61 | F4 | 62 | F5 | 63 | F6 | |
|
||||
| 64 | F7 | 65 | F8 | 66 | F9 | 67 | F10 | |
|
||||
| 68 | F11 | 69 | F12 | 70 | Print Screen | 71 | Scroll Lock | |
|
||||
| 72 | Pause | 73 | Insert | 74 | Home | 75 | Page Up | |
|
||||
| 76 | Delete forward | 77 | End | 78 | Page Down | 79 | Right Arrow | |
|
||||
| 80 | Left Arrow | 81 | Down Arrow | 82 | Up Arrow | 83 | Num Lock | |
|
||||
| 84 | Keypad / | 85 | Keypad * | 86 | Keypad - | 87 | Keypad + | |
|
||||
| 88 | Keypad Enter | 89 | Keypad 1 and End | 90 | Keypad 2 and Down Arrow | 91 | Keypad 3 and Page Down | |
|
||||
| 92 | Keypad 4 and Left Arrow | 93 | Keypad 5 | 94 | Keypad 6 and Right Arrow | 95 | Keypad 7 and Home | |
|
||||
| 96 | Keypad 8 and Up Arrow | 97 | Keypad 9 and Page Up | 98 | Keypad 0 and Insert | 99 | Keypad . and Delete | |
|
||||
| 100 | \ and | 101 | Application | 102 | Power | 103 | Keypad = |
|
||||
| 104 | F13 | 105 | F14 | 106 | F15 | 107 | F16 | |
|
||||
| 108 | F17 | 109 | F18 | 110 | F19 | 111 | F20 | |
|
||||
| 112 | F21 | 113 | F22 | 114 | F23 | 115 | F24 | |
|
||||
| 116 | Execute | 117 | Help | 118 | Menu | 119 | Select | |
|
||||
| 120 | Stop | 121 | Again | 122 | Undo | 123 | Cut | |
|
||||
| 124 | Copy | 125 | Paste | 126 | Find | 127 | Mute | |
|
||||
| 128 | Volume Up | 129 | Volume Down | | | | | |
|
||||
|
||||
完全列表可以在[HID 页表 1.12][2]的 53 页,第 10 节找到
|
||||
|
||||
### 5 车轮后的螺母
|
||||
|
||||
```
|
||||
这些总结和代码的描述组成了一个 API - 应用程序产品接口。
|
||||
|
||||
```
|
||||
|
||||
通常,当你使用其他人的代码,他们会提供一份自己代码的总结,描述代码都做了什么,粗略介绍了是如何工作的,以及什么情况下会出错。下面是一个使用我的 USB 驱动的相关步骤要求。
|
||||
|
||||
Table 5.1 CSUD 中和键盘相关的函数
|
||||
| 函数 | 参数 | 返回值 | 描述 |
|
||||
| ----------------------- | ----------------------- | ----------------------- | -----------------------|
|
||||
| UsbInitialise | None | r0 is result code | 这个方法是一个集多种功能于一身的方法,它加载USB驱动程序,枚举所有设备并尝试与它们通信。这种方法通常需要大约一秒钟的时间来执行,但是如果插入几个USB集线器,执行时间会明显更长。在此方法完成之后,键盘驱动程序中的方法就可用了,不管是否确实插入了键盘。返回代码如下解释。|
|
||||
| UsbCheckForChange | None | None | 本质上提供与 `usbinitialization` 相同的效果,但不提供相同的一次初始化。该方法递归地检查每个连接的集线器上的每个端口,如果已经添加了新设备,则添加它们。如果没有更改,这应该是非常快的,但是如果连接了多个设备的集线器,则可能需要几秒钟的时间。|
|
||||
| KeyboardCount | None | r0 is count | 返回当前连接并检测到的键盘数量。`UsbCheckForChange` 可能会对此进行更新。默认情况下最多支持4个键盘。多达这么多的键盘可以通过这个驱动程序访问。|
|
||||
| KeyboardGetAddress | r0 is index | r0 is address | 检索给定键盘的地址。所有其他函数都需要一个键盘地址,以便知道要访问哪个键盘。因此,要与键盘通信,首先要检查计数,然后检索地址,然后使用其他方法。注意,在调用 `UsbCheckForChange` 之后,此方法返回的键盘顺序可能会改变。
|
||||
|
|
||||
| KeyboardPoll | r0 is address | r0 is result code | 从键盘读取当前键状态。这是通过直接轮询设备来操作的,与最佳实践相反。这意味着,如果没有频繁地调用此方法,可能会错过一个按键。所有读取方法只返回上次轮询时的值。
|
||||
|
|
||||
| KeyboardGetModifiers | r0 is address | r0 is modifier state | 检索上次轮询时修饰符键的状态。这是两边的 `shift` 键、`alt` 键和 `GUI` 键。这回作为一个位字段返回,这样,位0中的1表示左控件被保留,位1表示左 `shift`,位2表示左 `alt` ,位3表示左 `GUI`,位4到7表示前几位的右版本。如果有问题,`r0` 包含0。|
|
||||
| KeyboardGetKeyDownCount | r0 is address | r0 is count | 检索当前按下键盘的键数。这排除了修饰键。这通常不能超过6次。如果有错误,这个方法返回0。|
|
||||
| KeyboardGetKeyDown | r0 is address, r1 is key number | r0 is scan code | 检索特定下拉键的扫描代码(见表4.1)。通常,要计算出哪些键是关闭的,可以调用 `KeyboardGetKeyDownCount`,然后多次调用 `KeyboardGetKeyDown` ,将 `r1` 的值递增,以确定哪些键是关闭的。如果有问题,返回0。在不调用 `KeyboardGetKeyDownCount` 并将0解释为未持有的键的情况下调用此方法是安全的(但不建议这样做)。注意,顺序或扫描代码可以随机更改(有些键盘按数字排序,有些键盘按时间排序,没有任何保证)。|
|
||||
| KeyboardGetKeyIsDown | r0 is address, r1 is scan code | r0 is status | 除了 `KeyboardGetKeyDown` 之外,还可以检查下拉键中是否有特定的扫描代码。如果不是,返回0;如果是,返回一个非零值。当检测特定的扫描代码(例如寻找ctrl+c)更快。出错时,返回0。
|
||||
|
|
||||
| KeyboardGetLedSupport | r0 is address | r0 is LEDs | 检查特定键盘支持哪些led。第0位代表数字锁定,第1位代表大写锁定,第2位代表滚动锁定,第3位代表合成,第4位代表假名,第5位代表能量,第6位代表静音,第7位代表合成。根据USB标准,这些led都不是自动更新的(例如,当检测到大写锁定扫描代码时,必须手动设置大写锁定)。|
|
||||
| KeyboardSetLeds | r0 is address, r1 is LEDs | r0 is result code | 试图打开/关闭键盘上指定的 LED 灯。查看下面的结果代码值。参见 `KeyboardGetLedSupport` 获取 LED 的值。
|
||||
|
|
||||
|
||||
```
|
||||
返回值是一种处理错误的简单方法,但是通常更优雅的解决途径存在于更高层次的代码。
|
||||
```
|
||||
|
||||
有几种方法返回 ‘返回值’。这些都是 C 代码的老生常谈了,就是用数字代表函数调用发生了什么。通常情况, 0 总是代表操作成功。下面的是驱动用到的返回值。
|
||||
|
||||
Table 5.2 - CSUD 返回值
|
||||
|
||||
| 代码 | 描述 |
|
||||
| ---- | ----------------------------------------------------------------------- |
|
||||
| 0 | 方法成功完成。 |
|
||||
| -2 | 参数: 函数调用了无效参数。 |
|
||||
| -4 | 设备: 设备没有正确响应请求。 |
|
||||
| -5 | 不匹配: 驱动不适用于这个请求或者设备。 |
|
||||
| -6 | 编译器: 驱动没有正确编译,或者被破坏了。 |
|
||||
| -7 | 内存: 驱动用尽了内存。 |
|
||||
| -8 | 超时: 设备没有在预期的时间内响应请求。 |
|
||||
| -9 | 断开连接: 被请求的设备断开连接,或者不能使用。 |
|
||||
|
||||
驱动的通常用法如下:
|
||||
|
||||
1. 调用 `UsbInitialise`
|
||||
2. 调用 `UsbCheckForChange`
|
||||
3. 调用 `KeyboardCount`
|
||||
4. 如果返回 0,重复步骤 2。
|
||||
5. 针对你支持的每种键盘:
|
||||
1. 调用 ·KeyboardGetAddress·
|
||||
2. 调用 ·KeybordGetKeyDownCount·
|
||||
3. 针对每个按下的按键:
|
||||
1. 检查它是否已经被按下了
|
||||
2. 保存按下的按键
|
||||
4. 针对每个保存的按键:
|
||||
3. 检查按键是否被释放了
|
||||
4. 如果释放了就删除
|
||||
6. 根据按下/释放的案件执行操作
|
||||
7. 重复步骤 2.
|
||||
|
||||
|
||||
最后,你可能做所有你想对键盘做的事,而这些方法应该允许你访问键盘的全部功能。在接下来的两节课,我们将会着眼于完成文本终端的输入部分,类似于大部分的命令行电脑,以及命令的解释。为了做这些,我们将需要在更有用的形式下得到一个键盘输入。你可能注意到我的驱动是(故意)没有太大帮助,因为它并没有方法来判断是否一个案件刚刚按下或释放了,它只有芳芳来判断当前那个按键是按下的。这就意味着我们需要自己编写这些方法。
|
||||
|
||||
### 6 可用更新
|
||||
|
||||
重复检查更新被称为 ‘轮询’。这是针对驱动 IO 中断而言的,这种情况下设备在准备好后会发一个信号。
|
||||
|
||||
首先,让我们实现一个 `KeyboardUpdate` 方法,检查第一个键盘,并使用轮询方法来获取当前的输入,以及保存最后一个输入来对比。然后我们可以使用这个数据和其它方法来将扫描码转换成按键。这个方法应该按照下面的说明准确操作:
|
||||
|
||||
1. 提取一个保存好的键盘地址(初始值为 0)。
|
||||
2. 如果不是0 ,进入步骤9.
|
||||
3. 调用 `UsbCheckForChange` 检测新键盘。
|
||||
4. 调用 `KeyboardCount` 检测有几个键盘在线。
|
||||
5. 如果返回0,意味着没有键盘可以让我们操作,只能退出了。
|
||||
6. 调用 `KeyboardGetAddress` 参数是 0,获取第一个键盘的地址。
|
||||
7. 保存这个地址。
|
||||
8. 如果这个值是0,那么退出,这里应该有些问题。
|
||||
9. 调用 `KeyboardGetKeyDown` 6 次,获取每次按键按下的值并保存。
|
||||
10. 调用 `KeyboardPoll`
|
||||
11. 如果返回值非 0,进入步骤 3。这里应该有些问题(比如键盘断开连接)。
|
||||
|
||||
要保存上面提到的值,我们将需要下面 `.data` 段的值。
|
||||
|
||||
```
|
||||
.section .data
|
||||
.align 2
|
||||
KeyboardAddress:
|
||||
.int 0
|
||||
KeyboardOldDown:
|
||||
.rept 6
|
||||
.hword 0
|
||||
.endr
|
||||
```
|
||||
|
||||
```
|
||||
.hword num 直接将半字的常数插入文件。
|
||||
```
|
||||
|
||||
```
|
||||
.rept num [commands] .endr 复制 `commands` 命令到输出 num 次。
|
||||
```
|
||||
|
||||
试着自己实现这个方法。对此,我的实现如下:
|
||||
|
||||
1.
|
||||
```
|
||||
.section .text
|
||||
.globl KeyboardUpdate
|
||||
KeyboardUpdate:
|
||||
push {r4,r5,lr}
|
||||
|
||||
kbd .req r4
|
||||
ldr r0,=KeyboardAddress
|
||||
ldr kbd,[r0]
|
||||
```
|
||||
我们加载键盘的地址。
|
||||
|
||||
2.
|
||||
```
|
||||
teq kbd,#0
|
||||
bne haveKeyboard$
|
||||
```
|
||||
如果地址非0,就说明我们有一个键盘。调用 `UsbCheckForChanges` 慢,所以如果全部事情都起作用,我们要避免调用这个函数。
|
||||
|
||||
3.
|
||||
```
|
||||
getKeyboard$:
|
||||
bl UsbCheckForChange
|
||||
```
|
||||
如果我们一个键盘都没有,我们就必须检查新设备。
|
||||
|
||||
4.
|
||||
```
|
||||
bl KeyboardCount
|
||||
```
|
||||
如果有心键盘添加,我们就会看到这个。
|
||||
|
||||
5.
|
||||
```
|
||||
teq r0,#0
|
||||
ldreq r1,=KeyboardAddress
|
||||
streq r0,[r1]
|
||||
beq return$
|
||||
```
|
||||
如果没有键盘,我们就没有键盘地址。
|
||||
|
||||
6.
|
||||
```
|
||||
mov r0,#0
|
||||
bl KeyboardGetAddress
|
||||
```
|
||||
让我们获取第一个键盘的地址。你可能想要更多。
|
||||
|
||||
7.
|
||||
```
|
||||
ldr r1,=KeyboardAddress
|
||||
str r0,[r1]
|
||||
```
|
||||
保存键盘地址。
|
||||
|
||||
8.
|
||||
```
|
||||
teq r0,#0
|
||||
beq return$
|
||||
mov kbd,r0
|
||||
```
|
||||
如果我们没有地址,这里就没有其它活要做了。
|
||||
|
||||
9.
|
||||
```
|
||||
saveKeys$:
|
||||
mov r0,kbd
|
||||
mov r1,r5
|
||||
bl KeyboardGetKeyDown
|
||||
|
||||
ldr r1,=KeyboardOldDown
|
||||
add r1,r5,lsl #1
|
||||
strh r0,[r1]
|
||||
add r5,#1
|
||||
cmp r5,#6
|
||||
blt saveKeys$
|
||||
```
|
||||
Loop through all the keys, storing them in KeyboardOldDown. If we ask for too many, this returns 0 which is fine.
|
||||
查询遍全部按键,在 `KeyboardOldDown` 保存下来。如果我们询问的太多了,返回 0 也是正确的。
|
||||
|
||||
10.
|
||||
```
|
||||
mov r0,kbd
|
||||
bl KeyboardPoll
|
||||
```
|
||||
现在哦我们得到了新的按键。
|
||||
|
||||
11.
|
||||
```
|
||||
teq r0,#0
|
||||
bne getKeyboard$
|
||||
|
||||
return$:
|
||||
pop {r4,r5,pc}
|
||||
.unreq kbd
|
||||
```
|
||||
最后我们要检查 `KeyboardOldDown` 是否工作了。如果没工作,那么我们可能是断开连接了。
|
||||
|
||||
有了我们新的 `KeyboardUpdate` 方法,检查输入变得简单到固定周期调用这个方法,而它甚至可以检查断开连接,等等。这是一个有用的方法,因为我们实际的按键处理会根据条件不同而有所差别,所以能够用一个函数调以它的原始方式获取当前的输入是可行的。下一个方法我们理想希望的是 `KeyboardGetChar`,简单的返回下一个按下的按钮的 ASCII 字符,或者如果没有按键按下就返回 0。这可以扩展到支持将一个按键多次按下,如果它保持了一个特定时间,也支持‘锁定’键和修饰键。
|
||||
|
||||
要使这个方法有用,如果我们有一个 `KeyWasDown` 方法,如果给定的扫描代码不在keyboard dolddown值中,它只返回0,否则返回一个非零值。你可以自己尝试一下。与往常一样,可以在下载页面找到解决方案。
|
||||
|
||||
### 7 查找表
|
||||
|
||||
```
|
||||
在编程的许多领域,程序越大,速度越快。查找表很大,但是速度很快。有些问题可以通过查找表和普通函数的组合来解决。
|
||||
```
|
||||
|
||||
`KeyboardGetChar`方法如果写得不好,可能会非常复杂。有 100 多种扫描代码,每种代码都有不同的效果,这取决于 shift 键或其他修饰符的存在与否。并不是所有的键都可以转换成一个字符。对于一些字符,多个键可以生成相同的字符。在有如此多可能性的情况下,一个有用的技巧是查找表。查找表与物理意义上的查找表非常相似,它是一个值及其结果的表。对于一些有限的函数,推导出答案的最简单方法就是预先计算每个答案,然后通过检索返回正确的答案。在这种情况下,我们可以建立一个序列的值在内存中,n值序列的ASCII字符代码扫描代码n。这意味着我们的方法只会发现如果一个键被按下,然后从表中检索它的值。此外,当按住shift键时,我们可以为值创建一个单独的表,这样shift键就可以简单地更改正在处理的表。
|
||||
|
||||
在 `.section` `.data` 命令之后,复制下面的表:
|
||||
|
||||
```
|
||||
.align 3
|
||||
KeysNormal:
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 'a', 'b', 'c', 'd'
|
||||
.byte 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'
|
||||
.byte 'm', 'n', 'o', 'p', 'q', 'r', 's', 't'
|
||||
.byte 'u', 'v', 'w', 'x', 'y', 'z', '1', '2'
|
||||
.byte '3', '4', '5', '6', '7', '8', '9', '0'
|
||||
.byte '\n', 0x0, '\b', '\t', ' ', '-', '=', '['
|
||||
.byte ']', '\\\', '#', ';', '\'', '`', ',', '.'
|
||||
.byte '/', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, '/', '*', '-', '+'
|
||||
.byte '\n', '1', '2', '3', '4', '5', '6', '7'
|
||||
.byte '8', '9', '0', '.', '\\\', 0x0, 0x0, '='
|
||||
|
||||
.align 3
|
||||
KeysShift:
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 'A', 'B', 'C', 'D'
|
||||
.byte 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'
|
||||
.byte 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T'
|
||||
.byte 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '"'
|
||||
.byte '£', '$', '%', '^', '&', '*', '(', ')'
|
||||
.byte '\n', 0x0, '\b', '\t', ' ', '_', '+', '{'
|
||||
.byte '}', '|', '~', ':', '@', '¬', '<', '>'
|
||||
.byte '?', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
.byte 0x0, 0x0, 0x0, 0x0, '/', '*', '-', '+'
|
||||
.byte '\n', '1', '2', '3', '4', '5', '6', '7'
|
||||
.byte '8', '9', '0', '.', '|', 0x0, 0x0, '='
|
||||
```
|
||||
|
||||
```
|
||||
.byte num 直接插入字节常量 num 到文件。
|
||||
```
|
||||
|
||||
```
|
||||
大部分的汇编器和编译器识别转义序列;插入特殊字符的字符序列,如\t。
|
||||
```
|
||||
|
||||
这些表直接将前 104 个扫描代码映射到 ASCII 字符,作为一个字节表。我们还有一个单独的表来描述 `shift` 键对这些扫描代码的影响。我使用 ASCII `null`字符(0)表示所有没有直接映射的 ASCII 键(例如函数键)。退格映射到ASCII退格字符(8表示 `\b` ), `enter` 映射到ASCII新行字符(10表示 `\n`), `tab` 映射到ASCII水平制表符(9表示 `\t` )。
|
||||
|
||||
|
||||
`KeyboardGetChar` 方法需要做以下工作:
|
||||
1. 检查 `KeyboardAddress` 是否返回 0。如果是,则返回0。
|
||||
2. 调用 `KeyboardGetKeyDown` 最多 6 次。每次:
|
||||
1. 如果按键时0,跳出循环。
|
||||
2. 调用 `KeyWasDown`。 如果返回是,处理下一个按键。
|
||||
3. 如果扫描码超过 103,进入下一个按键。
|
||||
4. 调用 `KeyboardGetModifiers`
|
||||
5. 如果 `shift` 是被按着的,就加载 `KeysShift` 的地址。否则加载 `KeysNormal` 的地址。
|
||||
6. 从表中读出 ASCII 码值。
|
||||
7. 如果是 0,进行下一个按键,否则返回 ASCII 码值并退出。
|
||||
3. 返回0。
|
||||
|
||||
|
||||
试着自己实现。我的实现展示在下面:
|
||||
|
||||
1.
|
||||
```
|
||||
.globl KeyboardGetChar
|
||||
KeyboardGetChar:
|
||||
ldr r0,=KeyboardAddress
|
||||
ldr r1,[r0]
|
||||
teq r1,#0
|
||||
moveq r0,#0
|
||||
moveq pc,lr
|
||||
```
|
||||
简单的检查我们是否有键盘。
|
||||
|
||||
2.
|
||||
```
|
||||
push {r4,r5,r6,lr}
|
||||
kbd .req r4
|
||||
key .req r6
|
||||
mov r4,r1
|
||||
mov r5,#0
|
||||
keyLoop$:
|
||||
mov r0,kbd
|
||||
mov r1,r5
|
||||
bl KeyboardGetKeyDown
|
||||
```
|
||||
r5 will hold the index of the key, r4 holds the keyboard address.
|
||||
`r5` 将会保存按键的索引, `r4` 保存键盘的地址。
|
||||
|
||||
1.
|
||||
```
|
||||
teq r0,#0
|
||||
beq keyLoopBreak$
|
||||
```
|
||||
如果扫描码是0,它要么意味着有错,要么说明没有更多按键了。
|
||||
|
||||
2.
|
||||
```
|
||||
mov key,r0
|
||||
bl KeyWasDown
|
||||
teq r0,#0
|
||||
bne keyLoopContinue$
|
||||
```
|
||||
如果按键已经按下了,那么他就没意思了,我们只想知道按下的按键。
|
||||
|
||||
3.
|
||||
```
|
||||
cmp key,#104
|
||||
bge keyLoopContinue$
|
||||
```
|
||||
如果一个按键有个超过 104 的扫描码,他将会超出我们的表,所以它是无关的按键。
|
||||
|
||||
4.
|
||||
```
|
||||
mov r0,kbd
|
||||
bl KeyboardGetModifiers
|
||||
```
|
||||
我们需要知道修饰键来推断字符。
|
||||
|
||||
5.
|
||||
```
|
||||
tst r0,#0b00100010
|
||||
ldreq r0,=KeysNormal
|
||||
ldrne r0,=KeysShift
|
||||
```
|
||||
当将字符更改为其移位变体时,我们要同时检测左 `shift` 键和右 `shift` 键。记住,`tst` 指令计算的是逻辑和,然后将其与 0 进行比较,所以当且仅当移位位都为 0 时,它才等于0。
|
||||
|
||||
|
||||
1.
|
||||
```
|
||||
ldrb r0,[r0,key]
|
||||
```
|
||||
现在我们可以从查找表加载按键了。
|
||||
|
||||
1.
|
||||
```
|
||||
teq r0,#0
|
||||
bne keyboardGetCharReturn$
|
||||
keyLoopContinue$:
|
||||
add r5,#1
|
||||
cmp r5,#6
|
||||
blt keyLoop$
|
||||
```
|
||||
如果查找码包含一个 0,我们必须继续。为了继续,我们要增加索引,并检查是否到 6 次了。
|
||||
|
||||
1.
|
||||
```
|
||||
keyLoopBreak$:
|
||||
mov r0,#0
|
||||
keyboardGetCharReturn$:
|
||||
pop {r4,r5,r6,pc}
|
||||
.unreq kbd
|
||||
.unreq key
|
||||
```
|
||||
在这里我们返回我们的按键,如果我们到达 `keyLoopBreak$` ,然后我们就知道这里没有按键被握住,所以返回0。
|
||||
|
||||
### 8 记事本操作系统
|
||||
|
||||
现在我们有了 `KeyboardGetChar` 方法,可以创建一个操作系统,只输入用户对着屏幕所写的内容。为了简单起见,我们将忽略所有不寻常的键。在 `main.s` ,删除`bl SetGraphicsAddress` 之后的所有代码。调用 `UsbInitialise`,将 `r4` 和 `r5` 设置为 0,然后循环执行以下命令:
|
||||
|
||||
1. 调用 `KeyboardUpdate`
|
||||
2. 调用 `KeyboardGetChar`
|
||||
3. 如果返回 0,跳转到步骤1
|
||||
4. 复制 `r4` 和 `r5` 到 `r1` 和 `r2` ,然后调用 `DrawCharacter`
|
||||
5. 把 `r0` 加到 `r4`
|
||||
6. 如果 `r4` 是 1024,将 `r1` 加到 `r5`,然后设置 `r4` 为 0。
|
||||
7. 如果 `r5` 是 768,设置 `r5` 为0
|
||||
8. 跳转到步骤1
|
||||
|
||||
|
||||
|
||||
现在编译这个,然后在 PI 上测试。您几乎可以立即开始在屏幕上输入文本。如果没有,请参阅我们的故障排除页面。
|
||||
|
||||
当它工作时,祝贺您,您已经实现了与计算机的接口。现在您应该开始意识到,您几乎已经拥有了一个原始的操作系统。现在,您可以与计算机交互,发出命令,并在屏幕上接收反馈。在下一篇教程[输入02][3]中,我们将研究如何生成一个全文本终端,用户在其中输入命令,然后计算机执行这些命令。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input01.html
|
||||
|
||||
作者:[Alex Chadwick][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[ezio](https://github.com/oska874)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.cl.cam.ac.uk
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/downloads.html
|
||||
[2]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/downloads/hut1_12v2.pdf
|
||||
[3]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input02.html
|
217
translated/tech/20181108 My Google-free Android life.md
Normal file
217
translated/tech/20181108 My Google-free Android life.md
Normal file
@ -0,0 +1,217 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (LuuMing)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (My Google-free Android life)
|
||||
[#]: via: (https://lushka.al/my-android-setup/)
|
||||
[#]: author: (Anxhelo Lushka https://lushka.al/)
|
||||
|
||||
我的 Google-free 安卓之旅
|
||||
======
|
||||
|
||||
最近人们经常问我有关我手机的事情,比如安卓怎么安装,怎样绕过 Google Service 使用手机。好吧,这篇文章就来详细的解决那些问题。我尽可能让这篇文章适合初学者,因此我会说的很慢,一个一个来讲并且附上截图,你就能更好地看到它是怎样运作的。
|
||||
|
||||
首先我会告诉你为什么 Google Services(在我看来)对你的设备不好。我可以让你看 [Richard Stallman][2] 写的这篇[文章][1]并摘取一部分,但我决定抓住几个要点附在这。
|
||||
|
||||
* 需要<ruby>非自由软件<rt>Nonfree software</rt></ruby>
|
||||
* 大体上,许多 Google Services 需要运行在非自由的 Javascript 代码之上。现如今,如果禁用掉 Javascript,什么都没有了,甚至 Google 帐号都需要运行非自由软件(由站点发送的 JavaScript),对于登录也是。
|
||||
* 被监视
|
||||
* Google 悄悄地把他的<ruby>广告跟踪配置文件<rt>ad-tracking profiles</rt></ruby>与浏览配置文件结合在一起,并为每个用户存储大量数据。
|
||||
* 服务条款
|
||||
* Google 会终止转售 Pixel 手机用户的账户。他们无法访问帐户下保存在 Google Services 中的所有邮件和文档。
|
||||
* 审查
|
||||
* Amazon 和 Google 切断了<ruby>域前置<rt>domain-fronting</rt></ruby>,该技术能使身处专制国家的人们访问到那里禁止的通信系统。
|
||||
* Google 已经同意为巴基斯坦政府执行特殊的 Youtube 审查,删除反动内容。
|
||||
* Youtube 的“content ID”会自动删除已发布的视频,这并不包含在版权法中。
|
||||
|
||||
这只是几个原因,你可以阅读上面我提到的 RMS 的文章,他详细解释了这几点。尽管听起来骇人听闻,但这些行为在现实生活中已经每天在发生。
|
||||
|
||||
### 下一步, 我的搭建教程
|
||||
|
||||
我有一款[小米 Redmi Note 5 Pro][3] 智能手机(代号**whyred**),生产于中国的[小米][4]。它是 4 个月之前(写这篇文章的时候)我花了大约 185 欧元买的。
|
||||
|
||||
现在你也许会想,“但你为什么买中国品牌,他们不可靠”。是的,它不是通常你所期望的(品牌)所生产的,例如三星(人们通常会将它和安卓联系在一起,这显然是错的)、一加、诺基亚等。但你应当知道几乎所有的手机都生产于中国。
|
||||
|
||||
我选择这款手机有几个原因,首先当然是价格。它是一款相当高<ruby>**性价比**<rt>budget-friendly</rt></ruby>的产品,大多数人都能买得起。下一个原因(不仅仅)是说明书上的规格,在这个<ruby>价位<rt>price tag</rt></ruby>上相当合适。拥有 6 英尺屏幕(<ruby>全高清分辨率<rt>Full HD resolution</rt></ruby>),**4000 毫安电池**(一流的电池寿命),4GB RAM,64GB 存储,双后摄像头(12 MP + 5 MP),一个带闪光灯的前摄像头(13 MP)和一个高性能的<ruby>骁龙<rt>Snapdragon</rt></ruby> 636,它可能是那时候最好的选择。
|
||||
|
||||
随之而来的问题是 [MIUI][5],大多数小米设备附带的安卓皮肤(除了 Android One 项目设备)。是的,它没有那么可怕,它有一些额外的功能,但问题在更深的地方。小米设备如此便宜(据我所知销售利润仅有 5-10%)的一个原因是**他们在系统里伴随 MIUI 添加了数据挖掘和广告**。这样的话,系统应用需要额外不必要的权限来获取你的数据并且进行广告轰炸,从中获取利润。
|
||||
|
||||
更有趣的是,包含在内的天气应用想要访问我的联系人并且打电话,如果它仅是显示天气的话为什么需要访问。另一个例子是录音机应用,它也需要联系人和网络权限,可能想把录音发送回小米。
|
||||
|
||||
为了解决它,我不得不格式化手机并且摆脱 MIUI。这在最近市场上的手机上就变得极为艰难。
|
||||
|
||||
格式化手机的想法很简单,删除掉现有的系统然后安装一个新的喜欢的系统(这次是原生安卓)。为了实现它,你先得解锁 [bootloader][6]。
|
||||
|
||||
> bootloader 是一个在计算机完成自检后为其加载操作系统或者运行环境的计算机程序。—[维基百科][7]
|
||||
|
||||
问题是小米关于解锁 bootloader 有明确的政策。几个月之前,流程就像这样。你需向小米[申请][8]解锁代码,并提供真实的原因,但不是每次都成功,因为他们可以拒绝你的请求并且不提供理。
|
||||
|
||||
现在,流程变了。你要从小米那下载一个软件,叫做 [Mi Unlock][9],在 Windows 电脑上安装它,在手机的[开发者模式中打开调试选项][10],重启到 bootloader 模式(关机状态下长按音量下 + 电源键)并将手机连接到电脑上,开始一个叫做“同意”的流程。这个过程会在小米的服务器上启动一个定时器,允许你**在 15 天之后解锁手机**(在一些少数情况下或者一个月,完全随机)。
|
||||
|
||||
![Mi Unlock app][11]
|
||||
|
||||
15 天过去后,重新连接手机并重复之前的步骤,这时候按下解锁键,你的 bootloader 就会解锁,并且能够安装其他 ROM(系统)。**注意,确保你已经备份好了数据,因为解锁 bootloader 会清空手机。**
|
||||
|
||||
下一步就是找一个兼容的系统([ROM][12])。我在 [XDA 开发者论坛上][13]找了个遍,它是 Android 开发者和用户们交流想法、应用等东西的地方。幸运的是,我的手机相当流行,因此论坛上有它[专门的版块][14]。在那儿,我掠过一些流行的 ROM 并决定使用 [AOSiP ROM][15]。(AOSiP 代表<ruby>安卓开源 illusion 项目<rt>Android Open Source illusion Project</rt></ruby>)
|
||||
|
||||
**校订**:有人发邮件告诉我说文章里写的东西与目的就是[/e/][16]。我想说谢谢你的帮助,但完全不是这样。我关于 /e/ 的看法背后的原因可以见此[网站][17],但我仍会在此列出一些原因。
|
||||
|
||||
eelo 是一个从 Kickstarter 和 IndieGoGo 上集资并超过 200K € 的“基金会”,承诺创造一个开放、安全且保护隐私的移动 OS 和网页服务器。
|
||||
|
||||
1. 他们的 OS 基于 LineageOS 14.1 (Android 7.1) 且搭载 microG 和其他开源应用,此系统已经存在很长一段时间了并且现在叫做 [Lineage for microG][18]。
|
||||
2. 所有的应用程序并非从源代码构建,而是从 [APKPure][19] 上下载安装包并推送进 ROM,不知道那些安装包中是否包含<ruby>专有代码<rt>proprietary code</rt></ruby>或<ruby>恶意软件<rt>malware</rt></ruby>。
|
||||
3. 有一段时间,它们就那样随意地从代码中删除 Lineage <ruby>版权标头<rt>copyright header</rt></ruby>并加入自己的。
|
||||
4. 他们喜欢删除负面反馈并且监视用户 Telegram 群聊中的舆论。
|
||||
|
||||
总而言之,我**不建议使用 /e/** ROM。(至少现在)
|
||||
|
||||
另一件你有可能要做的事情是获取手机的 [root 权限][20],让它真正的成为你的手机,并且修改系统中的文件,例如使用系统范围的 adblocker 等。为了实现它,我决定使用 [Magisk][21],一个天赐的应用,它由一个学生开发,可以帮你获取设备的 root 权限并安装叫做[模块][22]的基础软件。
|
||||
|
||||
下载 ROM 和 Magisk 之后,我得在手机上安装它们。为了完成安装,我将文件移动到了 SD 卡上。现在,若要安装系统,我需要使用 [recovery system][23]。我用的是较为普遍的 [TWRP][24](代表 TeamWin Recovery Project)。
|
||||
|
||||
要安装 recovery system(听起来有点难,我知道),我需要将文件[烧录][20]进手机。为了完成烧录,我将手机用一个叫做 [ADB 的工具][25]连接上电脑(Fedora Linux 系统)。使用命令让自己定制的 recovery 系统覆盖掉原先的。
|
||||
|
||||
> fastboot flash recovery twrp.img
|
||||
|
||||
完成之后,我关掉手机并按住音量上和电源键,直到 TWRP 界面显示。这意味着我进行顺利,并且它已经准备好接收我的指令。
|
||||
|
||||
![TWRP screen][26]
|
||||
|
||||
下一步是**发送擦除命令**,在你第一次为手机安装自定义 ROM 时是必要的。如上图所示,擦除命令会清除掉<ruby>数据<rt>Data</rt></ruby>,<ruby>缓存<rt>Cache</rt></ruby>和 Dalvik 。(这里也有高级选项让我们可以勾选上来删除掉系统,如果我们不再需要旧系统的话)
|
||||
|
||||
这需要几分钟去完成,之后,你的手机基本上就干净了。现在是时候**安装系统了**。通过按下主屏幕上的安装按钮,我们选择之前添加进的 zip 文件(ROM 文件)并滑动屏幕安装它。下一步,我们需要安装 Magisk,它可以给我们访问设备的 root 权限。
|
||||
|
||||
**校订**:一些有经验的安卓用户或发烧友也许注意到了,手机上不包含 [GApps](谷歌应用)。这在安卓世界里称之为 GApps-less,一个 GAps 应用也不安装。
|
||||
|
||||
注意有一个不好之处在于若不安装 Google Services 有的应用无法正常工作,例如它们的通知也许会花更长的时间到达或者根本不起作用。(对我来说这一点是最影响应用程序使用的)原因是这些应用使用了 [Google Cloud Messaging][28](现在叫做 [Firebase][29])唤醒手机并推送通知。
|
||||
|
||||
你可以通过安装使用 [microG][30](部分地)解决它,microG 提供一些 Google Services 的特性且允许你拥有更多的控制。我不建议使用它,因为它仍然有助于 Google Services 并且你不一定信任它。但是,如果你没法<ruby>立刻放弃使用<rt>go cold turkey on it</rt><ruby>,只想慢慢地退出谷歌,这便是一个好的开始。
|
||||
|
||||
都成功地安装之后,现在我们重启手机,就进入了主屏幕。
|
||||
|
||||
### 下一个部分,安装应用并配置一切
|
||||
|
||||
事情开始变得简单了。为了安装应用,我使用了 [F-Droid][31],一个可替代的应用商店,里面**只包含免费和开源应用**。如果这里没有你要的应用,你可以使用 [Aurora Store][32],一个从应用商店里下载应用且不需要使用谷歌帐号或被追踪的客户端。
|
||||
|
||||
F-Droid 里面有 repos,一个包含你可以安装应用的“仓库”。我使用默认的仓库,并从 [IzzyOnDroid][33] 添加了另一个,它有更多默认仓库中没有的应用,并且它更新地更频繁。
|
||||
|
||||
![My repos][34]
|
||||
|
||||
从下面你可以发现我所安装的应用清单,它们替代的应用与用途。
|
||||
|
||||
AdAway > 系统广告拦截器,使用 hosts 文件拦截所有的广告
|
||||
AfWall+ > 一个防火墙,可以阻止不想要的连接
|
||||
Amaze > 替代系统的文件管理器,允许文件的 root 访问权限,并且拥有 zip/unzip 功能。
|
||||
Ameixa > 大多数应用的图标包
|
||||
andOTP > 替代谷歌验证/Authy,一个可以用来登录网站并报告哪里开启了<ruby>双因子验证<rt>2FA</rt></ruby>的 TOTP 应用。
|
||||
AnySoftKeyboard/AOSP Keyboard > 开源键盘,它有许多主题和语言包,我也是其项目一员
|
||||
Battery Charge Limit > 当到 80% 时自动停止充电,降低<ruby>电池磨损<rt>battery wear</rt></ruby>并增加寿命
|
||||
DAVx5 > 这是我最常用的应用之一,对我来说它基本上替代了谷歌联系人,谷歌日历和谷歌 Tasks,它连接着我的 Nextcloud 实例让我完全控制自己的数据
|
||||
Document Viewer > 一个可以打开数百种文件格式的查看器应用,快速、轻量
|
||||
Deezloader Remix (让我可以在 Deezer 上下载高质量 MP3 的应用)
|
||||
Easy xkcd > xkcd 漫画阅读器,我喜欢这些 xkcd 漫画
|
||||
Etar > 日历应用,替代谷歌日历,与 DAVx5 一同工作
|
||||
FastHub-Libre > 一个 GitHub 客户端,完全 FOSS(自由及开源软件),非常实用如果你像我一样喜欢使用 Github 扩展。
|
||||
Fennec F-Droid > 替代谷歌 Chrome 和其他类似的应用,一个 F-Droid 品牌的火狐浏览器,不含专有二进制代码并允许安装扩展提升浏览体验
|
||||
Gadgetbridge > 替代小米运动,可以用来配对小米硬件的应用,追踪你的健康,步数,睡眠等。
|
||||
K-9 Mail > 邮件客户端,替代 GMail 应用,可定制并可以添加多个账户
|
||||
Lawnchair > 启动器,可以替代 Nova Launcher 或 Pixel Launcher,允许自定义并有各样的改变,也支持图标包
|
||||
Mattermost > 可以连接 Mattermost 服务器的应用,Mattermost 可替代 Slack
|
||||
NewPipe > 最好的 YouTube 客户端 IMO,替代 YoubTube,它完全是 FOSS,免除 YouTube 广告,占用更少空间,允许背景播放,允许下载视频/音频等。试一试吧。
|
||||
Nextcloud SMS > 允许备份/同步 SMS 到我的 Nextcloud 实例
|
||||
Nextcloud Notes > 允许我创建,修改,删除,分享笔记并同步/备份到 Nextcloud 实例
|
||||
OpenTasks > 允许我创建,修改,删除任务并同步到 Nextcloud 实例
|
||||
OsmAnd~ > 一个地图应用,使用 OpenStreetMap,允许下载离线地图和导航
|
||||
QKSMS > 我最喜欢的 SMS 应用,替代 stock Messaging 应用,拥有漂亮的界面,拥有备份,个性化,延迟发送等特性。
|
||||
Resplash/Mysplash > 允许你无限地从 Unsplash 下载漂亮的壁纸,全部免费使用与修改。
|
||||
ScreenCam > 一个录屏工具,允许各样的自定义和录制模式,没有广告并且免费
|
||||
SecScanQR > QR 码识别应用,快速轻量
|
||||
Send Reduced Free > 这个应用可以在发送之前通过减少 PPI(<ruby>个人识别信息<rt>personally identifiable information</rt></ruby>)让你立即分享大图
|
||||
Slide > 开源 Reddit 客户端
|
||||
Telegram FOSS > 没有追踪和 Google Services 的干净版本 Telegram 安卓客户端
|
||||
TrebleShot > 这个天才般的应用可以让你通过 WIFI 分享文件,真的非常快,且无需连接网络
|
||||
Tusky > Tusky 是 Mastodon 平台的客户端(Mastodon 替代 Twitter)
|
||||
Unit Converter Ultimate > 这款应用可以在 200 种单位之间来回转换,非常快并且完全离线
|
||||
Vinyl Music Player > 我喜欢的音乐播放器,替代谷歌音乐播放器或任何其他音乐播放器,它有漂亮的界面和许多特性
|
||||
VPN Hotspot > 这款应用可以让我打开热点的时候分享 VPN,因此我可以不用做任何别的事情来安全地浏览网页,甚至是在笔记本上
|
||||
|
||||
这些差不多就是我列出的一张**最实用的 F-Droid 应用**清单,但不巧,这些并不是所有应用。我使用的专有应用如下(我知道,我也许听起来是一个伪君子,但并不是所有的应用都可以替代,至少现在不是):
|
||||
|
||||
* AliExpress
|
||||
* Boost for Reddit
|
||||
* Google Camera(与 Camera API 2 耦合,它可以让我用 185 欧元的手机拍一张非常棒的图片,真是太不可思议了)
|
||||
* Instagram
|
||||
* MediaBox HD (让我可以看流媒体电影)
|
||||
* Mi Fit (与小米手环 2 配对的应用)
|
||||
* MyVodafoneAL (运营商应用)
|
||||
* ProtonMail (email 应用)
|
||||
* Shazam Encore (识别咖啡店里你经常能听到的歌曲)
|
||||
* Snapseed (图片编辑应用,非常简单,强大,相当棒)
|
||||
* Spotify (在线音乐)
|
||||
* Titanium Backup(备份应用数据,wifi 密码,通话记录等)
|
||||
* ViPER4Android FX (音乐均衡器)
|
||||
* VSCO (图片编辑,从没真正用过)
|
||||
* WhatsApp (E2E 专有聊天应用,几乎我认识的所有人都有它)
|
||||
* WiFi Map(地图标注可用的热点,在国外非常方便)
|
||||
|
||||
差不多就是这样,我用的手机上所有的应用。**配置非常简单明了,我可以给几点提示**。
|
||||
|
||||
1. 仔细阅读和检查应用的权限,不要无脑地点“安装”。
|
||||
2. 尽可能多地使用开源应用,它们即尊重你的隐私又是免费的。(既“<ruby>free beer<rt>软件好用下次请我喝一杯</rt></ruby>” 又自由)
|
||||
3. 尽可能地使用 VPN,找一个有名气的,别用免费的,否则你将被收割数据然后成为产品。
|
||||
4. 不要一直打开 WIFI/移动数据/定位,有可能引起安全隐患。
|
||||
5. 不要只依赖指纹解锁,或者尽可能只用 PIN/密码/模式解锁,因为生物数据可以被克隆后反对你,例如解锁你的手机盗取你的数据。
|
||||
|
||||
作为坚持读到这儿的奖励,**一张主屏幕的截图奉上**
|
||||
|
||||
![Screenshot][35]
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://lushka.al/my-android-setup/
|
||||
|
||||
作者:[Anxhelo Lushka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[LuuMing](https://github.com/luuming)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://lushka.al/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://stallman.org/google.html
|
||||
[2]: https://en.wikipedia.org/wiki/Richard_Stallman
|
||||
[3]: https://www.gsmarena.com/xiaomi_redmi_note_5_pro-8893.php
|
||||
[4]: https://en.wikipedia.org/wiki/Xiaomi
|
||||
[5]: https://en.wikipedia.org/wiki/MIUI
|
||||
[6]: https://forum.xda-developers.com/wiki/Bootloader
|
||||
[7]: https://en.wikipedia.org/wiki/Booting
|
||||
[8]: https://en.miui.com/unlock/
|
||||
[9]: http://www.miui.com/unlock/apply.php
|
||||
[10]: https://www.youtube.com/watch?v=7zhEsJlivFA
|
||||
[11]: https://lushka.al//assets/img/posts/mi-unlock.png
|
||||
[12]: https://www.xda-developers.com/what-is-custom-rom-android/
|
||||
[13]: https://forum.xda-developers.com/
|
||||
[14]: https://forum.xda-developers.com/redmi-note-5-pro
|
||||
[15]: https://forum.xda-developers.com/redmi-note-5-pro/development/rom-aosip-8-1-t3804473
|
||||
[16]: https://e.foundation
|
||||
[17]: https://ewwlo.xyz/evil
|
||||
[18]: https://lineage.microg.org/
|
||||
[19]: https://apkpure.com/
|
||||
[20]: https://lifehacker.com/5789397/the-always-up-to-date-guide-to-rooting-any-android-phone
|
||||
[21]: https://forum.xda-developers.com/apps/magisk/official-magisk-v7-universal-systemless-t3473445
|
||||
[22]: https://forum.xda-developers.com/apps/magisk
|
||||
[23]: http://www.smartmobilephonesolutions.com/content/android-system-recovery
|
||||
[24]: https://dl.twrp.me/whyred/
|
||||
[25]: https://developer.android.com/studio/command-line/adb
|
||||
[26]: https://lushka.al//assets/img/posts/android-twrp.png
|
||||
[27]: https://opengapps.org/
|
||||
[28]: https://developers.google.com/cloud-messaging/
|
||||
[29]: https://firebase.google.com/docs/cloud-messaging/
|
||||
[30]: https://microg.org/
|
||||
[31]: https://f-droid.org/
|
||||
[32]: https://f-droid.org/en/packages/com.dragons.aurora/
|
||||
[33]: https://android.izzysoft.de/repo
|
||||
[34]: https://lushka.al//assets/img/posts/android-fdroid-repos.jpg
|
||||
[35]: https://lushka.al//assets/img/posts/android-screenshot.jpg
|
||||
[36]: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
Loading…
Reference in New Issue
Block a user