/64
的IPv6地址。然而根据官方文档需要手动配置,甚至官方都知道自己的RA(Router Advertisement)是有问题的,需要配置服务器不接受RA。最让人无法理解的是不仅需要配置静态地址,甚至连网关路由都得静态配置。如果只是给一台机器用,纯静态问题不是很大。但是如果需要给虚拟机继续分配,简直就是灾难。虽然理论上可以把外网网卡桥接到所有VM并手动配置,但这样做终究还是不方便。有个老哥甚至写了篇文章吐槽OVH的IPv6:IPv6 setup in two hosting providers compared: awful (OVH) and awesome (Online.net)。这篇文章虽然讲了需要配置NDP代理,并没有什么实际的可操作性。经过一大番折腾我才终于彻底解决OpenWrt给其他VM分配地址的问题。WAN6没什么特别的,配置静态地址+前缀就完事了。
路由也没什么特别的,按道理IPv6应该用来自RA的Link-local地址自动配置路由,然而OVH家并没有能正常使用的RA,只能用静态路由。
首先当然是配置好IPv6分配的长度:
接下来重点来了。如果只配置到这一步,你会发现其他VM可以获取到正常的stateful和stateless地址,但是如果尝试ping外网地址:ping6 google.com
你会发现根本不通!这是因为像前面提到的那篇文章所说的,外面的路由器并不知道这个地址的存在,所以需要配置NDP代理为relay,让软路由帮其他VM通告它们的地址:
再尝试ping外网地址,通了!Hooray!
到这就结束了吗?那你就想多了。
过大约30分钟后,你会发现,从OpenWrt依旧可以ping通外网v6,VM里面的v6地址还在,VM的v6路由表也正常,然而又ping不通了!
这就是更大的坑了,DHCPv6的地址默认是无限期的,看起来NDP代理的地址通告只会在获取地址的时候进行一次,过了半个小时由于没有继续通告,外面的路由器就忘记还有这个VM的存在了。为了解决这个问题,我们需要降低DHCP的租期,比如15分钟:
然后你会发现DHCPv6的租期还是无限期。这时需要登录OpenWrt的命令行(比如SSH),编辑/etc/config/dhcp
,在LAN配置下方加入option ra_useleasetime '1'
:
然后重启dnsmasq或者直接重启OpenWrt。
接着DHCPv6就works as intended了。所有VM都能获取并保持IPv6地址,即使过一天也不会失去公网访问,从外面也可以ping通VM的v6地址。
]]>iPhone XS/XS Max和新iPad Pro搭载了内置神经网络加速器的A12芯片。然而,waifu2x-ios在这块“性能怪兽”上似乎有点水土不服,输出图片会有明显的色阶(见此issue)。手上并没有设备进行调试,所以只能靠推测认为是因为神经引擎降精度运行导致的。即使有设备调试,也就能看看GPU Frame,多半也没有什么好解决办法,因为Core ML内部就是个黑盒,外面无从得知里面到底怎么运行的,更不可能通过一些手段介入。所以,是时候考虑增加MPSNN作为后端了。
作为更加底层的框架,MPSNN没有定义任何数据格式。神经网络中的weights和bias只接受按照顺序存储的32位浮点数指针。考虑到Core ML已经保存有包括网络结构在内的全部数据,完全有可能直接通过程序读取mlmodel中的数据构建MPSNN网络。这样一来既有了现成的数据格式,又可以节省另外存储模型的空间,还能同时兼容现有的Core ML模式。
经过搜索找到一篇详细讲解Core ML编译后格式的博客:Reverse Engineering Core ML
简单来说,Core ML在编译后会把模型的数据保存在以.mlmodelc
结尾的文件夹中(这很Apple)。其中model.espresso.shape
存储网络每一层的形状,实际上应该是被用来限制Core ML中feature的大小。model.espresso.net
存储网络结构,为JSON格式。model.espresso.weights
以32位浮点数的形式存储所有的weights和bias,数据没有任何加密。
JSON文件中每个对象代表网络的一层,top
和bottom
字段分别表示上一层和下一层的feature名称(和节点名称name
不一样)。通过这些信息可以建立一个有向无环图,只要知道输入feature和输出feature的名称即可通过遍历这个图构建出路径。值得注意的是激活层(activation)和卷积层(convolution)是分开的,而在MPSNN的API中这两层需要被放到同一个节点(node)中。对于waifu2x这种只有一条路径的网络,构建起来很简单,甚至不需要考虑一个feature需要用两次的情况。
bias很简单,通过blob编号找到位置,按顺序复制就完事了。而weights相对来说就有点麻烦,因为Core ML和MPSNN对weights顺序的定义不一样。Core ML存储顺序为[outputFeatureChannels inputFeatureChannels kernelWidth kernelHeight]
,而MPSNN存储顺序为[outputFeatureChannels kernelHeight kernelWidth inputFeatureChannels]
。而在反卷积核中,MPSNN的格式不变,Core ML的格式变成了[inputFeatureChannels outputFeatureChannels kernelWidth kernelHeight]
。这种不一致导致必须在将weights传给MPSNN前将格式进行转换。
由于完全运行在GPU上,MPSNN必须在真机上才可以运行。刚开始网络输出一片空白,通过抓取GPU Frame得知是因为alpha通道因为不明原因而全是0。这时MPSNN的优势就体现出来了,直接在网络输出的MPSImage后面增加一个kernel将alpha填1,输出就正常了。
]]>根据SDK里面的教程,在格式化成FAT32的U盘里面创建目录\steamlink\config\system
,里面创建文件enable_ssh.txt
,将其插入Steam Link后开机,通过root密码steamlink123即可登录ssh。
由于CPU基于ARMv7,可以使用到OpenWrt官网下载mvebu/cortexa9
的固件,这里以Linksys WRT1200AC为例,在Linux环境下(WSL就行)下载linksys-wrt1200ac-squashfs-sysupgrade.bin
,应该会得到类似openwrt-xxx-sysupgrade.bin
的文件。
sysupgrade固件分为两层,最外面是未压缩的tar,里面是squashfs,直接对其解包:1
2
3tar xf openwrt-xxx-sysupgrade.bin
cd sysupgrade-armada-385-linksys-caiman
unsquashfs -f -d openwrt root
经过此操作后,固件内容就会被解包到openwrt/里面:1
2$ ls openwrt
bin dev etc lib mnt overlay proc rom root sbin sys tmp usr var www
进入openwrt目录,创建文件chroot.sh,内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20SHELL=ash #如果已安装zsh之类的shell,可以在这里替换成zsh
MY_PATH="`dirname \"$0\"`"
cd $MY_PATH
HOME=/root
if [ ! -f dev/null ]; then
mount -t proc proc proc/ || true
mount -t sysfs sys sys/ || true
mount -o bind /dev dev/ || true
mount -o mode=1777,nosuid,nodev -t tmpfs tmpfs tmp/ || true
mkdir -p var/run
mkdir -p var/lib
mkdir -p var/log
mkdir -p var/lock
mount -o mode=1777,nosuid,nodev -t tmpfs tmpfs var/run || true
fi
if [ $# -eq 0 ]; then
chroot . $SHELL
else
chroot . "$@"
fi
打包的重点是要保留符号链接,因为几乎所有的Linux命令都指向busybox,这里使用.tar.gz
可以很好的满足要求:1
tar czf openwrt.tar.gz openwrt
Steam Link根目录下大部分地方为只读文件系统,所以直接将OpenWrt安装到根目录是不可能的,这里需要变通一下,将其装到可写的/home/apps
:1
scp openwrt.tar.gz root@steamlink-xxxx:/home/apps
其中xxxx为大写的序列号后四位,可以在包装上看S/N,也可以在Steam Link界面设置->系统
里面查看。
ssh登录Steam Link,运行以下命令:1
2
3
4
5
6
7cd /home/apps
tar xzf openwrt.tar.gz
cd openwrt
rm var
mkdir var
chmod +x chroot.sh
./chroot.sh
熟悉的ash界面出现了:
在这个环境中,可以很方便的用opkg安装、卸载软件包,使用方法跟OpenWrt完全一样。
既然有了opkg,换个好用的shell是必不可少的。运行以下命令:1
2
3opkg update
opkg install zsh curl ca-bundle git-http
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
编辑chroot.sh,将ash替换成zsh,重新运行./chroot.sh
,就可以看到oh-my-zsh生效了:
如果需要安装开机启动的应用,必须从OpenWrt外部启动。以nginx为例,首先创建必需的文件夹:1
2mkdir -p /var/log/nginx
mkdir -p /var/lib/nginx/body
运行nginx
,如果没有报错,尝试在浏览器中访问http://steamlink-xxxx/
,如果能打开页面,说明安装成功:
输入exit
退出OpenWrt环境,创建文件/etc/init.d/startup/S90nginx
,加入以下内容:1
2
/home/apps/openwrt/chroot.sh nginx
运行/etc/init.d/reboot.sh
重启Steam Link,等待完成后如果能通过浏览器访问nginx,说明自启动服务创建成功。
本文以老司機鎖屏内置的默认yande.re路线为例进行编写,当然用同样的道理也可以做“必应每日壁纸”等类似的应用,在这里推荐一个Chrome插件XPath Helper,可以很方便的查看网页元素的XPath。
mac的命令行内置了几乎全部需要使用的工具,只需要多安装wget
,具体方法就不多说了。
Automator中的bash环境并不包含~/.bashrc
,所以如果wget
安装在/usr/local/bin
之类的位置就会报命令不存在的错误。因此脚本的第一步就是得设置环境变量:1
PATH=$PATH:/usr/local/bin
1 | export http_proxy=http://127.0.0.1:1087 |
头两行属于科学上网(原因众所周知),不过一般来说不加问题也不大,只会略微增大翻车频率。第3行指定了壁纸保存的位置,第4行设置了保留壁纸数量的上限。search_query
这个参数为图片的搜索条件,具体可以参考yande.re上的说明。
Finder在系统刚启动的时候无法接受osascript的指令,会导致设置壁纸失败。同时科学上网工具也需要时间启动,所以这里需要加一个延迟,具体时长可以根据实际情况修改。1
sleep 15
1 | encoded_query=`python -c "import sys, urllib as ul; print ul.quote_plus(sys.argv[1])" "$search_query"` |
首先借助python标准库中的工具将搜索条件进行URL编码,然后使用curl获取搜索结果页面并保存在变量中。
1 | pids=(`echo $list_html|xmllint --html --xpath "//ul[@id='post-list-posts']/li[count(a/span[2][substring-before(text() , 'x') >= substring-after(text() , 'x')]) > 0]//a[@class='thumb']/@href" - 2>/dev/null`) |
借助xmllint
,可以很容易使用XPath解析HTML字符串。这里的XPath来自老司機鎖屏应用。最后结果以数组形式保存在pids
中。
这个操作只是为了和老司機鎖屏应用保持相同的逻辑,不过也可以实现每次登录都更换壁纸的效果(当然也得看人品)。最后选出来的图片ID保存在pid
中。1
2
3pid_count="${#pids[@]}"
rand_idx="$(( ( RANDOM % pid_count ) + 1 ))"
pid=`echo ${pids[rand_idx]}|awk 'BEGIN { FS = "show\/" } ; { print $2 }'|awk 'BEGIN { FS = "\"" } ; { print $1 }'`
1 | purl="https://yande.re/post/show/$pid" |
杀鸡焉用牛刀?由于我们需要的图片链接只有一个,这里提取地址就没必要使用XPath了,所以使用更简单的grep
和awk
。
1 | ext=`echo $picurl|awk 'BEGIN { FS = "." } ; { print $NF }'` |
1 | osascript -e "tell application \"Finder\" to set desktop picture to POSIX file \"$picpath\"" |
这里就属于mac特色了。当然经过修改也能在Linux上实现相同的功能。
开车怎能不带行车记录仪?为了追踪历史使用的图片,这里将图片地址及时间戳写入文件中:1
2
3
4
5
6
7ret=$?
if [ $ret -eq 0 ]
then
echo "$(date '+%Y-%m-%d %H:%M:%S'): $purl" >> $download_path/wallpaper.log
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): $purl Failed $ret" >> $download_path/wallpaper.log
fi
接下来是完整的代码,可以直接在Automator中封装成app并添加启动项。具体操作教程一大把,这里就不赘述了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
PATH=$PATH:/usr/local/bin
# Parameters
export http_proxy=http://127.0.0.1:1087
export https_proxy=http://127.0.0.1:1087
download_path="$HOME/wallpaper_dir"
image_limit=10
mkdir -p $download_path
## Query, reference: https://yande.re/help/cheatsheet
search_query="rating:s score:>5 -bikini -bra -pantyhose -bikini_armor"
# Wait for system loading
sleep 15
# Search for images
encoded_query=`python -c "import sys, urllib as ul; print ul.quote_plus(sys.argv[1])" "$search_query"`
list_html=`curl https://yande.re/post\?tags\=$encoded_query\&commit\=Search 2>/dev/null`
# Get URLs via XPath
pids=(`echo $list_html|xmllint --html --xpath "//ul[@id='post-list-posts']/li[count(a/span[2][substring-before(text() , 'x') >= substring-after(text() , 'x')]) > 0]//a[@class='thumb']/@href" - 2>/dev/null`)
# Randomly select an image
pid_count="${#pids[@]}"
rand_idx="$(( ( RANDOM % pid_count ) + 1 ))"
pid=`echo ${pids[rand_idx]}|awk 'BEGIN { FS = "show\/" } ; { print $2 }'|awk 'BEGIN { FS = "\"" } ; { print $1 }'`
# Download image detail page
purl="https://yande.re/post/show/$pid"
picurl=`curl $purl 2>/dev/null|grep '"original-file-changed"'|awk 'BEGIN { FS = "href=\"" } ; { print $2 }'|awk 'BEGIN { FS = "\">" } ; { print $1 }'`
# Download image
ext=`echo $picurl|awk 'BEGIN { FS = "." } ; { print $NF }'`
picpath="$download_path/$pid.$ext"
wget $picurl -O $picpath 2>/dev/null
# Set wallpaper
osascript -e "tell application \"Finder\" to set desktop picture to POSIX file \"$picpath\""
# Remove old wallpapers
ls -1t $download_path/*.jpg | tail -n +$(($image_limit + 1)) | xargs rm
# Log
ret=$?
if [ $ret -eq 0 ]
then
echo "$(date '+%Y-%m-%d %H:%M:%S'): $purl" >> $download_path/wallpaper.log
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): $purl Failed $ret" >> $download_path/wallpaper.log
fi
1 | # ITU-R BT.601 |
DOSBox是一个x86模拟器,使用平台无关的代码实现了基本的
x86处理器指令集,可以用来运行大部分DOS游戏。DOSBox SVN Daum是DOSBox的改良版本,增加了大量高级功能,甚至可以运行Windows 98。dospad是DOSBox的iOS的移植版,使用原版DOSBox 0.74。然而,由于DOSBox官方太久没有更新,性能已经远远落后于Daum版本,兼容性也差很多。
经过几天的折腾,我已经成功将dospad中的DOSBox替换成Daum版本。经过测试,在Simple模式下可以在17款iPad Pro上模拟出最高100MHz的奔腾处理器,如果能用arm64汇编实现指令集速度应该至少还能翻3倍。
修改后的代码已经放在fork里: https://github.com/imxieyi/dospad
安装方法推荐自行编译。如果实在没法找到XCode环境,这里提供一个未签名的ipa,可以使用Cydia Impactor、iResign等重签名工具安装: dospad.ipa
链接前面已经给出。PC版因为可以使用原生x86汇编,性能比iOS设备强很多,所以推荐在PC上进行前期安装工作。当然,如果你有足够的耐心,在iOS上安装也完全没有问题。
为了实现虚拟IDE驱动器,挂载img镜像是必不可少的。镜像可以跟着这个教程创建,也可以直接下载现成的。由于iOS系统限制,直接在dospad中运行imgmake命令会报错,所以这一步必须在PC上进行。
MuLinux是非常轻量级的Linux发行版,可以直接在DOS环境下引导。DOSTOOLS和mulinux-*.tgz两个文件必不可少,剩余的addons则可以根据需要下载,tgz文件都不要提前解压。全部下载完后,将DOSTOOLS
解压到电脑版dosbox文件夹中mulinux下,剩下的压缩包也放进这个文件夹,mulinux-*.tgz
重命名为mu.tgz
,然后打开dosbox.conf进行修改,在文件最后的[autoexec]
部分增加以下内容:1
2imgmount c linux.img
mount d mulinux
其中mulinux
及linux.img
请根据实际情况修改。
然后下载MSDOS 6.22启动盘,将622C.IMG
也放到mulinux目录下。
启动dosbox,输入以下命令启动MSDOS:1
boot d:\622c.img
然后运行fdisk
,连续按回车直到提示重启。如果显示Primary DOS Partition already exists
,就连续按ESC退出fdisk。
重启dosbox后,用同样的方法进入MSDOS中,运行:1
format c:
输入Y
同意,label随便写一个或者不写,然后关闭dosbox。
挂载目录无法模拟IDE设备,所以必须将相关文件复制到镜像中。
再次启动dosbox,输入以下命令:1
2
3
4c:
md mulinux
cd mulinux
xcopy /s /r d:\ .
终于进入正片了。输入以下命令:1
2unpack.bat
boot.bat
等待一段时间后会显示以下界面:
选择2
并回车,然后等待安装,这一过程需要较长时间,直到显示以下界面:
按回车后选择0
并回车,dosbox会自动重启。
首次启动需要进行大量设置,所以建议也在电脑上完成。
输入以下命令进入MuLinux中:1
2
3c:
cd linux
linux.bat
系统会挨个询问是否安装addon,建议为了启动速度尽量少安装,尤其是会启动服务的插件如SRV和VNC。X11建议也不要安装,桌面环境是无法启动的。EMU一定不要安装,会导致系统启动时崩溃。由于之前已经将插件放进mulinux目录中,系统可以自动找到相关文件。如果找不到,路径格式为/DOS/path/to/tgz
,其中path/to/tgz
为挂载的C盘中的绝对路径。
addon安装完成后,会显示如下界面:
这里选择minimal或lazy即可,等待其配置完毕。Available Wizards
界面移动光标至Editor-Wizard
后按回车,选择e3开头的任意一个编辑器(根据偏好),等待配置完成后选择Cancel
退出。tour
界面选择No
(有兴趣看内置的文档也可以选择Yes
)。接下来就会出现登录提示了:
输入账号root
,接下来就会出现shell,直接运行shutdown now
,dosbox自动重启。
用和之前相同的命令再次启动MuLinux,直到出现以下配置界面:
一路回车即可,直到再次提示登录,进入shell,运行shutdown now
,重启后直接关闭dosbox。
将设备连上电脑,使用iTunes文件共享将linux.img
导入DOSPAD Beta
中:
另外新建一个文本文件,保存为linux.bat
,内容如下:1
2
3
4imgmount e d:\linux.img
e:
cd linux
linux.bat
也将其用iTunes导入。
打开iOS设备上的DOSPAD Beta
,输入以下命令(推荐使用物理键盘):1
2d:
linux.bat
接下来就是漫长的启动过程。如果不想等太久,也可以在出现Press -ENTER-
的时候按回车跳过配置读取。
当以下界面出现时,说明已经大功告成:
用root登录后就可以随意折腾了。
可以看到内核已经相当老了,不过好歹也是真正的Linux,跑些内置命令是完全没问题的,还能运行gcc编译器,Java编译器和虚拟机也可以:menu
命令可以打开主菜单,如果前面配置有问题可以在这里更改。
对于nginx和Apache,网上已经有大量教程,不管是什么样的服务都能找到相应的配置。然而由于使用IIS的人数相对较少,对于IIS的反向代理几乎没有什么人去研究。连进行URL重写的AAR模块也没什么相关资料。
如果服务只需要用HTTP,配置相对简单。首先添加入站规则,匹配模式
设为xxx/(.*)
,重写URL
设为http://ip.address.or.domain/{R:1}
,同时勾选附加查询字符串
和停止处理后续规则
就搞定了。
然而,很多时候光转发HTTP是完全不够的。许多服务在使用HTTP的同时还会打开WebSocket连接,URL为ws://
或者wss://
开头。这种情况下如果只转发HTTP,服务就无法正常使用,比如jupyter就无法启动内核。所以,一般来说最好能同时转发HTTP和WebSocket流量。
使用IIS实现起来并不复杂,然而由于资料较少,我没少走弯路。除了之前转发HTTP的配置外,在条件
下添加一项,条件输入
为{CACHE_URL}
,模式为(.+)://
,然后重写URL
改为{C:1}://ip.address.or.domain/{R:1}
,问题迎刃而解。
可以看到,效果跟gnome-terminal等Linux原生Shell有一定差距,不过对于Win10的CMD来说已经相当不错了。
接下来是具体实现步骤:
参考cmd-colors-solarized修改CMD的默认配色为Solarized Dark/Light
。README里面说的很详细,这里就不赘述了。
从powerline/fonts里下载字体。经过测试只有DejaVuSansMono
系列可以在CMD中完美兼容Powerline,所以只安装这一系列就够了(不需要Powerline符号支持的略过)。如果想全装,建议不要用文件夹中的powershell脚本,它是一个一个安装的,速度极慢,直接搜索ttf然后批量安装即可。
打开注册表,定位到HKEY_CURRENT_USER\Console\%SystemRoot%_System32_bash.exe
。增加一个DWORD
项,命名为CodePage
,值设为十进制65001
。再增加一个字符串
项,命名为FaceName
,值为DejaVu Sans Mono for Powerline
。如果Linux子系统是从Windows应用商店安装的,Console
项下面应该还会有个C:_Program Files_WindowsApps_
开头的项,也需要进行相同的修改。
做到这一步,剩下的就跟原生Linux下一样了,教程网上也一大把。唯一不同的就是不需要在Linux子系统中安装任何字体,遇到相应步骤跳过即可。
]]>1 |
|
效果如下:
时间一天天过去,转眼iOS 11就要正式发布了,我也拥有了付费的开发者账号。在将XCode更新到9的正式版后想起来了当初的这个idea,于是开始着手实现。
所谓“知己知彼,百战不殆”,首先当然是要学会Core ML的使用方法。Awesome-CoreML-Models收集了不少基于Core ML的开源项目,对于我这种初学者来说是非常好的学习资料。
移植过程其实非常简单:首先将waifu2x-caffe中提供的caffe版模型用python脚本转换成Core ML格式,然后导入XCode中会自动生成Swift类,用这个类中的prediction方法即可运行模型预测。
最后上代码:https://github.com/imxieyi/waifu2x-ios
顺便说一句这个应用已经上架App Store:
]]>最近在折腾各种iOS的模拟器游戏,从能装Windows 95的dospad到覆盖全平台的PPSSPP都玩了个遍。但是,除了dospad之外都普遍存在一个问题:不兼容iPad Pro的Smart Keyboard。
dospad不仅能完美识别Smart Keyboard上的按键,还把部分按键映射进行了优化,比如把左上角的“`”替换成了ESC键,大大增强了用户体验。这说明Smart Keyboard的原始输入本身是可以被iOS应用读取的。
PPSSPP经过几年的发展,也得益于A10X处理器的强大性能,在未越狱设备上不开JIT运行也十分流畅。可惜它不支持Smart Keyboard。
经过各种试验,我发现一个有趣的现象:有的时候键盘上的wsad键能被识别成gamepad输入的方向键。不过它只能识别按下而不能识别弹起,所以按下后就会卡住,得通过屏幕虚拟按键释放。
查看输入部分代码发现PPSSPP支持一种叫iCade的控制器。它的输入使用UIViewController中提供的文本读取接口实现。而dospad中的输入实现更加底层,直接通过UIApplication中的接口读取按键代码和状态。显然,后者的实现方式更加灵活。
这样一来目的就很明确了:实现UIApplication的子类,并用其启动应用,里面实现读取键盘原始输入的方法,然后将按键代码和状态映射到PPSSPP的键盘输入。经测试,Smart Keyboard上的除了左下角地球键外每一个按键都有对应的代码。
最终效果还不错,输入基本感觉不到延迟。
演示视频:https://youtu.be/F8D0Zu1xrJ0
修改后的PPSSPP代码:https://github.com/imxieyi/ppsspp
修改及添加的文件全部位于ios目录下。
]]>1 | import UIKit |
使用方法很简单,直接在UIImaveView的Class中指定BlendImageView,然后就会出现Color和Alpha的选项,修改后还能实时显示结果。
]]>整个系统基本照着《30天自制操作系统》(OSASK)这本书上的代码写。不过,我在系统UI方面一直是颜控,这样做出来的操作系统只有256色,而且处处受制于所谓的调色板,界面连我自己都看不下去。经过参考其他hobby OS的代码,发现了multiboot这一系统规范,其中最吸引我的就是32位颜色以及最高4k分辨率(需要efi)的原生支持。
经过几天的努力,我成功将大部分OSASK的代码移植到了multiboot中,并尽量保持了API的一致。
为了实现炫(zhuang)酷(bi)的界面,OSASK里面关于绘图的代码当然不能直接用了。
最麻烦的就是透明度。OSASK里面的256色界面几乎没有透明度的概念,而我特别喜欢的半透明窗口边框要求必须引入透明度,所以各图层的缓冲区就得用32位保存。接下来还得重写刷新缓冲区的代码。
考虑到同时叠加的半透明图层可能不止一层,刷新部分使用了简单的递归,也就是每个像素从最上面一层开始往下刷新直到透明度为0。
最终效果确实不错。开始没有引入窗口拖动,所以并没有觉得性能很差。而在实现窗口拖动后能感到严重的延迟。为了优化性能我尝试给每个图层加一个缓冲区,每次只刷新变化了的像素,可惜由于内存管理存在未知bug而反复系统崩溃。
突然想起来计算机组成原理课上讲过关于缓存优化的部分。我们知道CPU缓存的作用是加速大部分内存访问,其核心思想就是Direct mapped cache,也就是说每个内存地址都对应到固定的一块缓存区,每次遇到Cache Miss就把那一块内存搬到缓存中。既然是“块”,就是连续的。到此为止思路就很明确了,尽量连续操作内存就能大大降低缓存的Miss Rate,平均下来的内存访问延迟也就会大大降低。
在显存里面像素是逐行储存的。之前实现刷新像素时由于个人习惯先循环x后循环y。表面看上去没啥问题,实际上每次循环y+1时总体内存偏移量很大,造成了大量没必要的Cache Miss。而简单替换两层循环后刷新速度翻了好几倍,甚至拖动窗口都不卡了。有些时候只需要一点简单的trick就能起到非常显著的效果。
优化前:
优化后:
LEDE是著名开源路由器系统项目OpenWrt的继任者。它继承了OpenWrt极高的自由度和海量的插件,可以轻松实现广告过滤、PT挂种、Samba共享等强大且实用的功能。同时,它也带来了很多问题,最麻烦的就是发热。随便运行几个插件就能让温度稳在80℃以上,在夏天尤其危险。温度过高轻则导致路由器重启,重则烧坏芯片,所以随时了解路由器温度显得十分重要。
对于iOS用户来说,没有什么监测工具比HomeKit更方便的了。所以很容易想到用HomeBridge把路由器接入HomeKit中。接下来就是详细步骤(以Linksys WRT1200AC和CHIP Pro为例):
安装HomeBridge并不难,按照官方教程很快就能搭建起来。如果你要用树莓派、CHIP等单板电脑7*24小时运行HomeBridge,一定记得加装散热片,最好也加上散热风扇。如果你也用的CHIP Pro,一定还要加上WiFi天线。同时,我建议不要尝试在路由器上运行HomeBridge。
sshpass可以实现在命令行参数中包含ssh密码,这样就不需要设置ssh证书或者将路由器密码留空。1
sudo apt install sshpass
使用ssh客户端登陆路由器,运行以下命令:1
2
3opkg update
opkg install lm-sensors
sensors
如果安装成功,会出现类似下图的输出内容:
因为我们需要检测的参数可以很容易通过命令行获得,所以使用homebridge-temperature-cmd插件即可。1
npm install -g homebridge-temperature-cmd
安装完成后,找到/usr/lib/node_modules/homebridge-temperature-cmd/index.js,将1
var res = Math.round(stdout * 100) /100;
修改成1
var res = Math.round(parseFloat(stdout) * 100) /100;
如果不修改,很容易出现命令行运行指令没问题,而手机查询时配件无响应的谜之问题。
ssh最后一个参数就是运行的命令,运行完成后就会自动登出。搭配sshpass就能实现自动登陆同时运行一条命令。接下来只需要通过命令行提取输出内容。
插件配置格式如下:1
2
3
4
5{
"accessory": "TemperatureCMD",
"name": "配件名称",
"command": "具体命令"
}
以上图的sensors输出为例,我们需要得到第三个温度(CPU温度)。在安装HomeBridge的设备上运行:1
sshpass -p [password] ssh root@[192.168.1.1] 'sensors'|grep temp1|sed -n '2,1p'|awk -F '[+]' '{print $2}'
请将中括号中的内容根据实际情况替换。
输出内容应该只有一个温度。
注:grep temp1用于筛选出带temp1的行。而我们看到sensors输出中有两行含有temp1,所以用sed选择第二行。接下来的awk以“+”作为分隔符分割并输出后面的内容。由于前面已经在插件中作出修改,剩下的非数字内容会被nodejs自动过滤掉。
路由器负载会直接影响路由器温度。uptime命令可以用于获取路由器平均负载。同样的道理,运行:1
sshpass -p [password] ssh root@[192.168.1.1] 'uptime'|awk -F '[:]' '{print $5}'|awk -F '[,]' '{print $1}'
输出内容应该只有一个浮点数,代表1分钟平均负载。
建议将负载放入homebridge-humidity-cmd中,这样就会以百分比方式显示,同样需要修改插件的index.js,修改中最后的“/100”请去掉。配件格式和温度插件完全一样,只需要将accessory字段设置成HumidityCMD。
CHIP Pro不同于树莓派,并没有提供简单的获取温度方式,我们需要直接读取i2c总线。
将以下内容保存到/root/cputemp.sh:1
2
3
4
5
6
7
#read Internal Temperature 5Eh, 5Fh (0x000)-144.7 C -> 264.8 C(0xFFF) , 0.1°C / bit
TEMPERATURE_LSB=$(i2cget -y -f 0 0x34 0x5f)
TEMPERATURE_MSB=$(i2cget -y -f 0 0x34 0x5e)
TEMPERATURE_BIN=$(( $(($TEMPERATURE_MSB << 4)) | $(($TEMPERATURE_LSB & 0x0F))))
TEMP_DEGC=$(echo "(($TEMPERATURE_BIN/10)-144.7)"|bc)
echo $TEMP_DEGC
然后尝试运行脚本,输出为当前CPU温度。
目前实现的功能:
StoryBoard播放演示视频:
http://www.bilibili.com/video/av9580463
http://www.bilibili.com/video/av9582040
http://www.bilibili.com/video/av9582174
http://www.bilibili.com/video/av9582511
只要是音游爱好者,肯定不会不知道osu!这款游戏。在所有音游中,osu!将开放性做到了极致,任何人都可以制作谱面并提交,所以osu!的曲库虽然质量参差不齐,但是数量上绝对能碾压所有其他的音游。而且它独特的背景视频和StoryBoard机制可以大大增加游戏的乐趣。
不少人第一眼看到osu!都会怀疑它到底是不是PC游戏,他们吐槽“这种游戏就应该在手机平板上玩吧”。的确,这种全屏幕点击的核心玩法在触摸屏上看起来更简单。但事实上触摸屏在高速连打时就显得无能为力了。即便如此,很多人还是希望能随时随地来上几把,而不仅仅只能在电脑上玩。
大约在5年前osu!官方出了款叫osu!droid的Android游戏,它可以读取PC版osu!的谱面,拥有接近PC的体验,甚至可以改皮肤。不过由于当时手机性能普遍较低(或者开发者太懒),它只能显示背景图片,并不能显示背景视频或StoryBoard。可惜的是这个项目最后一次的更新在2014年。后来的版本由贴吧大神们基于osu!droid制作维护,还加入了在线排名功能。
在iOS平台上,有个叫osu!stream的游戏,早在2011年就发布。不过,这跟PC版的osu!是完全不同的两个游戏。它不能读取PC版的谱面,歌曲也少得可怜,其中很大比例还需要付费。除此之外,官方论坛上还有人开发了osu! iPhone,不过早就停止更新,而且只能在越狱设备上运行。
osu!从2016年8月开始全部开源,所有代码放在https://github.com/ppy/osu上。事实上早在2014年就有大神用Java语言编写了opsu,游戏体验和原版osu!非常接近,在PC上甚至可以播放背景视频(Android版依旧不行)。
最近在学习iOS开发,以我的习惯,从头跟着教程系统地学习是学不下去的。突然想到iOS上还没有一个相对完整的osu!,于是决定从零开始开发iOS版的osu!,目标就是去一点一点实现PC版osu!的大部分功能(包括背景视频和StoryBoard,先立个flag),同时学习Swift语言和游戏开发。经过查阅资料,决定使用SpriteKit框架。
由于个人精力有限,这个项目很可能会烂尾。不过我还是希望能偶尔抽空更新,并在将来的某一天实现所有预期的功能。
]]>根据安装文档,先配置msys2,使直接启动msys2后能成功编译程序,注意将环境变量设置写入/etc/profile中,不要修改~/.bashrc的内容。
直接替换powershell为bash是不可行的,因为msys2启动时会运行/etc/profile这个脚本用来设置环境变量等配置,直接替换就会跳过这个配置文件,造成环境不完整,编译报错。而指定配置文件为/etc/profile后启动时会运行/etc/post-install/05-home-dir.post这个脚本,造成启动后目录切换到/home/xxx,而我们需要的是启动后仍然保持当前目录,所以我们需要对/etc/profile进行修改。
首先复制/etc/profile到/etc/profile_vscode,然后在profile_vscode中找到以下代码:1
2
3for postinst in $(export LC_COLLATE=C; echo /etc/post-install/*.post); do
[ -e "${postinst}" ] && . "${postinst}"
done
将其替换为:1
2
3
4
5
6
7for postinst in $(export LC_COLLATE=C; echo /etc/post-install/*.post); do
if [ -n "echo $postinst|grep home" ]
then
continue
fi
[ -e "${postinst}" ] && . "${postinst}"
done
在配置文件中添加以下代码:1
2
3
4
5"terminal.integrated.shell.windows": "C:\\msys32\\usr\\bin\\bash.exe",
"terminal.integrated.shellArgs.windows": [
"--init-file",
"C:\\msys32\\etc\\profile_vscode"
],
注意根据具体msys2安装位置设置其中的参数。
接下来就是实际操作部分了。
安装虚拟机没啥好说的,不会的话网上教程一搜一大把。不管是windows、linux还是mac,只要能播放网课就没问题。
在这里需要强调的是:由于我们会使用检测CPU占用的方式
检测网课暂停,必须使用纯净的系统!而且请将虚拟CPU核心数设置为1!
何为纯净?网上乱飞的ghost版windows都内置了各种垃圾软件,它们都会对我们检测CPU占用进行干扰,甚至可能弹窗导致视频无故暂停。(当然如果你有兴趣一点一点把垃圾软件删干净也可以)
为何要单核?多核会削弱CPU总体占用率,而这不利于我们检测视频暂停。
我使用的是VMware Player 12+原版Windows xp sp3+IE8补丁。实测效果很不错,几乎没有误报漏报的情况。
一点优化:最好关闭屏幕保护、自动关闭显示器等一切节能功能,这些功能对虚拟机没有任何帮助。
终于到了重头戏。废话少说,上代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#参数设置
$cpucap=50
$lasttime=8
#脚本主体
$count=0
$wshell = New-Object -ComObject Wscript.Shell#和弹窗有关
while(1){
#vmware-vmx为vmware的虚拟机进程名,如果你使用的是其他虚拟机,请替换为相关进程名,不需要加上.exe。
$cpu=(powershell '((Get-Counter \"\Process(*)\% Processor Time\").CounterSamples|Where-Object {$_.InstanceName -eq \"vmware-vmx\"}).CookedValue')
echo $cpu
If ($cpu -lt $cpucap){$count++}
Else{$count=0}
echo $count
if ($count -eq $lasttime)
{
[System.Media.SystemSounds]::Hand.Play()#声音提示
$wshell.Popup("检测到视频暂停!",0,"通知",0x0)#弹窗提示
$count=0
}
}
将脚本保存在任何位置,扩展名为.ps1,右键点击脚本,选择“使用Power Shell运行”即可。
注意开头的两个参数:$cpucap和$lasttime。$cpucap指的是当指定进程(详见脚本注释)的CPU占用率达到多少以上判定为视频正在播放。$lasttime是指CPU占用率持续多少秒低于$cpucap的时候判定为视频已经暂停。这两个参数在每台电脑上肯定都不一样,你需要自己调整。最开始你可以将$cpucap设置为0,运行脚本,输出内容中的浮点数即为实时CPU占用,让视频处于播放和暂停状态,观察CPU占用率变化,选择最佳的$cpucap和$lasttime组合。如果你发现误报率或漏报率较高,可以随时调整参数,保存后记得重新运行脚本。
一个秘诀:将视频清晰度调为720P会显著提高播放与暂停状态下CPU占用率的差距。
接下来是通知效果:
如果你不喜欢这种弹窗的方式,可以采用系统通知的方式,相关代码如下:1
2
3
4
5
6
7
8[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$objNotifyIcon.Icon = "D:\icon.ico"#请修改为有效ico图标文件的绝对路径
$objNotifyIcon.BalloonTipIcon = "Info"
$objNotifyIcon.BalloonTipText = "检测到视频暂停!"
$objNotifyIcon.BalloonTipTitle = "通知"
$objNotifyIcon.Visible = $True
$objNotifyIcon.ShowBalloonTip(10000)
效果如下:
开始享受吧!你完全可以把虚拟机静音,放上喜欢的音乐,干自己想干的事,让脚本帮助你看网课就行了!
最后,也是最重要的是:Use at you own risk!