# kindle-plugin-touch **Repository Path**: qsbye/kindle-plugin-touch ## Basic Information - **Project Name**: kindle-plugin-touch - **Description**: 为越狱的kindle开发的插件「touch」,显示每日天气 - **Primary Language**: PHP - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-03-05 - **Last Updated**: 2023-09-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # kindle-plugin-touch ## 介绍 为越狱的kindle开发的插件「touch」,显示每日天气 ## 项目结构 - hello_weather_one 初代项目,主要是Shell - hello_weather_two Golang实现 ## git [git@gitee.com:qsbye/kindle-plugin-touch.git] ```SHELL git push -u gitee master ``` [git@github.com:QS2002/kindle-plugin-touch.git] ```SHELL git push -u github master ``` ## 流程 在服务器用php生成600*800的png图片
kindle端定时获取图片并显示到屏幕上 ## 安装说明 (子目录,e.g. kindle_weather_one) 1. php>7.4 2. kindle已经越狱 3. 复制文件夹到电脑挂载的kindle盘的/extensions目录,这个目录在kindle中实际上是/mnt/us/extensions ## 使用说明 1. 进入kual找到touch打开 2. kindle最好先连上wifi ## 工程日志 ### 第一天:2022年9月21日 主要内容:整理目录,区分一二代 ### 第二天:2023年3月20日 主要内容:恢复工程;信息流图片生成;升级插件;恢复文档; 备忘:[文档](https://www.cnblogs.com/qsbye/p/15390296.html); #### 安装go插件 ```SHELL export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890 go install -v github.com/ramya-rao-a/go-outline@v0.0.0-20210608161538-9736a4bde949 go install -v golang.org/x/tools/gopls@latest ``` #### 编译测试 [https://zhuanlan.zhihu.com/p/507786807] ```SHELL cd hello_weather_two go build test4.go # -x选项显示详细输出 GOOS=linux GOARCH=arm go build -x test4.go GOOS=darwin GOARCH=arm64 go build test4.go ./test4 ``` 结果: ```SHELL little-endian: [0 1 2 3] to 50462976 big-endian: [0 1 2 3] to 66051 ``` #### 查看文件信息 ```SHELL file test4 ``` 输出: ```text test4: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, Go BuildID=b_zNxvL8Mx9BbKsvTTre/W9WOSRtyZx8-Lcukc1-d/4thocgzUpZ4Dpt4c3_YE/nOD6UV8V0GX07dezmmtA, with debug_info, not stripped ``` ### 第三天:2023年3月21日 主要内容:信息流图片生成;go操作framebuffer写入png图片;触摸; #### 信息流来源 1. 一言 2. 心知天气 3. rss订阅 4. 天气新闻 5. bing每日一图 6. 高质量广告图片 #### RSS订阅 [https://zhuanlan.zhihu.com/p/178788237] [国家统计局](http://www.stats.gov.cn/wzgl/rss/202302/t20230217_1912859.html) [http://www.stats.gov.cn/sj/zxfb/rss.xml] [http://www.stats.gov.cn/sj/sjjd/rss.xml] [https://zhuanlan.zhihu.com/p/113487194] [知乎每日精选](https://www.zhihu.com/rss) [豆瓣书评](https://www.douban.com/feed/review/book) [豆瓣影评](https://link.zhihu.com/?target=https%3A//www.douban.com/feed/review/movie) [科学网](http://www.sciencenet.cn/xml/blog.aspx) [周末画报](http://www.modernweekly.com/rssxml/cat_3.xml) #### 解析RSS 2.0为HTML文件 [https://www.cnblogs.com/sinosaurus/p/12287388.html] 目前尝试了两种方式,一种是直接在 html 中使用,一种是在vue项目中使用。 这里直接阐述结果,细节不做过多介绍. html效果 vue 需要注意的问题 在html中使用npm包时,需要使用 Browserify 进行转化为 浏览器可以读取的代码 在vue中使用上述的包时,并不是每个都可以直接使用,需要测试一下 由于每个包的开发理念不同,有的从xml转移到json的结构哦,数据嵌套过深,因而还需要自己测试一下 获取 feed 的文件,其实跟平时的 ajax请求,没有什么特别之处,只是在于数据结构不同罢了 #### bing每日一图 [https://tool.liumingye.cn/bingimg/] 请求地址: http://tool.liumingye.cn/bingimg/img.php 请求地址(HTTPS): https://tool.liumingye.cn/bingimg/img.php 请求方法: GET ```HTML
``` #### 图片布局 目标尺寸:600x800 颜色:黑白 #### 图片生成 问题: 用php7;从```http://tool.liumingye.cn/bingimg/img.php```获取图片;图片处理为5px圆角;生成600x800的png图片,将从如上网址获取的图片等比例缩放到600x800的图片中;给出代码 用python3;从```http://tool.liumingye.cn/bingimg/img.php```获取图片;图片处理为5px圆角;生成600x800的png图片,将从如上网址获取的图片等比例缩放到600x800的图片中;给出代码 你可以使用Pillow库来实现这个需求,以下是Python 3的代码: ```PYTHON from io import BytesIO from PIL import Image, ImageDraw # 获取图片 response = requests.get("http://tool.liumingye.cn/bingimg/img.php") image = Image.open(BytesIO(response.content)) # 将图片处理为5px圆角 radius = 5 mask = Image.new("L", image.size, 0) draw = ImageDraw.Draw(mask) draw.rectangle((0, radius, radius, image.size[1] - radius), fill=255) draw.rectangle((radius, 0, image.size[0] - radius, image.size[1]), fill=255) draw.rectangle((image.size[0] - radius, radius, image.size[0], image.size[1] - radius), fill=255) mask = mask.filter(ImageFilter.GaussianBlur(radius=radius)) image.putalpha(mask) # 将图片等比例缩放到600x800的大小 width, height = image.size if width / height > 0.75: target_width = int(height * 0.75) target_height = height else: target_width = width target_height = int(width / 0.75) image = image.resize((target_width, target_height), resample=Image.LANCZOS) # 在600x800的背景图片中心插入处理后的图片 background = Image.new("RGB", (600, 800), "white") x = (600 - image.size[0]) // 2 y = (800 - image.size[1]) // 2 background.paste(image, (x, y), image) # 保存为png格式图片 background.save("output.png", "PNG") ``` 代码中,首先使用requests库获取图片,然后使用Pillow库处理图片。Pillow库提供了ImageDraw模块来进行图片绘制,可以使用rectangle()方法绘制矩形。将图片的四个角处理为圆角,可以先创建一个mask图层,然后在上面绘制圆角矩形,最后将mask图层模糊,得到圆角效果。使用putalpha()方法将处理后的mask图层作为图片的透明度层。然后根据图片的宽高比例,将其等比例缩放到600x800的大小,并在600x800的背景图片中心插入处理后的图片。最后将结果保存为png格式的图片。 #### 图片显示 问题: 使用go语言;每5分钟自动从```http://tool.liumingye.cn/bingimg/img.php```获取图片`并裁剪为600x800的图片并保存到```/mnt/us/kindle.png```;将```/mnt/us/kindle.png```图片写入/dev/fb0的framebuffer中;给出代码 以下是使用Go语言实现的代码,需要使用image和image/draw包来处理图片: ```GO package main import ( "image" "image/draw" "image/png" "io/ioutil" "log" "net/http" "os" "time" ) func main() { // 定义图片URL和文件路径 imgUrl := "http://tool.liumingye.cn/bingimg/img.php" filePath := "/mnt/us/kindle.png" // 设置定时器,每5分钟执行一次 timer := time.NewTicker(5 * time.Minute) for range timer.C { // 获取图片 response, err := http.Get(imgUrl) if err != nil { log.Fatal(err) } defer response.Body.Close() // 解码图片 img, err := png.Decode(response.Body) if err != nil { log.Fatal(err) } // 裁剪图片为600x800大小 width := img.Bounds().Max.X height := img.Bounds().Max.Y aspectRatio := float64(width) / float64(height) if aspectRatio > 0.75 { targetWidth := int(float64(height) * 0.75) img = cropImage(img, (width-targetWidth)/2, 0, targetWidth, height) } else { targetHeight := int(float64(width) / 0.75) img = cropImage(img, 0, (height-targetHeight)/2, width, targetHeight) } img = resizeImage(img, 600, 800) // 保存图片到文件 file, err := os.Create(filePath) if err != nil { log.Fatal(err) } defer file.Close() err = png.Encode(file, img) if err != nil { log.Fatal(err) } // 将图片写入framebuffer fb, err := os.OpenFile("/dev/fb0", os.O_WRONLY, 0666) if err != nil { log.Fatal(err) } defer fb.Close() err = writeImageToFramebuffer(fb, img) if err != nil { log.Fatal(err) } } } // 裁剪图片 func cropImage(img image.Image, x, y, width, height int) image.Image { rect := image.Rect(x, y, x+width, y+height) cropped := image.NewRGBA(rect) draw.Draw(cropped, cropped.Bounds(), img, rect.Min, draw.Src) return cropped } // 缩放图片 func resizeImage(img image.Image, width, height int) image.Image { resized := image.NewRGBA(image.Rect(0, 0, width, height)) draw.NearestNeighbor.Scale(resized, resized.Bounds(), img, img.Bounds(), draw.Src) return resized } // 将图片写入framebuffer func writeImageToFramebuffer(fb *os.File, img image.Image) error { bounds := img.Bounds() for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { pixel := img.At(x, y) r, g, b, a := pixel.RGBA() r >>= 8 g >>= 8 b >>= 8 a >>= 8 color := (r << 16) | (g << 8) | b err := writePixel(fb, x, y, color) if err != nil { return err } } } return nil } // 写入单个像素到framebuffer func writePixel(fb *os.File, x, y, color int) error { var data [4]byte data[0] = byte((color >> 16) & 0xff) data[1] = byte((color >> 8) & 0xff) data[2] = byte(color & 0xff) data[3] = 0x00 offset := (y*600 + x) * 4 _, err := fb.WriteAt(data[:], int64(offset)) return err } ``` 代码中使用定时器每隔5分钟执行一次,从指定的URL获取图片,并将其裁剪为600x800大小,保存到文件中,然后将图片写入framebuffer。图片裁剪和缩放的过程使用了image包中的方法,写入framebuffer的过程则是将每个像素点的RGB值转换为一个32位整数,然后写入framebuffer中对应的位置。 报错: ```SHELL # command-line-arguments ./show_img1.go:82:7: undefined: draw.NearestNeighbor NearestNeighbor not declared by package ./show_img1.go:98:32: cannot use color (variable of type uint32) as type int in argument to writePixel ``` ```text x/image 包中有四种缩放算法: NearestNeighbor ApproxBiLinear BiLinear CatmullRom ``` [https://jaycechant.info/2019/golang-1-13-from-dep-to-mod/] [golang.google.cn] ```SHELL go install golang.org/x/image@latest go install golang.org/x@latest go install golang.org/x/image/draw@latest go list image/draw touch go.mod go mode tidy go mod edit -module=golang.org/x/image go proxy export GOPROXY=http://127.0.0.1:7890 ``` 因为国情特殊,一定要设置 GOPROXY。 golang.org/x/image: module golang.org/x: reading http://127.0.0.1:7890/golang.org/x/@v/list: 400 Bad Request 问题: go: golang.org/x/image@latest: module golang.org/x/image@latest found (v0.6.0), but does not contain package golang.org/x/image 问题: cannot use color (variable of type uint32) as int value in argument to writePixel ### 第四天:2023年3月22日 主要内容:SSH; #### kindle的MAC地址 00:BB:3A:9E:69:7D #### OpenSSH安装 [https://blog.csdn.net/cuemes08808/article/details/100392125] [http://violet.ibs.com.au/openssh/] [https://www.openssh.com] [https://github.com/openssh/openssh-portable] #### DropBearSSH安装(嵌入式Linux) [https://www.cnblogs.com/Jimmy1988/p/9060826.html] 1. 交叉编译zlib 2. 交叉编译dropbear ##### 交叉编译工具链 目标架构:Linux,armv7 ```text /Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin /Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc ``` ##### cmake设置教程编译 [https://zhuanlan.zhihu.com/p/467812302] set(CMAKE_SYSTEM_NAME Linux) CMAKE_SYSTEM_NAME 交叉编译的必设参数,只有当CMAKE_SYSTEM_NAME这个变量被设置了,CMake才认为此时正在交叉编译,它会额外设置一个变量CMAKE_CROSSCOMPILING为true. set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_SYSTEM_PROCESSOR armv7) ##### zlib编译 ```SHELL cd ./files/zlib-1.2.13 mkdir build cd build #cmake -A armv7 .. ../configure --prefix=$PWD ``` 修改Makefile: ```SHELL CC=/home/BvSDK/toolchain/bin/arm-openwrt-linux-gcc //你交叉编译工具的绝对路径 AR=/home/BvSDK/toolchain/bin/arm-openwrt-linux-gcc-ar RANLIB=/home/BvSDK/toolchain/bin/arm-openwrt-linux-gcc-ranlib LDSHARED=/home/BvSDK/toolchain/bin/arm-openwrt-linux-gcc -shared -Wl,-soname,libz.so.1,--version-script,zlib.map //(我只是将原来的gcc改成了我自己的编译工具,后面的参数没动过) ``` 修改: ```SHELL CC=/Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc AR=/Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-ar RANLIB=//Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc-ranlib LDSHARED=/Users/workspace/Desktop/projects/blue_bridge_embeded/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc -shared -Wl,-soname,libz.so.1,--version-script,zlib.map ``` 然后: ```SHELL make make install ``` ##### dropbear编译 ```SHELL cd ./files/dropbear-2016.74 mkdir build cd build ../configure --prefix=$PWD --with-zlib=/Users/workspace/Desktop/projects/kindle-plugin-touch/files/zlib-1.2.13/build CC=gcc --host=arm make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" install ``` #### 局域网设备扫描 ```SHELL brew install nmap sudo nmap -sP -PI -PT 192.168.0.1/24 ``` #### 图片布局 ```CSS #wrapper{ width:600px; height:800px; margin: 0 auto; border:1px solid; } * { box-sizing: border-box; } body { font-family: Arial; padding: 10px; background: #f1f1f1; } /* 头部标题 */ .header { padding: 30px; text-align: center; background: white; } .header h1 { font-size: 50px; } /* 导航条 */ .topnav { overflow: hidden; background-color: #333; } /* 导航条链接 */ .topnav a { float: left; display: block; color: #f2f2f2; text-align: center; padding: 14px 16px; text-decoration: none; } /* 链接颜色修改 */ .topnav a:hover { background-color: #ddd; color: black; } /* 创建两列 */ /* Left column */ .leftcolumn { float: left; width: 75%; } /* 右侧栏 */ .rightcolumn { float: left; width: 25%; background-color: #f1f1f1; padding-left: 20px; } /* 图像部分 */ .fakeimg { background-color: #aaa; width: 100%; padding: 20px; } /* 文章卡片效果 */ .card { background-color: white; padding: 20px; margin-top: 20px; } /* 列后面清除浮动 */ .row:after { content: ""; display: table; clear: both; } /* 底部 */ .footer { padding: 20px; text-align: center; background: #ddd; margin-top: 20px; } /* 响应式布局 - 屏幕尺寸小于 800px 时,两列布局改为上下布局 */ @media screen and (max-width: 800px) { .leftcolumn, .rightcolumn { width: 100%; padding: 0; } } ``` 根据以上CSS布局,给出生成600x800px的包含如下内容的图片的python代码: 1. 来自```http://tool.liumingye.cn/bingimg/img.php```的图片, 2. ```text 西窗烛 宋代:谭宣子 春江骤涨,晓陌微干,断云如梦相逐。料应怪我频来去,似千里迢遥,伤心极目。为楚腰、惯舞东风,芳草萋萋衬绿。 燕飞独。知是谁家箫声多事,吹咽寻常怨曲。仅教衿袖香泥涴,君不见、扬州三生杜牧。待泪华、暗落铜盘,甚夜西窗剪烛。 ``` 结果: ```PYTHON from PIL import Image, ImageDraw, ImageFont import requests # 获取图片 img_url = 'http://tool.liumingye.cn/bingimg/img.php' response = requests.get(img_url) # 创建画布 canvas = Image.new('RGB', (600, 800), 'white') # 在画布上添加图片 img = Image.open(BytesIO(response.content)) img = img.resize((600, 400), Image.ANTIALIAS) canvas.paste(img, (0, 0)) # 在画布上添加文字 draw = ImageDraw.Draw(canvas) font = ImageFont.truetype('SmileySans-Oblique.ttf', 18) text = '''西窗烛 宋代:谭宣子 春江骤涨,晓陌微干,断云如梦相逐。料应怪我频来去,似千里迢遥,伤心极目。为楚腰、惯舞东风,芳草萋萋衬绿。 燕飞独。知是谁家箫声多事,吹咽寻常怨曲。仅教衿袖香泥涴,君不见、扬州三生杜牧。待泪华、暗落铜盘,甚夜西窗剪烛。''' draw.text((10, 420), text, font=font, fill='black') # 保存图片 canvas.save('result.png') ``` ### 圆角图片 根据以下规则,给出生成600x800px大小的png图片的python代码: 内容: ```HTML

西窗烛 宋代:谭宣子 春江骤涨,晓陌微干,断云如梦相逐。料应怪我频来去,似千里迢遥,伤心极目。为楚腰、惯舞东风,芳草萋萋衬绿。 燕飞独。知是谁家箫声多事,吹咽寻常怨曲。仅教衿袖香泥涴,君不见、扬州三生杜牧。待泪华、暗落铜盘,甚夜西窗剪烛。

``` 布局样式: ```CSS /* styles.css */ p{ font-family: Arial, Helvetica, sans-serif; } body{ background-color: skyblue; background-size: cover; background-attachment:fixed; width: 600px; height: 800px; -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); } /*毛玻璃效果*/ .glass{ -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); } /*卡片效果*/ .card { width: 400px; height: auto; padding: 2rem; border-radius: 1rem; background: rgba(255, 255, 255, .7); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); margin: 5rem; } .card-title { margin-top: 0; margin-bottom: .5rem; font-size: 1.2rem; } p, a { font-size: 1rem; } a { color: #4d4ae8; text-decoration: none; } .glass h1{ color:aliceblue; font-family: Arial, Helvetica, sans-serif; } /* Add styles here to customize the appearance of your app */ ``` 结果: ```PYTHON from PIL import Image, ImageDraw, ImageFont import requests # 获取图片 img_url = 'http://tool.liumingye.cn/bingimg/img.php' response = requests.get(img_url) # 创建画布 canvas = Image.new('RGB', (600, 800), 'white') # 在画布上添加图片 img = Image.open(BytesIO(response.content)) img = img.resize((200, 100), Image.ANTIALIAS) canvas.paste(img, (200, 50)) # 在画布上添加文字 draw = ImageDraw.Draw(canvas) font = ImageFont.truetype('arial.ttf', 18) text = '''西窗烛 宋代:谭宣子 春江骤涨,晓陌微干,断云如梦相逐。料应怪我频来去,似千里迢遥,伤心极目。为楚腰、惯舞东风,芳草萋萋衬绿。 燕飞独。知是谁家箫声多事,吹咽寻常怨曲。仅教衿袖香泥涴,君不见、扬州三生杜牧。待泪华、暗落铜盘,甚夜西窗剪烛。''' draw.text((50, 200), text, font=font, fill='black') # 添加卡片效果 draw.rectangle([100, 150, 500, 600], fill=(255, 255, 255, 100), outline=None) draw.rectangle([120, 170, 480, 580], fill=(255, 255, 255, 150), outline=None) draw.rectangle([140, 190, 460, 560], fill=(255, 255, 255, 200), outline=None) # 添加毛玻璃效果 canvas = canvas.filter(ImageFilter.GaussianBlur(radius=10)) # 保存图片 canvas.save('result.png') ``` #### 梯子客户端 [https://github.com/2dust/clashN] ClashN ### 第五天:2023年3月23日 主要内容:图片接收2; #### 图片接收并显示 问题: 使用go语言;每5分钟自动从```http://tool.liumingye.cn/bingimg/img.php```获取图片并保存到```/mnt/us/kindle.png```;将```/mnt/us/kindle.png```图片写入/dev/fb0的framebuffer中;给出代码, 以下是使用Go语言每5分钟从指定URL获取图片并将其写入到Kindle设备的framebuffer中的代码: ```PYTHON package main import ( "image" "image/png" "log" "os" "time" "github.com/kettek/goroimagehash" "github.com/sajari/regression" "golang.org/x/exp/io/i2c" ) const ( frameBufferPath = "/dev/fb0" imagePath = "/mnt/us/kindle.png" imageURL = "http://tool.liumingye.cn/bingimg/img.php" ) func main() { for { // 获取图片 resp, err := http.Get(imageURL) if err != nil { log.Printf("Failed to get image: %v", err) continue } defer resp.Body.Close() img, err := png.Decode(resp.Body) if err != nil { log.Printf("Failed to decode image: %v", err) continue } // 保存图片到本地 f, err := os.Create(imagePath) if err != nil { log.Printf("Failed to create file: %v", err) continue } defer f.Close() if err := png.Encode(f, img); err != nil { log.Printf("Failed to encode image: %v", err) continue } // 将图片写入framebuffer中 fb, err := os.OpenFile(frameBufferPath, os.O_WRONLY, 0644) if err != nil { log.Printf("Failed to open framebuffer: %v", err) continue } defer fb.Close() if err := writeImageToFramebuffer(img, fb); err != nil { log.Printf("Failed to write image to framebuffer: %v", err) continue } // 等待5分钟 time.Sleep(5 * time.Minute) } } func writeImageToFramebuffer(img image.Image, fb *os.File) error { // 获取图片的尺寸和颜色模式 bounds := img.Bounds() width, height := bounds.Dx(), bounds.Dy() format := img.ColorModel() // 创建framebuffer fb, err := i2c.NewFile(0x3c, frameBufferPath) if err != nil { return err } // 设置framebuffer的像素格式 if _, err := fb.Write([]byte{0, 0x80, 0x08, 0x01}); err != nil { return err } // 将图片写入framebuffer for y := 0; y < height; y++ { for x := 0; x < width; x++ { color := format.Convert(img.At(x, y)) r, g, b, _ := color.RGBA() pixel := uint16((r>>8)<<11 | (g>>8)<<5 | (b >> 8)) if _, err := fb.Write([]byte{byte(pixel >> 8), byte(pixel)}); err != nil { return err } } } return nil } ``` 需要注意的是,以上代码使用了第三方库goroimagehash和sajari/regression,分别用于计算图像的哈希值和做线性回归分析。如果没有安装这两个库,请在终端中执行以下命令进行安装: ```PYTHON go get github.com/kettek/goroimagehash go get kgithub.com/kettek/goroimagehash go get github.com/sajari/regression go get kgithub.com/sajari/regression go mod download golang.org/x/exp go get golang.org/x/exp ``` #### 显示当前时间 问题: kindle 7;使用go语言;屏幕分辨率600x800;仿照“新机表APP"给出一个写入/dev/fb0的framebuffer中显示当前时间的程序 以下是使用Go语言在Kindle 7设备上以600x800分辨率,仿照“新机表APP”在framebuffer中显示当前时间的程序: ```GO package main import ( "fmt" "image" "image/color" "image/draw" "log" "os" "time" "golang.org/x/exp/io/i2c" ) const ( frameBufferPath = "/dev/fb0" screenWidth = 600 screenHeight = 800 ) func main() { for { // 获取当前时间 now := time.Now() hour, min, sec := now.Hour(), now.Minute(), now.Second() // 创建画布 canvas := image.NewRGBA(image.Rect(0, 0, screenWidth, screenHeight)) // 添加背景色 draw.Draw(canvas, canvas.Bounds(), &image.Uniform{color.White}, image.ZP, draw.Src) // 添加时针 hourAngle := float64(hour%12)/12*360 + float64(min)/60*30 hourX, hourY := getHandPosition(hourAngle, 100, 20, screenWidth/2, screenHeight/2) drawLine(canvas, screenWidth/2, screenHeight/2, hourX, hourY, color.Black) // 添加分针 minAngle := float64(min)/60*360 + float64(sec)/60*6 minX, minY := getHandPosition(minAngle, 150, 10, screenWidth/2, screenHeight/2) drawLine(canvas, screenWidth/2, screenHeight/2, minX, minY, color.Black) // 添加秒针 secAngle := float64(sec) / 60 * 360 secX, secY := getHandPosition(secAngle, 200, 5, screenWidth/2, screenHeight/2) drawLine(canvas, screenWidth/2, screenHeight/2, secX, secY, color.Red) // 将画布写入framebuffer中 fb, err := os.OpenFile(frameBufferPath, os.O_WRONLY, 0644) if err != nil { log.Printf("Failed to open framebuffer: %v", err) continue } defer fb.Close() if err := writeImageToFramebuffer(canvas, fb); err != nil { log.Printf("Failed to write image to framebuffer: %v", err) continue } // 等待1秒 time.Sleep(time.Second) } } func getHandPosition(angle float64, length, width int, cx, cy int) (int, int) { rad := angle * math.Pi / 180 x := cx + int(float64(length)*math.Sin(rad)) y := cy - int(float64(length)*math.Cos(rad)) return x, y } func drawLine(img *image.RGBA, x0, y0, x1, y1 int, c color.Color) { dx := math.Abs(float64(x1 - x0)) dy := math.Abs(float64(y1 - y0)) sx, sy := 0, 0 if x0 < x1 { sx = 1 } else { sx = -1 } if y0 < y1 { sy = 1 } else { sy = -1 } err := dx - dy for { img.Set(x0, y0, c) if x0 == x1 && y0 == y1 { break } e2 := 2 * err if e2 > -dy { err -= dy x0 += sx } if e2 < dx { err += dx y0 += sy } } } func writeImageToFramebuffer(img image.Image, fb *os.File) error { // 获取图片的尺寸和颜色模式 bounds := img.Bounds() width, height := bounds.Dx(), bounds.Dy() format := img.ColorModel() // 创建framebuffer fb, err := i2c.NewFile(0x3c, frameBufferPath) if err != nil { return err } // 设置framebuffer的像素格式 if _, err := fb.Write([]byte{0, 0x80, 0x08, 0x01}); err != nil { return err } // 将图片写入framebuffer for y := 0; y < height; y++ { for x := 0; x < width; x++ { color := format.Convert(img.At(x, y)) r, g, b, _ := color.RGBA() pixel := uint16((r>>8)<<11 | (g>>8)<<5 | (b >> 8)) if _, err := fb.Write([]byte{byte(pixel >> 8), byte(pixel)}); err != nil { return err } } } return nil } ``` 以上代码使用了image和image/draw包分别用于创建画布和绘制时分秒针,使用了math包计算时分秒针的位置。需要注意的是,以上代码使用了i2c包的NewFile函数来创建framebuffer,如果你的Kindle设备上的framebuffer不是I2C设备,则需要使用其他方式来创建framebuffer。 ### 第六天:2023年5月21日 主要内容:rust运行测试; #### 查看CPU架构 ```SHELL cat /proc/cpuinfo ``` 结果: ```SHELL Processor:ARM v7 rev 10(v71) MIPS:1963.43 Features:swp half thumb fastmult vfp edsp neon vfpv3 Hardware:Freescale i.MX 6soloLite based Wario Board ``` #### 查看系统信息 ```SHELL unname -r ``` 结果: ```SHELL 3.0.35-lab126 ``` #### macOS平台交叉编译 现在已经有开发者把交叉编译工具做个 brew 安装脚本,安装 ```SHELL brew install FiloSottile/musl-cross/musl-cross --without-x86_64 --with-arm-hf #brew install FiloSottile/musl-cross/musl-cross ``` 这个交叉编译工具使用的是 musl c 库,跨平台,性能比 glibc 要差一点。 然后通过 rustup 安装不同的目标支持 ```SHELL rustup target add arm-unknown-linux-musleabihf # armv6 rustup target add armv7-unknown-linux-musleabihf # armv7 ``` 编译设置 在项目下创建一个 .cargo/config 文件,内容为 ```Rust [target.arm-unknown-linux-musleabihf] linker = "arm-linux-musleabihf-ld" [target.armv7-unknown-linux-musleabihf] linker = "arm-linux-musleabihf-ld" [target.arm-unknown-linux-gnueabihf] linker = "arm-linux-gnueabihf-gcc" [target.armv7-unknown-linux-gnueabihf] linker = "arm-linux-gnueabihf-gcc" ``` 然后用按照不同的开发机、目标机类型,使用 cargo 进行编译 添加环境变量: ```SHELL #/opt/homebrew/Homebrew/Cellar #/opt/homebrew/Homebrew/Cellar/musl-cross ``` ```SHELL cargo build --target arm-unknown-linux-gnueabihf --release # armv6, linux cargo build --target armv7-unknown-linux-gnueabihf --release # armv7, linux cargo build --target arm-unknown-linux-musleabihf --release # armv6, mac cargo build --target armv7-unknown-linux-musleabihf --release # armv7, mac ``` 编译完后,在 target/<目标类型>/release 中可找到对应的可执行文件,copy 到目标上就可以运行。 这个文件也可使用 strip 工具来删掉一些符号,从而减少整个可执行文件的体积。 #### rust运行测试 [rust 交叉编译树莓派程序](https://www.cnblogs.com/fengyc/p/12198178.html) ```SHELL cargo init rustup target add arm-unknown-linux-gnueabihf rustup target add armv7-unknown-linux-gnueabihf cargo build --target=armv7-unknown-linux-gnueabihf ``` ```SHELL GOOS=linux GOARCH=arm go build -x test4.go ``` 将如上Go的编译命令翻译为Rust语言的编译命令; 在Rust中,要将代码编译成ARM架构的Linux二进制文件,可以使用以下命令: ```SHELL cargo build --target=arm-unknown-linux-gnueabihf ``` 其中,`cargo build`是Rust的构建命令,`--target`选项指定了编译目标为ARM架构的Linux,`arm-unknown-linux-gnueabihf`是Linux下ARMv7架构的三元组(Triple),其中`unknown`表示厂商未知,`gnueabihf`表示使用了GNU工具链和硬浮点支持。 报错: ```SHELL = note: clang: warning: argument unused during compilation: '-pie' [-Wunused-command-line-argument] ld: unknown option: --as-needed clang: error: linker command failed with exit code 1 (use -v to see invocation) ``` 这个错误提示表明你使用了不支持`--as-needed`选项的链接器。这通常是因为你的编译环境中的链接器版本过低导致的。 为了解决这个问题,你可以尝试以下几个步骤: 1. 更新链接器版本:尝试更新你的编译环境中的链接器版本。具体方法取决于你所使用的操作系统和包管理器。例如,如果你使用的是Ubuntu系统,可以尝试运行以下命令来更新链接器版本: ```SHELL sudo apt-get update sudo apt-get install --only-upgrade binutils ``` 2. 移除`--as-needed`选项:如果更新链接器版本不可行,你可以尝试在编译命令中移除`--as-needed`选项。在某些情况下,这可能会导致编译出的二进制文件大小增加,但可以解决链接器错误问题。例如: ```SHELL GOOS=linux GOARCH=arm CGO_ENABLED=0 go build -ldflags="-s -w" ``` 在这个命令中,我们移除了`-pie`和`--as-needed`选项,并添加了`-s`和`-w`选项来减小生成二进制文件的大小。 希望这些方法可以帮助你解决问题。 如果你在执行`cargo build --target=arm-unknown-linux-gnueabihf`命令时遇到了错误,可以根据错误提示来修改命令。 例如,如果你遇到了`error: linker `arm-linux-gnueabihf-gcc` not found`错误,这意味着你的系统中没有安装ARM架构的GCC编译器。你可以通过以下命令来安装: ```SHELL sudo apt-get install gcc-arm-linux-gnueabihf ``` 如果你遇到了其他错误,可以根据错误提示来修改命令。一般来说,你需要确保你的编译环境中已经安装了适当的交叉编译工具链,并且在构建命令中正确地设置了目标架构。例如,如果你想编译为ARMv7架构,则应该使用`armv7-unknown-linux-gnueabihf`作为目标架构。 ```SHELL cargo build --target=armv7-unknown-linux-gnueabihf ``` 希望这些信息可以帮助你解决问题。 问题: ```Rust fn main() { println!("Hello, world!"); } ``` 配置: ```Rust [package] name = "hello_weather_three" version = "0.1.0" edition = "2021" [dependencies] [target.armv7-unknown-linux-gnueabihf] rustflags = ["-Clink-arg=-Wl,-z,relro,-z,now"] ``` 编译: ```SHELL cargo build --target=armv7-unknown-linux-gnueabihf ``` 报错: ```SHELL = note: clang: warning: argument unused during compilation: '-pie' [-Wunused-command-line-argument] ld: unknown option: --as-needed clang: error: linker command failed with exit code 1 (use -v to see invocation) ``` 这个错误可能是因为您的链接器不支持 `--as-needed` 标志,这个标志在某些链接器上是默认启用的。您可以尝试在 `Cargo.toml` 文件中添加以下代码: ``` [target.armv7-unknown-linux-gnueabihf] rustflags = ["-Clink-arg=-Wl,-z,relro,-z,now"] ``` 这将替换默认链接器标志,以便在链接时使用 `-Wl,-z,relro,-z,now` 标志,而不是默认的 `--as-needed` 标志。 这个错误可能是由于在使用 `--target=armv7-unknown-linux-gnueabihf` 时,链接器不支持 `--as-needed` 标志引起的。您可以尝试将 `rustflags` 更改为以下内容,以避免使用 `--as-needed` 标志: ```Rust [target.armv7-unknown-linux-gnueabihf] rustflags = ["-Clink-arg=-Wl,-z,relro,-z,now,-z,noexecstack"] ``` 这将使用 `-z,noexecstack` 标志替换 `--as-needed` 标志。 运行: ```SHELL /mnt/us/hello_weather_three ``` 结果: ```SHELL Hello,World! ``` #### kindle文件路径 ```SHELL ls /mnt/us ``` #### 获取屏幕信息 ```SHELL eips -i ``` 结果: ```SHELL Fixed framebuffer info id: mxc_epdc_fb smem_start: 0x80C00000 smem_len: 3268608 type: PACKED_PIXELS type_aux: 0 visual: STATIC_PSEUDOCOLOR xpanstep: 1 ypanstep: 1 ywrapstep: 0 line_length: 608 mmio_start: 0x00000000 mmio_len: 0 accel: 0 Variable framebuffer info xres: 600 yres: 800 xres_virtual: 608 yres_virtual: 5376 xoffset: 0 yoffset: 0 bits_per_pixel: 8 grayscale: 1 red.offset: 0 green.offset: 0 red.length: 8 green.length: 8 red.msb_right: 0 green.msb_right: 0 blue.offset: 0 transp.offset: 0 blue.length: 8 transp.length: 0 blue.msb_right: 0 transp.msb_right: 0 nonstd: 0 activate: 128 width: 91 height: 122 accel_flags: 0 pixclock: 27027027 left_margin: 8 right_margin: 100 upper_margin: 4 lower_margin: 8 hsync_len: 4 vsync_len: 1 sync: 0 vmode: 0 rotate: 3 DPI: 167.472527 166.557377 167 waveform type = 2 material type = 0 temperature = 28 (C) ``` #### 清空framebuffer 使用RUST;使用Linux;给出清空framebuffer的代码,使得屏幕短暂空白; 以下是使用Rust在Linux上清空framebuffer的代码: ```rust use std::fs::OpenOptions; use std::io::{Seek, Write}; use std::mem::size_of; fn main() { // 打开framebuffer设备 let mut fb_file = OpenOptions::new() .write(true) .open("/dev/fb0") .expect("failed to open framebuffer device"); // 获取framebuffer的大小 let mut fb_var_screeninfo = mem::MaybeUninit::::uninit(); unsafe { libc::ioctl(fb_file.as_raw_fd(), libc::FBIOGET_VSCREENINFO, fb_var_screeninfo.as_mut_ptr()) .expect("failed to get variable screen info"); } let fb_var_screeninfo = unsafe { fb_var_screeninfo.assume_init() }; let fb_size = fb_var_screeninfo.xres * fb_var_screeninfo.yres * fb_var_screeninfo.bits_per_pixel / 8; // 清空framebuffer let zero_buffer = vec![0u8; fb_size as usize]; fb_file.seek(SeekFrom::Start(0)).expect("failed to seek framebuffer device"); fb_file.write_all(&zero_buffer).expect("failed to write to framebuffer device"); } ``` 这段代码使用了`libc`库中的`ioctl`函数来获取framebuffer的大小,并使用标准库中的`fs`和`io`模块来操作framebuffer设备文件。在清空framebuffer时,我们创建了一个与framebuffer大小相同的零向量,然后将其写入framebuffer设备文件中。这将导致屏幕短暂地变成空白。 使用RUST;使用Linux;使用musl c库;给出清空framebuffer的代码,使得屏幕短暂空白; 以下是使用Rust编写的清空Linux Framebuffer的代码,使用musl c库: ```rust use std::fs::OpenOptions; use std::io::{self, Write}; use std::mem::size_of; const FB_DEVICE: &str = "/dev/fb0"; #[repr(C)] struct FbVarScreenInfo { xres: u32, yres: u32, xres_virtual: u32, yres_virtual: u32, xoffset: u32, yoffset: u32, bits_per_pixel: u32, grayscale: u32, red: FbBitfield, green: FbBitfield, blue: FbBitfield, transp: FbBitfield, nonstd: u32, activate: u32, height: u32, width: u32, accel_flags: u32, pixclock: u32, left_margin: u32, right_margin: u32, upper_margin: u32, lower_margin: u32, hsync_len: u32, vsync_len: u32, sync: u32, vmode: u32, rotate: u32, colorspace: u32, reserved: [u32; 4], } #[repr(C)] struct FbBitfield { offset: u32, length: u32, msb_right: u32, } fn main() -> io::Result<()> { let mut fb = OpenOptions::new().read(true).write(true).open(FB_DEVICE)?; let mut vinfo = FbVarScreenInfo::default(); unsafe { let ptr = &mut vinfo as *mut FbVarScreenInfo as *mut u8; let len = size_of::(); libc::ioctl(fb.as_raw_fd(), 0x4600, ptr); } let mut buffer = vec![0; (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8) as usize]; // Clear the buffer to black buffer.iter_mut().for_each(|x| *x = 0); // Write the buffer to the framebuffer fb.write_all(&buffer)?; Ok(()) } ``` 这段代码通过使用`ioctl`函数和`/dev/fb0`设备文件来获取屏幕信息并清空屏幕,使其短暂地变成黑色。请注意,这段代码假定您的系统支持Framebuffer,并且您已经以root身份运行了该程序。 报错: ```SHELL 9 | struct FbVarScreenInfo { | ---------------------- function or associated item `default` not found for this struct ... 51 | let mut vinfo = FbVarScreenInfo::default(); | ^^^^^^^ function or associated item not found in `FbVarScreenInfo` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `default`, perhaps you need to implement it: candidate #1: `Default` ``` 非常抱歉,我在之前提供的代码中犯了错误。由于`FbVarScreenInfo`是一个自定义结构体,它没有默认值。您需要手动实现`Default` trait,以便在声明`vinfo`时可以使用`FbVarScreenInfo::default()`。 以下是更新后的代码: ```rust use std::fs::OpenOptions; use std::io::{self, Write}; use std::mem::size_of; const FB_DEVICE: &str = "/dev/fb0"; #[repr(C)] struct FbVarScreenInfo { xres: u32, yres: u32, xres_virtual: u32, yres_virtual: u32, xoffset: u32, yoffset: u32, bits_per_pixel: u32, grayscale: u32, red: FbBitfield, green: FbBitfield, blue: FbBitfield, transp: FbBitfield, nonstd: u32, activate: u32, height: u32, width: u32, accel_flags: u32, pixclock: u32, left_margin: u32, right_margin: u32, upper_margin: u32, lower_margin: u32, hsync_len: u32, vsync_len: u32, sync: u32, vmode: u32, rotate: u32, colorspace: u32, reserved: [u32; 4], } impl Default for FbVarScreenInfo { fn default() -> Self { FbVarScreenInfo { xres: 0, yres: 0, xres_virtual: 0, yres_virtual: 0, xoffset: 0, yoffset: 0, bits_per_pixel: 0, grayscale: 0, red: FbBitfield::default(), green: FbBitfield::default(), blue: FbBitfield::default(), transp: FbBitfield::default(), nonstd: 0, activate: 0, height: 0, width: 0, accel_flags: 0, pixclock: 0, left_margin: 0, right_margin: 0, upper_margin: 0, lower_margin: 0, hsync_len: 0, vsync_len: 0, sync: 0, vmode: 0, rotate: 0, colorspace: 0, reserved: [0; 4], } } } #[repr(C)] struct FbBitfield { offset: u32, length: u32, msb_right: u32, } impl Default for FbBitfield { fn default() -> Self { FbBitfield { offset: 0, length: 0, msb_right: 0, } } } fn main() -> io::Result<()> { let mut fb = OpenOptions::new().read(true).write(true).open(FB_DEVICE)?; let mut vinfo = FbVarScreenInfo::default(); unsafe { let ptr = &mut vinfo as *mut FbVarScreenInfo as *mut u8; let len = size_of::(); libc::ioctl(fb.as_raw_fd(), 0x4600, ptr); } let mut buffer = vec![0; (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8) as usize]; // Clear the buffer to black buffer.iter_mut().for_each(|x| *x = 0); // Write the buffer to the framebuffer fb.write_all(&buffer)?; Ok(()) } ``` 这样,您应该能够成功编译并运行这段代码了。 ```SHELL cargo add libc # from macOS to arm-v7 cargo build --target armv7-unknown-linux-musleabihf --release ``` #### musl c库 [几种C库比较(musl uClibc dietlibc glibc)](https://www.cnblogs.com/merlindu/p/6370825.html) [https://www.bytenote.net/article/163117942798548993] musl libc和glibc是两个不同的C标准库,用于提供C语言程序运行所需的基础功能。它们在实现上存在一些重要的差异: 实现方式:musl libc是一个简单,轻量级的C标准库,它的设计目标是实现纯粹的C标准,没有任何额外的功能。相反,glibc是一个功能非常强大的标准库,提供了大量的扩展功能,以及与操作系统内核进行交互的特殊接口。 兼容性:由于glibc提供了大量的额外功能,因此它与各种操作系统,硬件平台以及第三方库之间的兼容性更好。然而,这也使得glibc的代码实现更加复杂,可维护性更加困难。 性能:由于musl libc的设计目标是简单和轻量级,因此它的运行速度通常比glibc更快。同时,由于musl libc代码实现更简单,它的内存使用量也通常更少。 #### 根据获取的eips信息修正代码 ```SHELL Fixed framebuffer info id: mxc_epdc_fb smem_start: 0x80C00000 smem_len: 3268608 type: PACKED_PIXELS type_aux: 0 visual: STATIC_PSEUDOCOLOR xpanstep: 1 ypanstep: 1 ywrapstep: 0 line_length: 608 mmio_start: 0x00000000 mmio_len: 0 accel: 0 Variable framebuffer info xres: 600 yres: 800 xres_virtual: 608 yres_virtual: 5376 xoffset: 0 yoffset: 0 bits_per_pixel: 8 grayscale: 1 red.offset: 0 green.offset: 0 red.length: 8 green.length: 8 red.msb_right: 0 green.msb_right: 0 blue.offset: 0 transp.offset: 0 blue.length: 8 transp.length: 0 blue.msb_right: 0 transp.msb_right: 0 nonstd: 0 activate: 128 width: 91 height: 122 accel_flags: 0 pixclock: 27027027 left_margin: 8 right_margin: 100 upper_margin: 4 lower_margin: 8 hsync_len: 4 vsync_len: 1 sync: 0 vmode: 0 rotate: 3 DPI: 167.472527 166.557377 167 waveform type = 2 material type = 0 temperature = 28 (C) ``` 根据如上信息,修正如下代码,给出完整代码; ```Rust use std::fs::OpenOptions; use std::io::{self, Write}; use std::mem::size_of; use std::os::fd::AsRawFd; const FB_DEVICE: &str = "/dev/fb0"; #[repr(C)] struct FbVarScreenInfo { xres: u32, yres: u32, xres_virtual: u32, yres_virtual: u32, xoffset: u32, yoffset: u32, bits_per_pixel: u32, grayscale: u32, red: FbBitfield, green: FbBitfield, blue: FbBitfield, transp: FbBitfield, nonstd: u32, activate: u32, height: u32, width: u32, accel_flags: u32, pixclock: u32, left_margin: u32, right_margin: u32, upper_margin: u32, lower_margin: u32, hsync_len: u32, vsync_len: u32, sync: u32, vmode: u32, rotate: u32, colorspace: u32, reserved: [u32; 4], } impl Default for FbVarScreenInfo { fn default() -> Self { FbVarScreenInfo { xres: 0, yres: 0, xres_virtual: 0, yres_virtual: 0, xoffset: 0, yoffset: 0, bits_per_pixel: 0, grayscale: 0, red: FbBitfield::default(), green: FbBitfield::default(), blue: FbBitfield::default(), transp: FbBitfield::default(), nonstd: 0, activate: 0, height: 0, width: 0, accel_flags: 0, pixclock: 0, left_margin: 0, right_margin: 0, upper_margin: 0, lower_margin: 0, hsync_len: 0, vsync_len: 0, sync: 0, vmode: 0, rotate: 0, colorspace: 0, reserved: [0; 4], } } } #[repr(C)] struct FbBitfield { offset: u32, length: u32, msb_right: u32, } impl Default for FbBitfield { fn default() -> Self { FbBitfield { offset: 0, length: 0, msb_right: 0, } } } fn main() -> io::Result<()> { let mut fb = OpenOptions::new().read(true).write(true).open(FB_DEVICE)?; let mut vinfo = FbVarScreenInfo::default(); unsafe { let ptr = &mut vinfo as *mut FbVarScreenInfo as *mut u8; let len = size_of::(); libc::ioctl(fb.as_raw_fd(), 0x4600, ptr); println!("request ioctl ok!\n"); } let mut buffer = vec![0; (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8) as usize]; // Clear the buffer to black buffer.iter_mut().for_each(|x| *x = 0); // Write the buffer to the framebuffer fb.write_all(&buffer)?; println!("Write the buffer to the framebuffer!\n"); Ok(()) } ``` 结果: ```RUST use std::fs::OpenOptions; use std::io::{self, Write}; use std::mem::size_of; use std::os::fd::AsRawFd; const FB_DEVICE: &str = "/dev/fb0"; #[repr(C)] struct FbVarScreenInfo { xres: u32, yres: u32, xres_virtual: u32, yres_virtual: u32, xoffset: u32, yoffset: u32, bits_per_pixel: u32, grayscale: u32, red: FbBitfield, green: FbBitfield, blue: FbBitfield, transp: FbBitfield, nonstd: u32, activate: u32, height: u32, width: u32, accel_flags: u32, pixclock: u32, left_margin: u32, right_margin: u32, upper_margin: u32, lower_margin: u32, hsync_len: u32, vsync_len: u32, sync: u32, vmode: u32, rotate: u32, colorspace: u32, reserved: [u32; 4], } impl Default for FbVarScreenInfo { fn default() -> Self { FbVarScreenInfo { xres: 0, yres: 0, xres_virtual: 0, yres_virtual: 0, xoffset: 0, yoffset: 0, bits_per_pixel: 0, grayscale: 0, red: FbBitfield::default(), green: FbBitfield::default(), blue: FbBitfield::default(), transp: FbBitfield::default(), nonstd: 0, activate: 0, height: 0, width: 0, accel_flags: 0, pixclock: 0, left_margin: 0, right_margin: 0, upper_margin: 0, lower_margin: 0, hsync_len: 0, vsync_len: 0, sync: 0, vmode: 0, rotate: 0, colorspace: 0, reserved: [0; 4], } } } #[repr(C)] struct FbBitfield { offset: u32, length: u32, msb_right: u32, } impl Default for FbBitfield { fn default() -> Self { FbBitfield { offset: 0, length: 0, msb_right: 0, } } } fn main() -> io::Result<()> { let mut fb = OpenOptions::new().read(true).write(true).open(FB_DEVICE)?; let mut vinfo = FbVarScreenInfo::default(); unsafe { let ptr = &mut vinfo as *mut FbVarScreenInfo as *mut u8; let len = size_of::(); libc::ioctl(fb.as_raw_fd(), 0x4600, ptr); println!("request ioctl ok!\n"); } let mut buffer = vec![0; (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8) as usize]; // Clear the buffer to black buffer.iter_mut().for_each(|x| *x = 0); // Write the buffer to the framebuffer fb.write_all(&buffer)?; println!("Write the buffer to the framebuffer!\n"); Ok(()) } ``` #### 分析eips ```SHELL which eips #/usr/sbin/eips cp /usr/sbin/eips /mnt/us ``` 分析: ```SHELL objdump -x /Users/workspace/Desktop/projects/kindle-plugin-touch/files/eips ``` 结果: ```SHELL /Users/workspace/Desktop/projects/kindle-plugin-touch/files/eips: file format elf32-littlearm architecture: arm start address: 0x000096b8 Program Header: UNKNOWN off 0x0000cb9c vaddr 0x00014b9c paddr 0x00014b9c align 2**2 filesz 0x00000340 memsz 0x00000340 flags r-- PHDR off 0x00000034 vaddr 0x00008034 paddr 0x00008034 align 2**2 filesz 0x00000100 memsz 0x00000100 flags r-x INTERP off 0x00000134 vaddr 0x00008134 paddr 0x00008134 align 2**0 filesz 0x00000013 memsz 0x00000013 flags r-- LOAD off 0x00000000 vaddr 0x00008000 paddr 0x00008000 align 2**15 filesz 0x0000cee0 memsz 0x0000cee0 flags r-x LOAD off 0x0000d000 vaddr 0x0001d000 paddr 0x0001d000 align 2**15 filesz 0x000008d0 memsz 0x00005b00 flags rw- DYNAMIC off 0x0000d00c vaddr 0x0001d00c paddr 0x0001d00c align 2**2 filesz 0x000000f8 memsz 0x000000f8 flags rw- NOTE off 0x00000148 vaddr 0x00008148 paddr 0x00008148 align 2**2 filesz 0x00000020 memsz 0x00000020 flags r-- STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2 filesz 0x00000000 memsz 0x00000000 flags rw- Dynamic Section: NEEDED libm.so.6 NEEDED libgcc_s.so.1 NEEDED libc.so.6 INIT 0x00008b1c FINI 0x00012ce0 INIT_ARRAY 0x0001d000 INIT_ARRAYSZ 0x00000004 FINI_ARRAY 0x0001d004 FINI_ARRAYSZ 0x00000004 GNU_HASH 0x0000818c STRTAB 0x000086ec SYMTAB 0x000083bc STRSZ 0x000001e0 SYMENT 0x00000010 DEBUG 0x00000000 PLTGOT 0x0001d104 PLTRELSZ 0x00000160 PLTREL 0x00000011 JMPREL 0x000089bc REL 0x000089a4 RELSZ 0x00000018 RELENT 0x00000008 VERNEED 0x00008934 VERNEEDNUM 0x00000003 VERSYM 0x000088cc Version References: required from libgcc_s.so.1: 0x0b792655 0x00 04 GCC_3.5 required from libm.so.6: 0x0d696914 0x00 03 GLIBC_2.4 required from libc.so.6: 0x06969196 0x00 05 GLIBC_2.16 0x0d696914 0x00 02 GLIBC_2.4 Sections: Idx Name Size VMA Type 0 00000000 00000000 1 .interp 00000013 00008134 DATA 2 .note.ABI-tag 00000020 00008148 3 .gnu.hash 00000230 0000818c 4 .dynsym 00000330 000083bc 5 .dynstr 000001e0 000086ec 6 .gnu.version 00000066 000088cc 7 .gnu.version_r 00000070 00008934 8 .rel.dyn 00000018 000089a4 9 .rel.plt 00000160 000089bc 10 .init 0000000c 00008b1c TEXT 11 .plt 00000224 00008b28 TEXT 12 .text 00009f90 00008d50 TEXT 13 .fini 00000008 00012ce0 TEXT 14 .rodata 00001afa 00012ce8 DATA 15 .ARM.extab 000003b8 000147e4 DATA 16 .ARM.exidx 00000340 00014b9c 17 .eh_frame 00000004 00014edc DATA 18 .init_array 00000004 0001d000 19 .fini_array 00000004 0001d004 20 .jcr 00000004 0001d008 DATA 21 .dynamic 000000f8 0001d00c 22 .got 000000c0 0001d104 DATA 23 .data 00000708 0001d1c8 DATA 24 .bss 00005230 0001d8d0 BSS 25 .comment 00000011 00000000 26 .ARM.attributes 00000035 00000000 27 .gnu_debuglink 00000010 00000000 28 .shstrtab 00000101 00000000 SYMBOL TABLE: ``` 给出kindle的eips命令的源码; 以下是Kindle的EIPS命令的Python源代码: ```python #!/usr/bin/python # # eips.py # # Kindle eips command line utility # # Written by Igor Skochinsky # This code is released into the public domain # import os import sys import struct import fcntl import termios # ioctl constants KDSETMODE = 0x4B3A KD_GRAPHICS = 0x01 KD_TEXT = 0x00 KDFONTOP = 0x4B42 KDFONTOP_SET = 0x01 def ioctl_GWINSZ(fd): try: cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) except: return None return cr def set_font_size(fd, size): fcntl.ioctl(fd, KDFONTOP, KDFONTOP_SET*size) def set_graphics_mode(fd): fcntl.ioctl(fd, KDSETMODE, KD_GRAPHICS) def set_text_mode(fd): fcntl.ioctl(fd, KDSETMODE, KD_TEXT) def clear_screen(): os.system("clear") def print_usage(): print("Usage: eips [options] [text]") print("Options:") print(" -h, --help display this help message") print(" -c, --clear clear the screen") print(" -g, --graphics set graphics mode") print(" -t, --text set text mode") print(" -f, --fontsize set font size (1-4)") print(" -x set X position (0-758)") print(" -y set Y position (0-1023)") print(" -i, --image display image file (JPEG or PNG)") def print_text(text, x, y): print("\033[%d;%dH%s" % (y, x, text)) def display_image(imagefile, x, y): try: import Image except ImportError: print("Python Imaging Library is required to display images") return try: im = Image.open(imagefile) im = im.convert("L") im = im.resize((758, 1023)) except: print("Failed to open image file") return pix = im.load() for j in range(0, 1023): for i in range(0, 758): if pix[i,j] > 128: print_text("#", x+i, y+j) def main(): x = 0 y = 0 mode = "text" font_size = 1 imagefile = None text = None # parse arguments i = 1 while i < len(sys.argv): arg = sys.argv[i] if arg == "-h" or arg == "--help": print_usage() return elif arg == "-c" or arg == "--clear": clear_screen() elif arg == "-g" or arg == "--graphics": mode = "graphics" elif arg == "-t" or arg == "--text": mode = "text" elif arg == "-f" or arg == "--fontsize": i += 1 if i >= len(sys.argv): print("Missing font size argument") return font_size = int(sys.argv[i]) if font_size < 1 or font_size > 4: print("Invalid font size") return elif arg == "-x": i += 1 if i >= len(sys.argv): print("Missing X position argument") return x = int(sys.argv[i]) if x < 0 or x > 758: print("Invalid X position") return elif arg == "-y": i += 1 if i >= len(sys.argv): print("Missing Y position argument") return y = int(sys.argv[i]) if y < 0 or y > 1023: print("Invalid Y position") return elif arg == "-i" or arg == "--image": i += 1 if i >= len(sys.argv): print("Missing image file argument") return imagefile = sys.argv[i] else: text = arg i += 1 # set font size fd = os.open("/dev/tty1", os.O_WRONLY) set_font_size(fd, font_size) # set mode if mode == "graphics": set_graphics_mode(fd) else: set_text_mode(fd) # display image if imagefile is not None: display_image(imagefile, x, y) # display text elif text is not None: print_text(text, x, y) os.close(fd) if __name__ == "__main__": main() ``` 这个源码实现了Kindle的EIPS命令的各种选项,包括清除屏幕、设置文本/图形模式、设置字体大小、设置文本位置和显示图像等。 #### rust运行shell命令 使用RUST;使用Linux;使用musl c库;在代码中调用SHELL的`echo hello`命令;给出代码; 以下是一个使用Rust,并在Linux上使用musl c库的示例代码,该代码将调用SHELL的`echo hello`命令并将其输出到终端: ```rust use std::process::Command; fn main() { let output = Command::new("sh") .arg("-c") .arg("echo hello") .output() .expect("failed to execute process"); let hello_str = String::from_utf8(output.stdout).unwrap(); println!("{}", hello_str); } ``` 在这个例子中,我们使用`std::process::Command`模块来启动一个新的进程并运行`sh -c "echo hello"`命令。该命令将在SHELL中运行,并将输出发送到标准输出流。我们使用`String::from_utf8()`方法将输出转换为字符串,并将其打印到终端上。 请注意,我们在这个例子中使用了`musl` C库,这是一个轻量级的C标准库,它可以在Linux系统上提供更小的二进制文件大小和更快的启动时间。如果您想在其他操作系统上运行此代码,您可能需要使用不同的C库或编译选项。 #### 使用RUST调用eips命令 使用RUST;使用Linux;使用musl c库;从`https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg`获取图片并保存到`/mnt/us/img1.jpg`;在代码中转换`/mnt/us/img1.jpg`的格式为bitmap并保存到`mnt/us/img1.bmp`;调用shell的`eips -b /mnt/us/img1.bmp`以显示图片;给出完整代码; 以下是使用RUST编写的程序,用于从给定的URL下载图片并将其转换为位图格式,最后在Kindle设备上显示: ```rust use std::fs::File; use std::io::prelude::*; use std::process::Command; use image::{GenericImageView, ImageFormat}; fn main() { // Download the image from the URL let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg"; let mut response = reqwest::get(url).unwrap(); let mut buffer = Vec::new(); response.read_to_end(&mut buffer).unwrap(); // Save the image to the file system let mut file = File::create("/mnt/us/img1.jpg").unwrap(); file.write_all(&buffer).unwrap(); // Convert the image to bitmap format let img = image::open("/mnt/us/img1.jpg").unwrap(); let bmp = img.to_bmp(); // Save the bitmap to the file system let mut file = File::create("/mnt/us/img1.bmp").unwrap(); bmp.write(&mut file, ImageFormat::Bmp).unwrap(); // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-b") .arg("/mnt/us/img1.bmp") .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); } ``` 修改为使用`wget`命令获取`https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg`文件; 修改后的代码如下: ```rust use std::process::Command; use image::{GenericImageView, ImageFormat}; fn main() { // Download the image from the URL using wget command let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg"; let output = Command::new("wget") .arg("-O") .arg("/mnt/us/img1.jpg") .arg(url) .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); // Convert the image to bitmap format let img = image::open("/mnt/us/img1.jpg").unwrap(); let bmp = img.to_bmp(); // Save the bitmap to the file system let mut file = std::fs::File::create("/mnt/us/img1.bmp").unwrap(); bmp.write(&mut file, ImageFormat::Bmp).unwrap(); // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-b") .arg("/mnt/us/img1.bmp") .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); } ``` 这个代码使用了`wget`命令来下载图片,而不是使用`reqwest`库。修改后的代码与之前的代码相比,只有少量的更改。我们用`Command::new("wget")`来生成一个`wget`命令,然后使用`.arg()`方法来传递命令行参数。在这个例子中,我们使用了`-O`参数来指定输出文件名,`url`参数来指定要下载的URL。最后,我们使用`.output()`方法来执行命令并获取输出。 请注意,此程序需要在Linux操作系统上运行,并使用musl c库。在运行之前,请确保已经安装了必要的依赖项(例如Rust编译器、reqwest和image库)。 报错: ```SHELL cargo:rerun-if-env-changed=ARMV7_UNKNOWN_LINUX_MUSLEABIHF_OPENSSL_DIR ARMV7_UNKNOWN_LINUX_MUSLEABIHF_OPENSSL_DIR unset cargo:rerun-if-env-changed=OPENSSL_DIR OPENSSL_DIR unset cargo:rerun-if-env-changed=OPENSSL_NO_PKG_CONFIG --- stderr thread 'main' panicked at ' Could not find directory of OpenSSL installation, and this `-sys` crate cannot proceed without this knowledge. If OpenSSL is installed and this crate had trouble finding it, you can set the `OPENSSL_DIR` environment variable for the compilation process. Make sure you also have the development packages of openssl installed. For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora. If you're in a situation where you think the directory *should* be found automatically, please open a bug at https://github.com/sfackler/rust-openssl and include information about your system as well as this message. $HOST = aarch64-apple-darwin $TARGET = armv7-unknown-linux-musleabihf openssl-sys = 0.9.87 ', /Users/workspace/.cargo/registry/src/github.com-1ecc6299db9ec823/openssl-sys-0.9.87/build/find_normal.rs:190:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` 这是一个Rust Cargo构建脚本中的一些语句,用于检查环境变量并在必要时重新运行构建脚本。这些语句通常与OpenSSL库的构建和链接相关。 如果你遇到了这些语句并且不知道该怎么做,可以尝试以下步骤: 1. 检查你的环境变量是否正确设置了OpenSSL的路径。如果你已经安装了OpenSSL,可以尝试设置`OPENSSL_DIR`环境变量来指定OpenSSL的路径。 2. 如果你不确定要设置什么环境变量,可以尝试使用`pkg-config`来获取OpenSSL的路径。你可以安装`pkg-config`,然后运行`pkg-config --cflags openssl`来获取OpenSSL的C编译器标志,或者运行`pkg-config --libs openssl`来获取OpenSSL的链接器标志。 3. 如果你仍然遇到问题,可以尝试安装或重新安装OpenSSL库,并确保它已正确安装并配置。然后重新运行Cargo构建命令。 如果你仍然无法解决问题,你可以尝试搜索类似的错误信息,或者在Rust社区中寻求帮助。 你可以使用 `image` crate 来将 JPG 图片转换为 PNG 图片。下面是一个简单的 Rust 代码示例: ```rust use image::io::Reader as ImageReader; use image::ImageFormat; fn main() { // 打开 JPG 图片 let img = ImageReader::open("input.jpg").unwrap().decode().unwrap(); // 将图片保存为 PNG 格式 img.save_with_format("output.png", ImageFormat::Png).unwrap(); } ``` 这个示例首先使用 `ImageReader` 打开 JPG 图片,然后将其解码为 `DynamicImage`。接下来,它使用 `save_with_format` 方法将图片保存为 PNG 格式。你可以将 `"input.jpg"` 替换为你想要转换的 JPG 图片的路径,将 `"output.png"` 替换为你想要保存 PNG 图片的路径。 请确保在你的 `Cargo.toml` 文件中添加了以下依赖项: ```toml [dependencies] image = "0.23.12" ``` 这将允许你使用 `image` crate 来处理图像。 ```Rust // Convert the image to bitmap format let img = image::open("/mnt/us/img1.jpg").unwrap(); let bmp = img.save_with_format("", format) // Save the bitmap to the file system let mut file = std::fs::File::create("/mnt/us/img1.bmp").unwrap(); bmp.write(&mut file, ImageFormat::Bmp).unwrap(); ``` 修正如上代码; 你的代码中有一个小错误,`save_with_format` 方法返回一个 `Result<(), std::io::Error>` 类型的结果,因此你需要使用 `unwrap()` 或其他错误处理方法来处理它的结果。下面是一个修正后的代码示例: ```rust use image::io::Reader as ImageReader; use image::{DynamicImage, ImageFormat}; fn main() { // 打开 JPG 图片 let img = ImageReader::open("/mnt/us/img1.jpg").unwrap().decode().unwrap(); // 将图片保存为 BMP 格式 let bmp = img.save_with_format("/mnt/us/img1.bmp", ImageFormat::Bmp).unwrap(); // 将 BMP 图片保存到文件系统 let mut file = std::fs::File::create("/mnt/us/img1.bmp").unwrap(); bmp.write(&mut file, ImageFormat::Bmp).unwrap(); } ``` 这个示例首先使用 `ImageReader` 打开 JPG 图片,然后将其解码为 `DynamicImage`。接下来,它使用 `save_with_format` 方法将图片保存为 BMP 格式,并将结果保存到 `bmp` 变量中。最后,它打开一个文件并将 `bmp` 变量中的 BMP 图片写入该文件中。 请注意,`save_with_format` 方法将 BMP 图片直接保存到文件系统中,因此你不需要再次将其写入文件中。 ```/opt/homebrew/Homebrew/Cellar/openssl@3/3.0.7``` 报错: ```SHELL use image::{GenericImageView, ImageFormat}; --> src/main.rs:17:19 | 17 | let bmp = img.to_bmp(); | ^^^^^^ method not found in `DynamicImage` ``` 这个错误提示表明在 `DynamicImage` 上找不到 `to_bmp` 方法。这可能是因为 `to_bmp` 方法已经被删除或者被移动到了另一个模块中。 你可以检查你的代码,确保你正确导入了 `image` crate,并且使用了最新版本的 `image` crate。你也可以查看 `image` crate 的文档,以确定 `to_bmp` 方法是否已被删除或移动。如果 `to_bmp` 方法已经被删除或移动,你需要查找替代方法或更新你的代码以适应最新版本的 `image` crate。 编译: ```SHELL cargo add reqwest cargo add image export OPENSSL_DIR=/opt/homebrew/Homebrew/Cellar/openssl@3/3.0.7 echo $OPENSSL_DIR cargo build --target armv7-unknown-linux-musleabihf --release ``` #### 配置vscode插件的网络代理 要配置VSCode插件的网络代理,您需要按照以下步骤进行操作: 1. 打开VSCode并进入“扩展”选项卡。 2. 在搜索栏中输入“settings.json”并点击“打开设置(JSON)”。 3. 在打开的JSON文件中,添加以下代码: ```JSON "http.proxy": "http://yourproxyaddress:yourproxyport", "https.proxy": "http://yourproxyaddress:yourproxyport", "http.proxyStrictSSL": false ``` 请注意,您需要将“yourproxyaddress”和“yourproxyport”替换为您的代理服务器地址和端口号。如果您的代理服务器需要身份验证,则还需要添加以下代码: ```JSON "http.proxyAuthorization": "basic base64encodedcredentials" ``` 在这里,您需要将“base64encodedcredentials”替换为您的代理服务器的用户名和密码的base64编码。 4. 保存文件并重新启动VSCode,以确保更改生效。 这样,您就可以成功配置VSCode插件的网络代理。 #### rust的 Blocking waiting for file lock on package cache **原因:rust-analyzer插件** rust的 Blocking waiting for file lock on package cache,如何手动解锁文件? 在Rust中,如果您尝试在使用`cargo`命令时看到“Blocking waiting for file lock on package cache”错误消息,这意味着某些文件正在被锁定,从而阻止了`cargo`的正常运行。您可以尝试手动解锁文件来解决此问题。以下是一些可能的解决方法: 1. 关闭所有正在运行的Rust程序和IDE,并等待一段时间,以确保任何正在使用缓存的进程已经全部关闭。然后再次尝试运行`cargo`命令,看看问题是否已经解决。 2. 如果您使用的是Windows操作系统,请尝试使用“资源监视器”来查找并解锁被锁定的文件。打开资源监视器,然后转到“CPU”选项卡,然后选择“关联的句柄”选项卡。在“搜索句柄”字段中输入“package_cache”,然后查找与该文件关联的任何进程。选择进程,然后单击“关闭句柄”按钮,以解锁文件。 3. 如果您使用的是Linux或MacOS操作系统,请尝试使用“lsof”命令来查找并解锁被锁定的文件。打开终端窗口,然后输入以下命令: ```SHELL lsof | grep package_cache ``` 这将显示与该文件关联的任何进程。找到进程ID(PID),然后使用以下命令杀死该进程: ```SHELL kill -9 PID ``` 这将强制终止该进程,并解锁文件。 请注意,手动解锁文件可能会导致数据丢失或其他问题,因此请务必在进行此操作之前备份所有相关文件。 #### kindle的eips命令显示图片变形 kindle的eips命令显示图片变形是什么原因?图片尺寸与屏幕尺寸相同. 可能是由于图片的分辨率不够高,或者是图片格式不受Kindle支持所导致的。EIPS命令在Kindle上用于显示电子墨水屏幕上的图像,但是如果图片的分辨率不足,它可能会在显示时被拉伸或缩小,从而导致变形。 为了避免这种情况,您可以尝试将图片的分辨率增加到与Kindle屏幕分辨率相同,或者将图片转换为Kindle支持的格式,例如JPEG或PNG格式。您还可以尝试使用其他命令,例如FBI或MANGA,以查看是否会产生相同的变形问题。 eips帮助: ```SHELL For complete usage and examples, search eips in wiki page. usage: to print strings: eips [row] [col] [-h] string -h for highlighted text to show image: eips -g|-b image_path [-w waveform -f -x xpos -y ypos -v] -g for png, -b for bitmap -w waveform can be gc16, gl16, reagl, reagld, gck16, glkw16 or du, default is gc16, -f for full update, default is partial update -x -y in units of pixels -v for inverted picture -k filename, save workingbuffer to file to paint a retangle on screen: eips -d l=a,w=b,h=c [-x xx -y yy -w wf] a is gray level in hex, b is width, c is height, with optional xx yy location, waveform mode to clear display: eips -c to paint checker: eips -q to paint grayscale: eips -l to paint pattern: eips -p to paint barcode: eips -r barcode to overlay a grid: eips -a 5 (grid size) to print fb info: eips -i to flash display with current fb content: eips -s w=758,h=1024 -f ``` 根据如上命令帮助,给出显示`/mnt/us/output.png`图片的命令; 解释waveform模式和update模式 在Kindle设备上,波形模式和更新模式是用于控制电子墨水显示的两个重要参数。 1. 波形模式 波形模式是指在Kindle设备上用于控制电子墨水显示的电压波形。Kindle设备支持多种波形模式,包括gc16、gl16、reagl、reagld、gck16、glkw16和du等。每种波形模式都有不同的优点和缺点,例如显示速度、灰度级别和能耗等方面的差异。 2. 更新模式 更新模式是指在Kindle设备上更新电子墨水屏幕的方式。Kindle设备支持两种更新模式:部分更新和全屏更新。部分更新模式仅更新屏幕上需要更改的部分,而全屏更新模式则会更新整个屏幕。部分更新模式可以更快地更新屏幕,但可能会导致一些残留效应,而全屏更新模式则可以更好地清除屏幕上的残留效应,但更新速度较慢。 在使用eips命令显示图片时,可以使用-w选项来指定波形模式,并使用-f选项来指定更新模式。例如,使用-eips -w reagl -f -g /mnt/us/output.png命令可以使用reagl波形模式并进行全屏更新来显示图片。 #### 使用Python查看png文件的元数据 使用Python查看```/Users/workspace/Desktop/projects/kindle-plugin-touch/kindle_weather_one/touch/error.png```图片的所有元信息;给出代码; 可以使用Python中的Pillow库来查看图片的元信息。以下是一个示例代码,可以读取并打印指定路径下图片的元信息: ```python from PIL import Image # 指定图片路径 image_path = '/Users/workspace/Desktop/projects/kindle-plugin-touch/kindle_weather_one/touch/error.png' # 打开图片并获取元信息 with Image.open(image_path) as img: exif_data = img._getexif() # 打印元信息 print(exif_data) ``` 注意,元信息的格式可能因图片类型而异。如果要查看更多特定于图片类型的元信息,请查看Pillow文档。 可以使用Python中的Pillow库来查看图片的元信息。以下是一个示例代码,可以读取并打印指定路径下图片的所有元信息: ```python from PIL import Image # 指定图片路径 image_path = '/Users/workspace/Desktop/projects/kindle-plugin-touch/kindle_weather_one/touch/error.png' # 打开图片并获取元信息 with Image.open(image_path) as img: exif_data = img._getexif() # 打印元信息 for tag_id in exif_data: tag = TAGS.get(tag_id, tag_id) data = exif_data.get(tag_id) if isinstance(data, bytes): data = data.decode() print(f"{tag:25}: {data}") ``` 注意,元信息的格式可能因图片类型而异。如果要查看更多特定于图片类型的元信息,请查看Pillow文档。 报错: ```SHELL TypeError Traceback (most recent call last) /Users/workspace/Desktop/projects/kindle-plugin-touch/files/test1.ipynb 单元格 1 in () 8 exif_data = img._getexif() 10 # 打印元信息 ---> 11 for tag_id in exif_data: 12 tag = ExifTags.get(tag_id, tag_id) 13 data = exif_data.get(tag_id) TypeError: 'NoneType' object is not iterable ``` 这个错误可能是因为该图片没有元信息或元信息无法被解析。在Pillow中,如果图片没有元信息或无法解析元信息,则`_getexif()`方法将返回`None`。在这种情况下,我们可以添加一个条件语句来检查`exif_data`是否为`None`,以避免迭代`None`对象而导致的`TypeError`错误。以下是修改后的代码: ```python from PIL import Image, ExifTags # 指定图片路径 image_path = '/Users/workspace/Desktop/projects/kindle-plugin-touch/kindle_weather_one/touch/error.png' # 打开图片并获取元信息 with Image.open(image_path) as img: exif_data = img._getexif() # 打印元信息 if exif_data: for tag_id in exif_data: tag = ExifTags.get(tag_id, tag_id) data = exif_data.get(tag_id) if isinstance(data, bytes): data = data.decode() print(f"{tag:25}: {data}") else: print("该图片没有元信息或元信息无法被解析。") ``` 这样,如果图片没有元信息或无法解析元信息,则代码将输出一条相应的提示消息。 #### 在kterm外显示图片 ```SHELL #!/bin/sh touch /mnt/us/extensions/touch/1.txt #禁止休眠 lipc-set-prop -i com.lab126.powerd preventScreenSaver 1 #显示图片 #eips -c #eips -g /mnt/us/extensions/touch/kindle.png eips -w reagl -f -g /mnt/us/error.png #eips '' #开启wifi lipc-set-prop com.lab126.cmd wirelessEnable 1 #延时5s sleep 5 #显示另一张图片 #eips -g /mnt/us/extensions/touch/hello.jpg #延时3秒 #sleep 3 #获取新图 if curl "https://www.qsbye.cn/infoimg/kindle.png"|tee /mnt/us/extensions/touch/kindle.png;then #eips -c eips -g /mnt/us/extensions/touch/kindle.png else if eips -g /mnt/us/extensions/touch/default.png;then #sleep 2 eips -g /mnt/us/extensions/touch/error.png fi fi #延时8小时 sleep 8h #循环获取图片 /mnt/us/extensions/touch/touch.sh #调试 touch /mnt/us/extensions/touch/2.txt ``` #### 图片的颜色描述文件和色彩空间 png图片的色彩空间为RGB和Grey有什么不同?在kindle上只有Grey空间的图片能按照原来的比例正确显示的原因是什么? RGB和Grey是两种不同的色彩空间。RGB是一种彩色空间,由红色、绿色和蓝色三个基本颜色组成,可以产生各种颜色。而Grey是一种灰度空间,只有黑白两种颜色,没有彩色。 在Kindle等电子墨水屏幕上,只有Grey空间的图片能够按照原来的比例正确显示。这是因为电子墨水屏幕只能显示黑白两种颜色,无法显示彩色。因此,如果一个彩色图片在电子墨水屏幕上显示,必须将其转换为灰度图像。在这种情况下,只有Grey空间的图片能够按照原来的比例正确显示,因为它们只有一个颜色通道,而RGB空间的图片有三个颜色通道,转换为灰度图像时需要进行颜色通道的合并和平均,会导致比例失真。 #### 转换图片为Grey空间 ```Rust use std::process::Command; use image::{GenericImageView, ImageFormat,GrayImage, RgbImage}; fn main() { // Download the image from the URL using wget command let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg"; let output = Command::new("wget") .arg("-O") .arg("/mnt/us/img1.jpg") .arg(url) .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); // Convert the image to bitmap format let img = image::open("/mnt/us/img1.jpg").unwrap(); // 将图片保存为 BMP 格式 let bmp = img.save_with_format("/mnt/us/img1.bmp", ImageFormat::Bmp).unwrap(); // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-b") .arg("/mnt/us/img1.bmp") .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); } ``` 将bmp保存为Grey颜色空间;使用`eips -f -b /mnt/us/img1.bmp`显示图片;修正如上代码; ```Rust use std::process::Command; use image::{GenericImageView, ImageFormat, GrayImage, RgbImage}; fn main() { // Download the image from the URL using wget command let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg"; let output = Command::new("wget") .arg("-O") .arg("/mnt/us/img1.jpg") .arg(url) .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); // Convert the image to bitmap format and save as gray color space let img = image::open("/mnt/us/img1.jpg").unwrap().into_luma8(); let bmp = img.save_with_format("/mnt/us/img1.bmp", ImageFormat::Bmp).unwrap(); // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-f") .arg("-b") .arg("/mnt/us/img1.bmp") .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); } ``` 将图片转换为为png格式而不是bmp格式;使用`eips -f -g /mnt/us/img1.png`显示图片;修正如上代码; 好的,以下是更新后的代码: ```Rust use std::process::Command; use image::{GenericImageView, ImageFormat, GrayImage, RgbImage}; fn main() { // Download the image from the URL using wget command let url = "https://img.zcool.cn/community/01db63554977e30000019ae9930d77.jpg"; let output = Command::new("wget") .arg("-O") .arg("/mnt/us/img1.jpg") .arg(url) .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); // Convert the image to bitmap format and save as png color space let img = image::open("/mnt/us/img1.jpg").unwrap().into_luma8(); let png = img.save_with_format("/mnt/us/img1.png", ImageFormat::Png).unwrap(); // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-f") .arg("-g") .arg("/mnt/us/img1.png") .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); } ``` 在这个更新后的代码中,我们将图片的格式从bmp更改为png。我们还将`eips`命令的参数更改为`-g`,这将使图片以全屏模式显示。 ### 第七天:2023年5月22日 主要内容:php的图片订阅源代码;CSS布局; #### 更新php的图片订阅 ```php 7.4 /**定时获取消息,生成图片 */ $font_file6="yuanti.ttf";//圆体 /* *$sourePic:原图路径 * $smallFileName:小图名称 * $width:小图宽 * $heigh:小图高 */ function pngthumb($smallFileName, $width, $heigh,$cityname,$temperature,$humidity,$weather,$weather_logo,$yiyan,$board,$last_update){ /*创建画布,添加文字*/ //创建画布600*800 $im = imagecreatetruecolor($width, $heigh); //填充画布背景色 $color = imagecolorallocate($im, 255, 255, 255); imagefill($im, 0, 0, $color); //字体文件 $font_file="heiti.ttf"; $font_file2="xingshu.ttf";//春联行书体 $font_file3="tttgb.ttf";//腾讯体 $font_file4="miaowu.ttf";//喵物体 $font_file5="yuanti.ttf";//圆体 //!!!!!!版权原因,就不上传字体文件了,自行替换 //设定字体颜色 $font_color_1 = ImageColorAllocate ($im, 140, 140, 140); $font_color_2 = ImageColorAllocate ($im, 28, 28, 28); $font_color_3 = ImageColorAllocate ($im, 129, 129, 129); $font_color_red = ImageColorAllocate ($im, 217, 45, 32); $fang_bg_color = ImageColorAllocate ($im, 254, 216, 217); //添加文字 /**参数:画布,字体大小(磅),旋转角度,基线点x,y(大致从左下开始),字体颜色,字体文件,utf8字符串(php以utf8存储) */ imagettftext($im, 57,0, 50, 130, $font_color_2 ,$font_file2, $cityname); imagettftext($im, 40,0, 50, 300, $font_color_2 ,$font_file, $temperature); imagettftext($im, 40,0, 50, 400, $font_color_2 ,$font_file, $humidity); imagettftext($im, 28,0, 50, 460, $font_color_2 ,$font_file3, $weather); imagettftext($im, 57,0, 400, 460, $font_color_2 ,$font_file, '☁'); imagettftext($im, 14,0, 50, 600, $font_color_2 ,$font_file4, $yiyan); imagettftext($im, 14,0, 300, 50, $font_color_2 ,$font_file, '公告'); imagettftext($im, 14,0, 300, 100, $font_color_2 ,$font_file5, $board); imagettftext($im, 14,0, 50, 700, $font_color_2 ,$font_file,$last_update); /****/ imagepng($im,$smallFileName);//保存到/infoimg目录 return $smallFileName;//返回小图路径 } function autowrap($fontsize, $angle, $fontface, $string, $width) { //自动换行(11个字) // 这几个变量分别是 字体大小, 角度, 字体名称, 字符串, 预设宽度 $content = ""; $content_this_line=''; // 将字符串拆分成一个个单字 保存到数组 letter 中 for ($i=0;$i $width) { $content .= "\n"; $content_this_line=''; } $content_this_line.=$l; $content .= $l; } return $content; } function GetTextFromUrl(){ $url1='https://api.seniverse.com/v3/weather/now.json?key=你申请的密钥&location=guangzhou&language=zh-Hans&unit=c'; $url2='https://api.seniverse.com/v3/weather/daily.json?key=你申请的密钥&location=guangzhou&language=zh-Hans&unit=c'; $url3='https://v1.hitokoto.cn/?encode=text'; $url4='https://nightchina.net/wp-content/uploads/2014/05/nightchina.net-web-LOGO-1501.jpg'; $url5='./board.json'; $url6='https://v1.hitokoto.cn/'; $url7='https://api.seniverse.com/v3/weather/daily.json?key=你申请的密钥&location=ip&language=zh-Hans&unit=c';//通过ip判断地区 $now_json = file_get_contents($url1);// 获取数据 $daily_json=file_get_contents($url2); $board_json=file_get_contents($url5); $yiyan_json=file_get_contents($url6); //$yiyan=file_get_contents($url3); global $cityname,$weather,$temperature,$humidity,$wind_directioin,$last_update,$yiyan,$board_message,$board_message_time; $now_content=json_decode($now_json); $daily_content=json_decode($daily_json); $cityname=$now_content->results[0]->location->name; $weather=$now_content->results[0]->now->text; $temperature='温度:'.$now_content->results[0]->now->temperature.'℃'; $humidity='湿度:'.$daily_content->results[0]->daily[0]->humidity.'%'; $wind_directioin='风向:'.$daily_content->results[0]->daily[0]->wind_direction; $last_update=$now_content->results[0]->last_update; $last_update[10]=' ';$last_update[19]=' ';$last_update[20]=' ';$last_update[21]=' ';$last_update[22]=' ';$last_update[23]=' ';$last_update[24]=' '; $last_update='上次更新:'.$last_update; //解析公告板 $board_content=json_decode($board_json); $i1=0; while($board_content->contents[$i1]->message!=NULL){ $board_message[$i1]=$board_content->contents[$i1]->message; //换行 $board_message[$i1]=autowrap(14,0,"yuanti.ttf",$board_message[$i1],300); $board_message_time[$i1]=$board_content->contents[$i1]->post_time; $i1++; } //解析一言 $yiyan_content=json_decode($yiyan_json); $yiyan=$yiyan_content->hitokoto; return ; } GetTextFromUrl(); pngthumb("kindle.png", 600, 800,$cityname,$temperature,$humidity,$weather,NULL,$yiyan,$board_message[0]."\n".'('.$board_message_time[0].')',$last_update);//调用 echo '
'; ?> ``` 参考如上代码;页面布局:全部内容布局在屏幕上10%和下30%的区域,中间区域不布局内容;不使用如上代码中的字体;使用Python,给出生成png图片的代码; 以下是使用Python生成PNG图片的代码: ```python from PIL import Image, ImageDraw, ImageFont import requests import json # 获取天气信息 def get_weather_info(): url1 = 'https://api.seniverse.com/v3/weather/now.json?key=你申请的密钥&location=guangzhou&language=zh-Hans&unit=c' url2 = 'https://api.seniverse.com/v3/weather/daily.json?key=你申请的密钥&location=guangzhou&language=zh-Hans&unit=c' url5 = './board.json' url6 = 'https://v1.hitokoto.cn/' url7 = 'https://api.seniverse.com/v3/weather/daily.json?key=你申请的密钥&location=ip&language=zh-Hans&unit=c' now_json = requests.get(url1).text daily_json = requests.get(url2).text board_json = requests.get(url5).text yiyan_json = requests.get(url6).text now_content = json.loads(now_json) daily_content = json.loads(daily_json) board_content = json.loads(board_json) yiyan_content = json.loads(yiyan_json) cityname = now_content['results'][0]['location']['name'] weather = now_content['results'][0]['now']['text'] temperature = '温度:' + str(now_content['results'][0]['now']['temperature']) + '℃' humidity = '湿度:' + str(daily_content['results'][0]['daily'][0]['humidity']) + '%' wind_directioin = '风向:' + daily_content['results'][0]['daily'][0]['wind_direction'] last_update = now_content['results'][0]['last_update'] last_update = last_update[:10] + ' ' + last_update[11:19] yiyan = yiyan_content['hitokoto'] board_message = [] board_message_time = [] for content in board_content['contents']: if content['message'] is not None: board_message.append(content['message']) board_message_time.append(content['post_time']) return cityname, temperature, humidity, weather, yiyan, board_message, board_message_time, last_update # 自动换行 def autowrap(fontsize, angle, fontface, string, width): content = "" content_this_line = '' letter = [l for l in string] for l in letter: teststr = content_this_line + " " + l testbox = ImageFont.truetype(fontface, fontsize).getsize(teststr) if testbox[0] > width: content += "\n" content_this_line = '' content_this_line += l content += l return content # 生成图片 def generate_image(): cityname, temperature, humidity, weather, yiyan, board_message, board_message_time, last_update = get_weather_info() # 设置字体 font_file = "heiti.ttf" font_file2 = "xingshu.ttf" font_file3 = "tttgb.ttf" font_file4 = "miaowu.ttf" font_file5 = "yuanti.ttf" # 设置字体颜色 font_color_1 = (140, 140, 140) font_color_2 = (28, 28, 28) font_color_3 = (129, 129, 129) font_color_red = (217, 45, 32) fang_bg_color = (254, 216, 217) # 创建画布 im = Image.new("RGB", (600, 800), (255, 255, 255)) draw = ImageDraw.Draw(im) # 添加文字 draw.text((50, 130), cityname, font_color_2, font=ImageFont.truetype(font_file2, 57)) draw.text((50, 300), temperature, font_color_2, font=ImageFont.truetype(font_file, 40)) draw.text((50, 400), humidity, font_color_2, font=ImageFont.truetype(font_file, 40)) draw.text((50, 460), weather, font_color_2, font=ImageFont.truetype(font_file3, 28)) draw.text((400, 460), '☁', font_color_2, font=ImageFont.truetype(font_file, 57)) draw.text((50, 600), yiyan, font_color_2, font=ImageFont.truetype(font_file4, 14)) draw.text((300, 50), '公告', font_color_2, font=ImageFont.truetype(font_file, 14)) draw.text((300, 100), autowrap(14, 0, font_file5, board_message[0], 300) + '\n(' + board_message_time[0] + ')', font_color_2, font=ImageFont.truetype(font_file5, 14)) draw.text((50, 700), last_update, font_color_2, font=ImageFont.truetype(font_file, 14)) # 保存图片 im.save("kindle.png") if __name__ == '__main__': generate_image() ``` 需要注意的是,由于字体版权问题,需要替换代码中的字体文件路径。同时,由于涉及到天气API的使用,需要替换代码中的API密钥。 给出CSS布局代码; 以下是使用CSS实现的布局代码: ```html Weather Info

Weather Info

``` 在这个布局中,整个页面被分为三个部分:头部、主体和底部。头部和底部各占页面高度的10%,主体占80%。头部和底部都使用红色背景色,并居中显示文本。主体部分则包含了生成的PNG图片,使用了`max-width`和`max-height`属性来保证图片在不失真的情况下占满整个主体部分。 #### 页面空间测量 - 宽90mm - 长122mm - 底部向上至47mm - 顶部向下至20mm #### bing每日一图的网页 ```HTML 每日Bing图 - 刘明野的工具箱
描述:用于获取并返回必应今日美图
优势:
  • 支持跨域
  • 支持多分辨率
  • 采用安全可靠的https协议
  • 可写入css样式 background-images等
请求:
  • 请求地址: http://tool.liumingye.cn/bingimg/img.php
  • 请求地址(HTTPS): https://tool.liumingye.cn/bingimg/img.php
  • 请求方法: GET
请求示例:
  • http://tool.liumingye.cn/bingimg/img.php
  • <img src="http://tool.liumingye.cn/bingimg/img.php" />
  • <div style="background-image: url(http://tool.liumingye.cn/bingimg/img.php);"></div>
响应:重定向至今日美图地址

♪(^∇^*) 一言加载中...
资源来源于网络,如有侵权,请联系本人予以删除!
© 2015 - 2023

``` #### CSS布局2 ```HTML 每日Bing图 - 刘明野的工具箱
描述:用于获取并返回必应今日美图
优势:
  • 支持跨域
  • 支持多分辨率
  • 采用安全可靠的https协议
  • 可写入css样式 background-images等
请求:
  • 请求地址: http://tool.liumingye.cn/bingimg/img.php
  • 请求地址(HTTPS): https://tool.liumingye.cn/bingimg/img.php
  • 请求方法: GET
请求示例:
  • http://tool.liumingye.cn/bingimg/img.php
  • <img src="http://tool.liumingye.cn/bingimg/img.php" />
  • <div style="background-image: url(http://tool.liumingye.cn/bingimg/img.php);"></div>
响应:重定向至今日美图地址

♪(^∇^*) 一言加载中...
资源来源于网络,如有侵权,请联系本人予以删除!
© 2015 - 2023

``` 页面顶部向下20/122的空间为div_top;页面中间55/122的空间为div_middle;页面底部向上47/122的空间为div_bottom;图片``布局在div_top中,居中布局,与div内部边距为10% 10% 10% 10%,四周圆角6px;文字`2023年5月23日`布局在div_bottom中,居中布局,字号5pm;参考如上代码,给出完整html代码; 以下是您所需的HTML代码: ```HTML
2023年5月23日
``` 代码中,我们使用了两个`div`元素,分别命名为`div_top`和`div_bottom`,用于布局页面顶部和底部的内容。在`div_top`中,我们使用了`padding`属性来设置内部的边距,并使用`border-radius`属性来设置四周的圆角。图片元素使用了`display: block`来使其居中显示,并使用`margin: 0 auto`来水平居中。在`div_bottom`中,我们使用了`font-size`属性来设置字号,并使用居中对齐来使其居中显示。 以下是完整的HTML代码: ```HTML My Page
2023年5月23日
``` 在这个完整的HTML代码中,我们使用了``标签来设置页面的元数据,包括字符集和标题。在``标签中,我们使用了两个`div`元素,分别命名为`div_top`和`div_bottom`,用于布局页面顶部和底部的内容。在`div_top`中,我们使用了`padding`属性来设置内部的边距,并使用`border-radius`属性来设置四周的圆角。图片元素使用了`display: block`来使其居中显示,并使用`margin: 0 auto`来水平居中。在`div_bottom`中,我们使用了`font-size`属性来设置字号,并使用居中对齐来使其居中显示。 以下是完整的HTML代码: ```html 页面布局
2023年5月23日
``` 在这个代码中,我们使用了三个`div`元素来布局页面的不同部分,分别是顶部的`div_top`、中间的`div_middle`和底部的`div_bottom`。其中,顶部的`div_top`包含了一个图片,使用了居中布局和内部边距为10%的样式。底部的`div_bottom`包含了一个文字,使用了居中布局和5pt字号的样式。中间的`div_middle`没有设置任何样式,留待后续添加内容时再进行布局。 ```HTML 每日Bing图 - 刘明野的工具箱
描述:用于获取并返回必应今日美图
优势:
  • 支持跨域
  • 支持多分辨率
  • 采用安全可靠的https协议
  • 可写入css样式 background-images等
请求:
  • 请求地址: http://tool.liumingye.cn/bingimg/img.php
  • 请求地址(HTTPS): https://tool.liumingye.cn/bingimg/img.php
  • 请求方法: GET
请求示例:
  • http://tool.liumingye.cn/bingimg/img.php
  • <img src="http://tool.liumingye.cn/bingimg/img.php" />
  • <div style="background-image: url(http://tool.liumingye.cn/bingimg/img.php);"></div>
响应:重定向至今日美图地址
2023年5月23日

♪(^∇^*) 一言加载中...
资源来源于网络,如有侵权,请联系本人予以删除!
© 2015 - 2023

``` #### CSS布局3 ```HTML 每日Bing图

♪(^∇^*) 一言加载中...
2023年5月23日

``` #### 页面布局的CSS框架-AmazeUI [https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css] `https://npm.elemecdn.com/amazeui@2.7.2/dist/css/amazeui.flat.min.css`的CSS框架有什么特点? `amazeui.flat.min.css` 是 Amaze UI 2.7.2 版本的 CSS 框架,其特点如下: 1. 扁平化设计:Amaze UI 的设计风格以扁平化为主,简洁、直观、美观。 2. 响应式布局:Amaze UI 支持响应式布局,能够自适应不同设备的屏幕尺寸。 3. 组件丰富:Amaze UI 提供了丰富的组件,如导航栏、按钮、表单、标签页、轮播图等,可以满足大部分网站的需求。 4. 轻量级:Amaze UI 的 CSS 文件非常小,压缩后只有几十 KB,可以快速加载,提高网站的性能。 5. 易于使用:Amaze UI 的文档详细,提供了丰富的示例和代码片段,让开发者能够快速上手使用。 总的来说,Amaze UI 是一个功能丰富、设计美观、易于使用的 CSS 框架,适合用于构建各种类型的网站。 #### CSS布局4 ```CSS .div_top{ margin-top:20px; width:100%; text-align:center; border-radius:6px; overflow:hidden; box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4); } .div_top img{ width:80%; border-radius:6px; } .div_bottom{ margin-bottom:47px; width:100%; text-align:center; font-size:5pm; } .div_middle{ height:55%; } ``` 固定让div_middle占用页面中部的55%的空间,没有内容就留白;固定让div_top在页面顶部,距离顶部距离为0;固定让div_bottom在页面底部,距离底部距离为0;修正如上CSS布局; 可以使用以下 CSS 代码来实现所描述的布局: ```CSS html, body { height: 100%; margin: 0; } .div_top { position: absolute; top: 0; left: 0; right: 0; margin-top: 20px; width: 100%; text-align: center; border-radius: 6px; overflow: hidden; box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4); } .div_top img { width: 80%; border-radius: 6px; } .div_bottom { position: absolute; bottom: 0; left: 0; right: 0; margin-bottom: 47px; width: 100%; text-align: center; font-size: 50px; } .div_middle { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 55%; } ``` 具体实现方式如下: 1. 为了让 `div_middle` 占用页面中部的 55% 的空间,可以使用绝对定位,并设置 `top` 和 `bottom` 为 0,`left` 和 `right` 为 0。 2. 为了让 `div_top` 固定在页面顶部,可以使用绝对定位,并设置 `top` 为 0,`left` 和 `right` 为 0。 3. 为了让 `div_bottom` 固定在页面底部,可以使用绝对定位,并设置 `bottom` 为 0,`left` 和 `right` 为 0。 4. 为了让 `div_bottom` 中的文字居中,可以设置 `text-align: center`。 注意:为了让绝对定位的元素能够正确定位,需要为 `html` 和 `body` 设置 `height: 100%`,并去掉默认的 `margin`。 #### 获取每日日期 ```HTML

♪(^∇^*) 一言加载中...
2023年5月23日

``` 使用javascript获取每日日期并更新如上内容;给出示例; 可以使用JavaScript的Date对象来获取当前日期,并使用DOM操作来更新HTML元素的内容。以下是一个示例代码: ```HTML

♪(^∇^*) 一言加载中...

``` 在这个示例中,我们首先在HTML中添加了一个具有唯一ID的span元素,用于显示日期。然后,我们使用JavaScript的Date对象获取当前日期,并将年、月、日分别存储在变量中。最后,我们使用DOM操作获取span元素,并将其innerHTML属性设置为日期字符串。这样,在页面加载时,日期将被更新并显示在页面上。 #### 使用自定义字体 ```html

♪(^∇^*) 一言加载中...

``` 字体显示使用同目录下的`SmileySans-Oblique.ttf`字体文件;修改如上代码; 您可以尝试以下修改,以在CSS中使用`SmileySans-Oblique.ttf`字体文件: ```CSS @font-face { font-family: 'SmileySans-Oblique'; src: url('SmileySans-Oblique.ttf') format('truetype'); } html, body { height: 100%; margin: 0; } .div_top { position: absolute; top: 0; left: 0; right: 0; margin-top: 20px; width: 100%; text-align: center; border-radius: 6px; overflow: hidden; box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4); font-family: 'SmileySans-Oblique', sans-serif; } .div_top img { width: 80%; border-radius: 6px; } .div_bottom { position: absolute; bottom: 0; left: 0; right: 0; margin-bottom: 47px; width: 100%; text-align: center; font-size: 50px; font-family: 'SmileySans-Oblique', sans-serif; } .div_middle { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 55%; font-family: 'SmileySans-Oblique', sans-serif; } ``` 您需要使用`@font-face`规则将字体文件引入CSS中,并在需要的元素中使用`font-family`属性来指定字体。在这个例子中,我在`.div_top`,`.div_bottom`和`.div_middle`中添加了`font-family`属性,以便这些元素使用`SmileySans-Oblique`字体。 #### 添加一言古诗和网易云 [https://hitokoto.cn] ```HTML

:D 获取中...

``` #### CSS布局5 ```HTML 每日Bing图
``` #### 更新php文件 ```HTML 每日Bing图
``` 仅使用php7;页面布局和内容如上,将如上html翻译为php,然后使用imagepng库生成`daily.png`图片,图片尺寸为600x800;图片色彩空间为Grey;每15分钟更新一次图片;给出完整php代码; 以下是完整的PHP代码,将每日Bing图页面保存为daily.png图片,每15分钟更新一次: ```php $interval)) { //获取每日Bing图 $bing_url = 'https://api.dujin.org/bing/1920.php'; $bing_image = file_get_contents($bing_url); //创建图片 $image = imagecreatefromstring($bing_image); //设置图片色彩空间为Grey imagefilter($image, IMG_FILTER_GRAYSCALE); //保存图片 imagepng($image, $image_path); //释放资源 imagedestroy($image); } //输出图片 header('Content-Type: image/png'); readfile($image_path); ``` 将以上代码保存为php文件,上传到服务器,然后在需要显示每日Bing图的页面中引用该php文件即可。例如,将以下HTML代码保存为daily.html文件: ```html 每日Bing图 ``` 然后将上面的PHP代码保存为daily.php文件,并与daily.html文件放在同一目录下。最后访问daily.html页面即可显示每日Bing图,图片每15分钟更新一次。 以下是将HTML页面渲染为图片并保存的PHP代码: ```php setBinDir('/usr/local/bin'); // 创建页面设置对象 $width = 600; $height = 800; $top = 0; $left = 0; $renderType = 'png'; $quality = 100; $timeout = 5000; $viewportSize = "{$width},{$height}"; // 创建页面对象 $request = $client->getMessageFactory()->createCaptureRequest('http://localhost/daily.html', 'GET'); $request->setOutputFile('daily.png'); $request->setViewportSize($width, $height); $request->setCaptureDimensions($width, $height, $top, $left); $request->setRenderType($renderType); $request->setQuality($quality); $request->setTimeout($timeout); $request->setViewportSize($viewportSize); // 发送请求并保存响应内容 $response = $client->getMessageFactory()->createResponse(); $client->send($request, $response); if ($response->getStatus() === 200) { file_put_contents('daily.png', $response->getContent()); } // 每15分钟更新一次图片 sleep(900); header("Refresh:0"); ?> ``` 这段代码使用了PhantomJS库来将HTML页面渲染为图片并保存为`daily.png`。其中,`$width`和`$height`分别指定了图片的宽度和高度,`$top`和`$left`分别指定了图片的起始位置,`$renderType`指定了图片的格式,`$quality`指定了图片的质量,`$timeout`指定了渲染页面的超时时间,`$viewportSize`指定了页面的视口大小。 在代码的最后,使用`sleep(900)`函数来让程序暂停15分钟后再次执行,实现每15分钟更新一次图片的效果。同时,使用`header("Refresh:0")`函数来刷新页面,保证图片能够及时更新。 将HTML页面转换为PHP并生成PNG图像需要使用PHP的GD库。首先,将HTML代码保存为一个字符串,然后使用PHP的DOMDocument类将字符串解析为DOM对象。接着,使用GD库将DOM对象转换为图像。最后,使用imagepng函数将图像保存为PNG文件。 以下是完整的PHP代码: ```php 每日Bing图
'; // 将HTML字符串解析为DOM对象 $dom = new DOMDocument(); $dom->loadHTML($html); // 转换DOM对象为图像 $image = imagecreatefromstring($dom->saveHTML()); // 将图像颜色空间转换为灰度 imagefilter($image, IMG_FILTER_GRAYSCALE); // 保存图像为PNG文件 imagepng($image, 'daily.png'); // 释放内存 imagedestroy($image); // 设置定时器,每15分钟更新一次图像 set_time_limit(0); while (true) { sleep(15 * 60); updateImage(); } ?> ``` 注意:这段代码可能无法直接运行,因为将HTML转换为图像并不是一个简单的任务。你可能需要使用第三方库,如[PhantomJS](http://phantomjs.org/)或[wkhtmltoimage](https://wkhtmltopdf.org/)来实现这个功能。 #### 如何部署PhantomJS库 PhantomJS是一个基于WebKit的无头浏览器,可以用于自动化Web页面的交互和测试。要部署PhantomJS库,可以按照以下步骤操作: 1. 安装必要的依赖项:PhantomJS依赖于一些系统库,例如libfontconfig和libfreetype等。在Linux系统上,可以使用以下命令安装这些依赖项: ``` sudo apt-get install libfontconfig1 libfreetype6 ``` 2. 下载PhantomJS二进制文件:可以从PhantomJS官网(https://phantomjs.org/download.html)下载适用于您的系统的二进制文件。下载后,将其解压到适当的位置。 3. 设置环境变量:将PhantomJS二进制文件所在的目录添加到系统的PATH环境变量中,以便在终端中能够直接运行phantomjs命令。可以在.bashrc或.zshrc文件中添加以下行: ``` export PATH=/path/to/phantomjs/bin:$PATH ``` 4. 测试安装:在终端中运行以下命令,以确保PhantomJS已经正确安装: ``` phantomjs --version ``` 如果一切正常,该命令应该输出PhantomJS的版本号。现在,您已经成功部署了PhantomJS库。 #### 测试图片生成 [https://www.qsbye.cn/infoimg/DailyImg1.php] ```php 每日Bing图
'; // 将HTML字符串解析为DOM对象 $dom = new DOMDocument(); $dom->loadHTML($html); // 转换DOM对象为图像 $image = imagecreatefromstring($dom->saveHTML()); // 将图像颜色空间转换为灰度 imagefilter($image, IMG_FILTER_GRAYSCALE); // 保存图像为PNG文件 imagepng($image, 'daily.png'); // 释放内存 imagedestroy($image); //显示图片 echo "生成的图片"; // 设置定时器,每15分钟更新一次图像 set_time_limit(0); while (true) { sleep(15 * 60); updateImage(); } ?> ``` `daily.png`图片中的布局(div_top,div_middle和div_bottom)和内容(包括图片和文字)使用GD库的画布添加而不是使用html的DOM方式;修改如上代码,给出完整代码; ```php 每日Bing图 '; // 获取Bing每日背景图片 $bingImage = file_get_contents('https://api.dujin.org/bing/1920.php'); // 创建画布 $width = 600; $height = 800; $image = imagecreatetruecolor($width, $height); // 创建颜色 $white = imagecolorallocate($image, 255, 255, 255); $black = imagecolorallocate($image, 0, 0, 0); $gray = imagecolorallocate($image, 128, 128, 128); // 填充背景 imagefill($image, 0, 0, $white); // 添加顶部信息 $text = "♪(^∇^*) 一言加载中..."; $font = 'SmileySans-Oblique.ttf'; $fontSize = 20; $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0]; $textX = ($width - $textWidth) / 2; $textY = 50; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text); // 添加日期 $date = date('Y年m月d日'); $fontSize = 16; $textWidth = imagettfbbox($fontSize, 0, $font, $date)[2] - imagettfbbox($fontSize, 0, $font, $date)[0]; $textX = ($width - $textWidth) / 2; $textY = 80; imagettftext($image, $fontSize, 0, $textX, $textY, $gray, $font, $date); // 添加一言 $hitokoto = json_decode(file_get_contents('https://v1.hitokoto.cn'), true)['hitokoto']; $fontSize = 20; $textWidth = imagettfbbox($fontSize, 0, $font, $hitokoto)[2] - imagettfbbox($fontSize, 0, $font, $hitokoto)[0]; $textX = ($width - $textWidth) / 2; $textY = 120; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $hitokoto); // 添加中间留空信息区 $text = ""; $fontSize = 20; $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0]; $textHeight = imagettfbbox($fontSize, 0, $font, $text)[3] - imagettfbbox($fontSize, 0, $font, $text)[1]; $textX = ($width - $textWidth) / 2; $textY = ($height - $textHeight) / 2; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text); // 添加底部Bing每日背景图片 $bingImage = imagecreatefromstring($bingImage); $bingImageWidth = imagesx($bingImage); $bingImageHeight = imagesy($bingImage); $bingImageX = ($width - $bingImageWidth) / 2; $bingImageY = $height - $bingImageHeight - 100; imagecopyresampled($image, $bingImage, $bingImageX, $bingImageY, 0, 0, $bingImageWidth, $bingImageHeight, $bingImageWidth, $bingImageHeight); // 添加底部信息 $text = "每日Bing图"; $fontSize = 50; $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0]; $textX = ($width - $textWidth) / 2; $textY = $height - 50; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text); // 将图像颜色空间转换为灰度 imagefilter($image, IMG_FILTER_GRAYSCALE); // 保存图像为PNG文件 imagepng($image, 'daily.png'); // 释放内存 imagedestroy($image); } // 更新一次图像 updateImage(); // 显示图片 echo "生成的图片"; // 设置定时器,每15分钟更新一次图像 set_time_limit(0); while (true) { sleep(15 * 60); updateImage(); } ?> ``` #### 更改php画布的布局 ```CSS @font-face { font-family: 'SmileySans-Oblique'; src: url('SmileySans-Oblique.ttf') format('truetype'); } html, body { height: 100%; margin: 0; } .div_top { position: absolute; top: 0; left: 0; right: 0; margin-top: 20px; width: 100%; text-align: center; overflow: hidden; box-shadow: 0px 3px 20px -3px rgba(0, 0, 0, 0.4); font-family: 'SmileySans-Oblique', sans-serif; } .div_bottom img { width: 50%; height:30%; border-radius: 6px; } .div_bottom { position: absolute; bottom: 0; left: 0; right: 0; margin-bottom: 47px; width: 100%; text-align: center; font-size: 50px; font-family: 'SmileySans-Oblique', sans-serif; } .div_middle { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 55%; font-family: 'SmileySans-Oblique', sans-serif; } ``` 使用如上CSS布局修正如下php画布的元素布局; ```php // 获取Bing每日背景图片 $bingImage = file_get_contents('https://api.dujin.org/bing/1920.php'); // 创建画布 $width = 600; $height = 800; $image = imagecreatetruecolor($width, $height); // 创建颜色 $white = imagecolorallocate($image, 255, 255, 255); $black = imagecolorallocate($image, 0, 0, 0); $gray = imagecolorallocate($image, 128, 128, 128); // 填充背景 imagefill($image, 0, 0, $white); // 添加顶部信息 $text = "♪(^∇^*) 一言加载中..."; $font = 'SmileySans-Oblique.ttf'; $fontSize = 20; $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0]; $textX = ($width - $textWidth) / 2; $textY = 50; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text); // 添加日期 $date = date('Y年m月d日'); $fontSize = 16; $textWidth = imagettfbbox($fontSize, 0, $font, $date)[2] - imagettfbbox($fontSize, 0, $font, $date)[0]; $textX = ($width - $textWidth) / 2; $textY = 80; imagettftext($image, $fontSize, 0, $textX, $textY, $gray, $font, $date); // 添加一言 $hitokoto = json_decode(file_get_contents('https://v1.hitokoto.cn'), true)['hitokoto']; $fontSize = 20; $textWidth = imagettfbbox($fontSize, 0, $font, $hitokoto)[2] - imagettfbbox($fontSize, 0, $font, $hitokoto)[0]; $textX = ($width - $textWidth) / 2; $textY = 120; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $hitokoto); // 添加中间留空信息区 $text = ""; $fontSize = 20; $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0]; $textHeight = imagettfbbox($fontSize, 0, $font, $text)[3] - imagettfbbox($fontSize, 0, $font, $text)[1]; $textX = ($width - $textWidth) / 2; $textY = ($height - $textHeight) / 2; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text); // 添加底部Bing每日背景图片 $bingImage = imagecreatefromstring($bingImage); $bingImageWidth = imagesx($bingImage); $bingImageHeight = imagesy($bingImage); $bingImageX = ($width - $bingImageWidth) / 2; $bingImageY = $height - $bingImageHeight - 100; imagecopyresampled($image, $bingImage, $bingImageX, $bingImageY, 0, 0, $bingImageWidth, $bingImageHeight, $bingImageWidth, $bingImageHeight); // 添加底部信息 $text = "每日Bing图"; $fontSize = 50; $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0]; $textX = ($width - $textWidth) / 2; $textY = $height - 50; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text); ``` 结果: ```php // 获取Bing每日背景图片 $bingImage = file_get_contents('https://api.dujin.org/bing/1920.php'); // 创建画布 $width = 600; $height = 800; $image = imagecreatetruecolor($width, $height); // 创建颜色 $white = imagecolorallocate($image, 255, 255, 255); $black = imagecolorallocate($image, 0, 0, 0); $gray = imagecolorallocate($image, 128, 128, 128); // 填充背景 imagefill($image, 0, 0, $white); // 添加顶部信息 $text = "♪(^∇^*) 一言加载中..."; $font = 'SmileySans-Oblique.ttf'; $fontSize = 20; $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0]; $textX = ($width - $textWidth) / 2; $textY = 50; $top = "
$text
$date
$hitokoto
"; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text); // 添加日期 $date = date('Y年m月d日'); $fontSize = 16; $textWidth = imagettfbbox($fontSize, 0, $font, $date)[2] - imagettfbbox($fontSize, 0, $font, $date)[0]; $textX = ($width - $textWidth) / 2; $textY = 80; imagettftext($image, $fontSize, 0, $textX, $textY, $gray, $font, $date); // 添加一言 $hitokoto = json_decode(file_get_contents('https://v1.hitokoto.cn'), true)['hitokoto']; $fontSize = 20; $textWidth = imagettfbbox($fontSize, 0, $font, $hitokoto)[2] - imagettfbbox($fontSize, 0, $font, $hitokoto)[0]; $textX = ($width - $textWidth) / 2; $textY = 120; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $hitokoto); // 添加中间留空信息区 $middle = "
"; // 添加底部Bing每日背景图片 $bingImage = imagecreatefromstring($bingImage); $bingImageWidth = imagesx($bingImage); $bingImageHeight = imagesy($bingImage); $bingImageX = ($width - $bingImageWidth) / 2; $bingImageY = $height - $bingImageHeight - 100; imagecopyresampled($image, $bingImage, $bingImageX, $bingImageY, 0, 0, $bingImageWidth, $bingImageHeight, $bingImageWidth, $bingImageHeight); // 添加底部信息 $text = "每日Bing图"; $fontSize = 50; $textWidth = imagettfbbox($fontSize, 0, $font, $text)[2] - imagettfbbox($fontSize, 0, $font, $text)[0]; $textX = ($width - $textWidth) / 2; $textY = $height - 50; $bottom = "
$text
"; imagettftext($image, $fontSize, 0, $textX, $textY, $black, $font, $text); // 输出HTML $html = "Bing每日壁纸
$top$middle$bottom
"; echo $html; ``` #### 更改rust订阅地址,添加日志功能 ```rust use std::process::Command; use image::{GenericImageView, ImageFormat, GrayImage, RgbImage}; fn main() { // Download the image from the URL using wget command let url = "https://www.qsbye.cn/infoimg/daily.png"; let output = Command::new("wget") .arg("-O") .arg("/mnt/us/img1_old.png") .arg(url) .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); // Convert the image to bitmap format and save as png color space let img = image::open("/mnt/us/img1_old.png").unwrap().into_luma8(); let png = img.save_with_format("/mnt/us/img1.png", ImageFormat::Png).unwrap(); println!("Convert the image ok!");//log // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-f") .arg("-g") .arg("/mnt/us/img1.png") .output() .unwrap(); println!("{}", String::from_utf8_lossy(&output.stdout)); println!("display ok!"); } ``` 添加log日志功能,在同目录下的`runtime.log`文件追加`println`函数的结果,格式为`时间 信息`,一行一条日志信息,如果没有`runtime.log`文件就自动创建一个;修改如上代码; ```rust use std::process::Command; use image::{GenericImageView, ImageFormat, GrayImage, RgbImage}; use std::fs::{File, OpenOptions}; use std::io::Write; use chrono::prelude::*; fn main() { // Create or open the runtime.log file let mut file = OpenOptions::new() .create(true) .append(true) .open("/mnt/us/runtime.log") .unwrap(); // Download the image from the URL using wget command let url = "https://www.qsbye.cn/infoimg/daily.png"; let output = Command::new("wget") .arg("-O") .arg("/mnt/us/img1_old.png") .arg(url) .output() .unwrap(); let output_str = String::from_utf8_lossy(&output.stdout); println!("{}", output_str); writeln!(file, "{} Download image from URL: {}", Local::now(), url).unwrap(); writeln!(file, "{} {}", Local::now(), output_str).unwrap(); // Convert the image to bitmap format and save as png color space let img = image::open("/mnt/us/img1_old.png").unwrap().into_luma8(); let png = img.save_with_format("/mnt/us/img1.png", ImageFormat::Png).unwrap(); writeln!(file, "{} Convert the image to png format", Local::now()).unwrap(); // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-f") .arg("-g") .arg("/mnt/us/img1.png") .output() .unwrap(); let output_str = String::from_utf8_lossy(&output.stdout); println!("{}", output_str); writeln!(file, "{} Display the bitmap on the Kindle device", Local::now()).unwrap(); writeln!(file, "{} {}", Local::now(), output_str).unwrap(); } ``` 添加,如果代码执行遇到任何错误,就显示`/mnt/us/extensions/touch/error.png`图片,并输出错误信息到日志文件中;修改如上代码; 这个修改后的代码在原有代码的基础上,使用了`chrono`库来获取当前时间,并在每个日志信息前面添加了时间戳,以方便查看日志信息的时间顺序。同时,使用`std::fs::OpenOptions`来创建或打开`runtime.log`文件,并使用`writeln!`宏将日志信息写入文件中。 可以在代码中添加一个错误处理的块来实现这个功能。具体来说,您可以在每个可能会出错的步骤后添加一个 `match` 块,用于检查是否有错误发生。如果有错误,您可以使用 `std::fs::copy()` 函数将错误图片复制到 Kindle 设备上,然后将错误信息写入日志文件。 以下是修改后的代码: ```rust use std::process::Command; use image::{GenericImageView, ImageFormat, GrayImage, RgbImage}; use std::fs::{File, OpenOptions, copy}; use std::io::Write; use chrono::prelude::*; fn main() { // Create or open the runtime.log file let mut file = OpenOptions::new() .create(true) .append(true) .open("/mnt/us/runtime.log") .unwrap(); // Download the image from the URL using wget command let url = "https://www.qsbye.cn/infoimg/daily.png"; let output = Command::new("wget") .arg("-O") .arg("/mnt/us/img1_old.png") .arg(url) .output(); match output { Ok(output) => { let output_str = String::from_utf8_lossy(&output.stdout); println!("{}", output_str); writeln!(file, "{} Download image from URL: {}", Local::now(), url).unwrap(); writeln!(file, "{} {}", Local::now(), output_str).unwrap(); }, Err(e) => { println!("Error: {:?}", e); copy("/mnt/us/extensions/touch/error.png", "/mnt/us/img1.png").unwrap(); writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap(); return; } } // Convert the image to bitmap format and save as png color space let img = image::open("/mnt/us/img1_old.png").unwrap().into_luma8(); let png = img.save_with_format("/mnt/us/img1.png", ImageFormat::Png); match png { Ok(_) => { writeln!(file, "{} Convert the image to png format", Local::now()).unwrap(); }, Err(e) => { println!("Error: {:?}", e); copy("/mnt/us/extensions/touch/error.png", "/mnt/us/img1.png").unwrap(); writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap(); return; } } // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-f") .arg("-g") .arg("/mnt/us/img1.png") .output(); match output { Ok(output) => { let output_str = String::from_utf8_lossy(&output.stdout); println!("{}", output_str); writeln!(file, "{} Display the bitmap on the Kindle device", Local::now()).unwrap(); writeln!(file, "{} {}", Local::now(), output_str).unwrap(); }, Err(e) => { println!("Error: {:?}", e); copy("/mnt/us/extensions/touch/error.png", "/mnt/us/img1.png").unwrap(); writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap(); return; } } } ``` 这样,如果在代码执行过程中出现任何错误,它将复制错误图片到 `/mnt/us/img1.png` 并将错误信息写入日志文件。 #### 添加文件权限 ```SHELL #复制到/usr/sbin目录并重命名 cp /mnt/us/extensions/touch/hello_weather_three /usr/sbin & mv /usr/sbin/hello_weather_three /usr/sbin/hello #可执行文件添加权限777 ``` 完善以上SHELL命令; 以下是完善后的SHELL命令: ```SHELL # 复制到/usr/sbin目录并重命名 cp /mnt/us/extensions/touch/hello_weather_three /usr/sbin/hello # 添加可执行权限 chmod 777 /usr/sbin/hello ``` 这将把 `/mnt/us/extensions/touch/hello_weather_three` 复制到 `/usr/sbin/hello` 并为文件添加可执行权限,使其可以在 Kindle 设备上运行。 #### 完善脚本 **touch.sh** ```SHELL #!/bin/sh # 变量 export COUNT=1 #log touch /mnt/us/extensions/touch/$COUNT.txt && COUNT=$((COUNT+1)) # 超级权限 su #禁止休眠 lipc-set-prop -i com.lab126.powerd preventScreenSaver 1 #log touch /mnt/us/extensions/touch/$COUNT.txt && COUNT=$((COUNT+1)) # 复制到/usr/sbin目录并重命名 cp /mnt/us/extensions/touch/hello_weather_three /usr/sbin/hello # 添加可执行权限 chmod 777 /usr/sbin/hello #显示图片 eips -c hello #开启wifi lipc-set-prop com.lab126.cmd wirelessEnable 1 #log touch /mnt/us/extensions/touch/$COUNT.txt && COUNT=$((COUNT+1)) #延时5s sleep 5 #延时1小时 sleep 1h #循环获取图片 nohup /mnt/us/extensions/touch/touch.sh & #log touch /mnt/us/extensions/touch/$COUNT.txt && COUNT=$((COUNT+1)) ``` #### SHELL的环境变量自增 ```SHELL export COUNT=1 echo $COUNT && COUNT=COUNT+1 ``` 修正以上错误,实现COUNT自增 在Shell脚本中,要让变量自增,需要使用特殊的语法来实现。以下是修正后的代码: ```SHELL export COUNT=1 echo $COUNT && COUNT=$((COUNT+1)) ``` 在第二行中,我们使用了$(( ))语法来对COUNT变量进行自增操作。这个语法会将COUNT变量的值加1,并将结果赋值给COUNT变量。注意,在这个语法中,操作符和操作数之间需要有空格。 #### SHELL删除所有1.txt文件 删除`/mnt/us/extensions/touch`目录下所有txt文件,给出SHELL命令; 可以使用以下Shell命令来删除`/mnt/us/extensions/touch`目录下所有txt文件: ```SHELL rm /mnt/us/extensions/touch/*.txt ``` 该命令使用`rm`命令来删除文件,`/mnt/us/extensions/touch/*.txt`是通配符表达式,表示删除`/mnt/us/extensions/touch`目录下所有扩展名为`.txt`的文件。请注意,这个命令将不会删除目录,只会删除目录下的文件。 #### SHELL脚本中无法运行777权限的可执行文件 ```SHELL # 复制并重命名 cp /mnt/us/extensions/touch/hello_weather_three /mnt/us/extensions/touch/hello # 添加可执行权限 chmod 777 hello #显示图片 eips -c hello ``` kindle的SHELL脚本中无法运行777权限的可执行文件,怎么办? 如果您的kindle的SHELL脚本中无法运行777权限的可执行文件,可能是因为kindle的系统默认不允许运行这种文件。您可以尝试使用以下方法解决这个问题: 1. 尝试更改文件的权限为755,这是一个更安全的权限设置,也许可以被kindle系统允许执行。您可以使用以下命令更改文件的权限: ```SHELL chmod 755 /mnt/us/extensions/touch/hello ``` 2. 您可以尝试在kindle的系统中安装一个类似busybox或者其他的工具箱,这些工具箱可以提供更多的命令和功能,也许可以解决这个问题。 3. 如果以上两种方法都无法解决问题,您可以尝试使用一个更简单的方法,将可执行文件的权限设置为666,这样可以让kindle的系统允许执行。但是请注意,这种方法可能会存在一些安全隐患,因为其他用户也可以访问和修改这个文件。您可以使用以下命令更改文件的权限: ```SHELL chmod 666 /mnt/us/extensions/touch/hello ``` 希望这些方法可以帮助您解决问题。 #### 调用kterm执行SHELL命令 如何调用`/mnt/us/extensions/kterm/bin/kterm`可执行文件执行SHELL命令`eips -c`? 要调用`/mnt/us/extensions/kterm/bin/kterm`可执行文件来执行SHELL命令`eips -c`,您可以使用以下命令: ```SHELL /mnt/us/extensions/kterm/bin/kterm -e "eips -c" ``` 这个命令的含义是:使用`/mnt/us/extensions/kterm/bin/kterm`可执行文件打开一个新的终端窗口,并在该终端窗口中执行`eips -c`命令。 其中,`-e`选项用于指定要在新终端窗口中执行的命令。请注意,这个命令需要在kindle的SHELL脚本中执行,因为只有在kindle的SHELL环境中,才能调用`/mnt/us/extensions/kterm/bin/kterm`可执行文件。 #### 防止SHELL语句歧义 ```SHELL nohup /mnt/us/extensions/kterm/bin/kterm -e hello & ``` 在语句中添加分隔符防止语句歧义 如果您想在以下语句中添加分隔符防止语句歧义: ```SHELL nohup /mnt/us/extensions/kterm/bin/kterm -e hello & ``` 可以使用分号(;)或者换行符来分隔多个命令,例如: ```SHELL nohup /mnt/us/extensions/kterm/bin/kterm -e hello ; & ``` 或者: ```SHELL nohup /mnt/us/extensions/kterm/bin/kterm -e hello & ``` 这样,您就可以在一个语句中执行多个命令,并且不会出现歧义。请注意,在第二个示例中,`&`符号需要单独放在一行,因为它需要作为一个单独的命令来执行。 #### SHELL命令永不退出 ```SHELL /mnt/us/extensions/kterm/bin/kterm -e 'hello' ``` 在执行`hello`运行空语句,永不退出,给出修改; 可以使用bash的后台运行和`wait`命令来实现在执行`hello`命令后等待其结束并退出。 以下是修改后的命令: ```SHELL /mnt/us/extensions/kterm/bin/kterm -e 'hello & wait' ``` 这个命令中,`&`符号将`hello`命令放到后台运行,然后`wait`命令等待其结束。这样,当`hello`命令执行完毕后,`wait`命令也就结束了,整个命令也就退出了。 #### SHELL空语句防止进入docker容器后闪退 在进入Docker容器后,如果执行空语句会导致容器闪退,这可能是因为Shell会立即退出,从而导致容器停止运行。 为了防止这种情况发生,可以在Shell中添加一个无限循环来保持Shell运行。以下是一个示例命令: ```SHELL /bin/bash -c "while true; do sleep 3600; done" ``` 这个命令将在Shell中启动一个无限循环,每个循环都会让Shell休眠3600秒(1小时),然后重新开始循环。这样,即使没有其他命令在Shell中运行,Shell也会保持运行状态,从而防止容器闪退。 #### kterm的命令参数 ```SHELL /mnt/us/extensions/kterm/bin/kterm -e /mnt/us/extensions/kterm/bin/kterm -c ``` kindle中如上命令有什么作用?如何防止kterm执行完命令后退出? 这两个命令都是在Kindle设备上使用的,用于启动KTerm终端模拟器。具体作用如下: - `/mnt/us/extensions/kterm/bin/kterm -e`:启动KTerm并执行一个Shell命令,这里没有指定具体的命令,所以KTerm会启动后立即退出。 - `/mnt/us/extensions/kterm/bin/kterm -c`:启动KTerm并进入命令行模式,等待用户在终端中输入命令。 为了防止KTerm执行完命令后退出,可以使用`/bin/bash`命令来启动一个新的Bash Shell,并在其中执行命令。例如,要在KTerm中执行`ls`命令并等待用户输入命令,可以使用以下命令: ```SHELL /mnt/us/extensions/kterm/bin/kterm -e "/bin/bash -c 'ls; exec /bin/bash'" ``` 这个命令将在KTerm中启动一个Bash Shell,执行`ls`命令,然后进入一个无限循环,等待用户输入命令。`exec /bin/bash`命令将替换当前Shell进程,使得用户输入的所有命令都在同一个Shell中执行,从而避免了KTerm在执行完命令后退出的问题。 #### rust程序改为定时执行,后台运行,永不退出 ```rust use std::process::Command; use image::{GenericImageView, ImageFormat, GrayImage, RgbImage}; use std::fs::{File, OpenOptions, copy}; use std::io::Write; use chrono::prelude::*; fn main() { // Create or open the runtime.log file let mut file = OpenOptions::new() .create(true) .append(true) .open("/mnt/us/runtime.log") .unwrap(); // Download the image from the URL using wget command let url = "https://www.qsbye.cn/infoimg/daily.png"; let output = Command::new("wget") .arg("-O") .arg("/mnt/us/extensions/touch/img1_old.png") .arg(url) .output(); match output { Ok(output) => { let output_str = String::from_utf8_lossy(&output.stdout); println!("{}", output_str); writeln!(file, "{} Download image from URL: {}", Local::now(), url).unwrap(); writeln!(file, "{} {}", Local::now(), output_str).unwrap(); }, Err(e) => { println!("Error: {:?}", e); copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap(); writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap(); return; } } // Convert the image to bitmap format and save as png color space let img = image::open("/mnt/us/extensions/touch/img1_old.png").unwrap().into_luma8(); let png = img.save_with_format("/mnt/us/extensions/touch/img1.png", ImageFormat::Png); match png { Ok(_) => { writeln!(file, "{} Convert the image to png format", Local::now()).unwrap(); }, Err(e) => { println!("Error: {:?}", e); copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap(); writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap(); return; } } // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-f") .arg("-g") .arg("/mnt/us/extensions/touch/img1.png") .output(); match output { Ok(output) => { let output_str = String::from_utf8_lossy(&output.stdout); println!("{}", output_str); writeln!(file, "{} Display the bitmap on the Kindle device", Local::now()).unwrap(); writeln!(file, "{} {}", Local::now(), output_str).unwrap(); }, Err(e) => { println!("Error: {:?}", e); copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap(); writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap(); return; } } } ``` 添加定时获取图片并显示,每隔1小时就运行一次,程序永不退出;给出完整代码; 好的,以下是添加定时获取图片并显示的完整代码: ```rust use std::process::Command; use image::{GenericImageView, ImageFormat, GrayImage, RgbImage}; use std::fs::{File, OpenOptions, copy}; use std::io::Write; use chrono::prelude::*; use std::thread::sleep; use std::time::Duration; fn main() { // Create or open the runtime.log file let mut file = OpenOptions::new() .create(true) .append(true) .open("/mnt/us/runtime.log") .unwrap(); loop { // Download the image from the URL using wget command let url = "https://www.qsbye.cn/infoimg/daily.png"; let output = Command::new("wget") .arg("-O") .arg("/mnt/us/extensions/touch/img1_old.png") .arg(url) .output(); match output { Ok(output) => { let output_str = String::from_utf8_lossy(&output.stdout); println!("{}", output_str); writeln!(file, "{} Download image from URL: {}", Local::now(), url).unwrap(); writeln!(file, "{} {}", Local::now(), output_str).unwrap(); }, Err(e) => { println!("Error: {:?}", e); copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap(); writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap(); continue; } } // Convert the image to bitmap format and save as png color space let img = image::open("/mnt/us/extensions/touch/img1_old.png").unwrap().into_luma8(); let png = img.save_with_format("/mnt/us/extensions/touch/img1.png", ImageFormat::Png); match png { Ok(_) => { writeln!(file, "{} Convert the image to png format", Local::now()).unwrap(); }, Err(e) => { println!("Error: {:?}", e); copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap(); writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap(); continue; } } // Display the bitmap on the Kindle device let output = Command::new("eips") .arg("-f") .arg("-g") .arg("/mnt/us/extensions/touch/img1.png") .output(); match output { Ok(output) => { let output_str = String::from_utf8_lossy(&output.stdout); println!("{}", output_str); writeln!(file, "{} Display the bitmap on the Kindle device", Local::now()).unwrap(); writeln!(file, "{} {}", Local::now(), output_str).unwrap(); }, Err(e) => { println!("Error: {:?}", e); copy("/mnt/us/extensions/touch/error.png", "/mnt/us/extensions/touch/img1.png").unwrap(); writeln!(file, "{} Error: {:?}", Local::now(), e).unwrap(); continue; } } // Wait for an hour before running again sleep(Duration::from_secs(3600)); } } ``` 这个程序会不断循环运行,每隔1小时就会获取一次图片并显示在Kindle设备上。如果获取或显示图片的过程中出现错误,程序会记录错误信息并跳过当前循环,等待下一个循环重新尝试获取和显示图片。 程序没有闪退了. #### 复制文件夹 ```SHELL # 检查/mnt/us/extensions是否有kterm目录,否则复制/mnt/us/extensions/touch/kterm目录过去 ``` 完善如上命令 可以使用以下命令来检查/mnt/us/extensions是否有kterm目录,如果没有,则复制/mnt/us/extensions/touch/kterm目录到该目录下: ```SHELL if [ ! -d "/mnt/us/extensions/kterm" ]; then cp -r /mnt/us/extensions/touch/kterm /mnt/us/extensions/; fi ``` 该命令首先使用条件语句if来检查/mnt/us/extensions是否有kterm目录,如果没有,则执行复制命令cp将/mnt/us/extensions/touch/kterm目录复制到/mnt/us/extensions目录下。其中,-r选项用于递归复制整个目录。 #### 随机图片接口(图片画报) [https://www.dujin.org/12142.html] 之前缙哥哥已经分享过《5种方法获取Bing每日图片 附送高清API接口及网站背景调用》,但是 Bing 这边的图片是一天一更新,有时候缙哥哥想让小伙伴每次访问都能看到不同的背景(图片),并且呢,图片不想放自己的服务器上,毕竟很占宽带,用CDN又费钱…… 所以,今天就在网上找了找,弄了个随机图片接口,接口地址为: ```SHELL https://api.dujin.org/pic/ https://api.dujin.org/pic/fengjing https://api.dujin.org/pic/ghibli https://api.dujin.org/pic/ghibli/ceqt https://api.dujin.org/pic/ghibli/qfl https://api.dujin.org/pic/ghibli/alad https://api.dujin.org/pic/ghibli/qyqx https://api.dujin.org/pic/ghibli/hyjwy https://api.dujin.org/pic/ghibli/ydcb https://api.dujin.org/pic/ghibli/stj ``` 插入wordpress当登录页背景 将下面代码放入主题文件夹中的functions文件即可 ```JAVASCRIPT /** * 缙哥哥自定义必应壁纸为登录页面背景 * https://www.dujin.org/12142.html */ function custom_login_head(){ $imgurl = 'https://api.dujin.org/pic/'; if($imgurl){ echo' '; }} add_action('login_head', 'custom_login_head'); add_filter('login_headerurl', create_function(false,"return get_bloginfo('url');")); add_filter('login_headertitle', create_function(false,"return get_bloginfo('name');")); ``` #### 制作Keynote演示文档用于视频制作 大纲: - 以往成果 - 此次修改相比上一次的改进 - 简述过程 - 简述效果 #### 视频简介🎉 使用Rust语言重写在Kindle上定时显示图片的插件. 开源地址👏:[https://gitee.com/qsbye/kindle-plugin-touch],hello_weather_three目录, 视频配套文档🌈:[https://www.cnblogs.com/qsbye/p/17420780.html]. #### 视频稿 B站的小伙伴们,大家好 时隔一年,我用Rust语言重写了在Kindle上显示图片的插件. 以往成果:用Shell脚本调用`eips`命令显示图片,不足:图片显示不完全,图片尺寸与屏幕尺寸相同,但是显示时被拉伸. 改进:使用内存安全、带有包管理器的Rust语言重写插件,将png图片的颜色空间转换为`Grey`灰度图解决了图片显示不完全的问题;添加Bing每日一图;添加日志功能. 过程: 思考过程简述:使用Rust语言,使用Musl C库,使用Linux作为目标平台,从指定网址获取600x800大小的png图片,转换为Grey颜色空间后调用`eips`命令显示,如果获取图片失败,显示`error.png`图片,程序永不退出,间隔5分钟从头执行一次.所以只需要服务端配合生成600x800大小的图片就可以实现图片的自动更新. 效果: 点击`touch`按钮后屏幕清空,然后通过`kterm`执行`bash`然后执行自制的程序`hello`,屏幕显示图片. #### 使用ffmpeg导出视频的音频 使用ffmpeg导出视频的音频的命令 使用ffmpeg导出视频的音频可以使用以下命令: ```SHELL ffmpeg -i "/Users/workspace/Desktop/projects/kindle-plugin-touch/files/2023-05-22 16-51-40.mkv" -vn -acodec libmp3lame "/Users/workspace/Desktop/projects/kindle-plugin-touch/files/2023-05-22 16-51-40.mp3" ``` 其中,`-i`选项指定输入视频文件名,`-vn`选项表示不需要视频流,只需要音频流,`-acodec copy`选项表示将音频流复制到输出文件中,而不需要重新编码,最后指定输出音频文件名为`output_audio.aac`。 请注意,输出音频文件的格式可以根据需要进行更改。如果需要将音频编码为其他格式,可以使用相应的编解码器。例如,要将音频编码为MP3格式,可以使用`-acodec libmp3lame`选项。 #### 变音器 将mp3音频变声为大叔声音的Shell命令 要将mp3音频变声为大叔声音,可以使用SoX工具和Ffmpeg组合完成。下面是一个Shell命令示例: ```SHELL brew install sox sox "/Users/workspace/Desktop/projects/kindle-plugin-touch/files/2023-05-22 16-51-40.mp3" -C 192 -p pad 0 1 | sox - -C 192 "/Users/workspace/Desktop/projects/kindle-plugin-touch/files/2023-05-22 16-51-40-dashu.mp3" pitch -600 ``` 这个命令将输入文件`input.mp3`读取,使用SoX工具将其转换为raw音频数据,并将其输出到stdout。然后,该命令将raw音频数据传递给第二个SoX命令,该命令将音频数据处理为大叔声音,并将其编码为mp3格式,最终输出到`output.mp3`文件中。 在这个命令中,`-C 192`选项指定输出文件的比特率为192kbps,`pad 0 1`选项用于添加一个额外的空白样本以避免SoX在处理文件时出现问题。`pitch -600`选项用于将音频音高降低600个单位,从而使其听起来更像大叔声音。 请注意,SoX工具和Ffmpeg需要事先安装才能使用。 #### 更改为随机图片 [https://api.dujin.org/pic/fengjing] 测试: [https://www.qsbye.cn/infoimg/DailyImg1.php] ### 第八天:2023年6月2日 主要内容:添加网址;安装filebrowser; #### 优质RSS订阅源 [https://github.com/weekend-project-space/top-rss-list] 1. [https://www.zhihu.com/rss] 2. [https://www.ruanyifeng.com/blog/atom.xml] 3. [http://feeds.feedburner.com/ruanyifeng] 4. [https://sspai.com/feed] 5. [https://rsshub.app/meituan/tech/home] 6. [https://v2ex.com/index.xml] 7. [http://coolshell.cn/feed] 8. [https://www.ifanr.com/feed] 9. [https://rsshub.app/zhihu/hotlist] 10. [https://plink.anyfeeder.com/zaobao/realtime/china] 11. [https://rsshub.app/infzm/2] 12. [https://www.gcores.com/rss] 13. [https://rsshub.app/zhihu/daily] 14. [http://feed.appinn.com/] 15. [https://rsshub.app/juejin/category/frontend] 16. [https://rsshub.app/coolapk/tuwen-xinxian] 17. [https://wangyurui.com/feed.xml] 18. [http://feed.iplaysoft.com/] 19. [https://rss.huxiu.com/] 20. [https://rsshub.app/juejin/posts/1838039172387262] 21. [https://rsshub.app/oschina/news/project] 22. [http://feeds.feedburner.com/zhihu-daily] 23. [http://node2.feed43.com/mryw.xml] 24. [https://www.ithome.com/rss/] 25. [https://rsshub.app/zaker/focusread] 26. [https://www.36kr.com/feed] 27. [https://tech.youzan.com/rss/] 28. [https://plink.anyfeeder.com/idaily/today] 29. [http://hellogithub.com/rss] 30. [https://plink.anyfeeder.com/chinadaily/dual] 31. [https://blogs.360.net/rss.html] 32. [https://plink.anyfeeder.com/weixin/dukuxiaobao] 33. [https://wanqu.co/feed/] 34. [https://plink.anyfeeder.com/weixin/qnwzwx] 35. [https://plink.anyfeeder.com/newscn/whxw] 36. [https://plink.anyfeeder.com/infoq/recommend] 37. [https://plink.anyfeeder.com/weixin/lifeweek] 38. [https://plink.anyfeeder.com/wsj/cn] 39. [https://plink.anyfeeder.com/weixin/cctvnewscenter] 40. [https://plink.anyfeeder.com/guangmingribao] 41. [https://plink.anyfeeder.com/weixin/banyuetan-weixin] 42. [https://plink.anyfeeder.com/people-daily] 43. [https://plink.anyfeeder.com/bjnews] 44. [https://plink.anyfeeder.com/thepaper] 45. [https://plink.anyfeeder.com/weixin/phoenixweekly] 46. [https://plink.anyfeeder.com/weixin/CSDNnews] 47. [https://plink.anyfeeder.com/weixin/MSRAsia] 48. [https://plink.anyfeeder.com/weixin/AI_era] 49. [https://plink.anyfeeder.com/weixin/vistaweek] 50. [https://cn.wsj.com/zh-hans/rss] 51. [https://plink.anyfeeder.com/weixin/ScientificAmerican] 52. [https://plink.anyfeeder.com/weixin/hqsbwx] 53. [https://plink.anyfeeder.com/weixin/diqiuzhishiju] 54. [https://plink.anyfeeder.com/linux.cn] 55. [https://plink.anyfeeder.com/weixin/dili360] 56. [https://plink.anyfeeder.com/cnbeta] 57. [https://rsshub.app/bing] 58. [https://plink.anyfeeder.com/weixin/xueqiujinghua] #### KUAL源码 [https://github.com/bfabiszewski/kterm] [https://www.mobileread.com/forums/showthread.php?t=225030] This is a simple GTK+ terminal emulator with embedded virtual keyboard. It is based on VteTerminal library. Some initial settings may be defined in kterm.conf file. Keyboard layouts are defined in xml config files. The keyboard config files follow the same rules as matchbox keyboard configs (backward compatible with kterm 1.x which used embedded matchbox keyboard). Kterm has been developed for Kindle Touch. It is reported to also work on Paperwhites. Generally it should work on any platform which supports GTK+, either version 2 or 3. On Kindle menu pops up on two fingers tap in the terminal window. On other devices on right button mouse click. #### kindle插件的其他启动方式 如果使用KUAL 无法启动,可以将 bin 目录下的 RUNME.sh 复制到Kindle根目录,在搜索栏 输入 ;log runme 即可运行天气插件! #### 安装filebrowser [https://bookfere.com/post/823.html/comment-page-2#comment-28241] [https://github.com/ngxson/hobby-kindle-http-file-server] 登录验证的默认用户名和密码均为“admin”(可登录之后在设置页面自行修改), 插件启动后默认会显示 Kindle 的根目录(即 Kindle 系统中的绝对路径 /mnt/us)。你可以修改插件文件夹中的 start.sh 文件,将最后一行中的路径 /mnt/us 改成你想要的路径,比如存放电子书的路径 /mnt/us/documents,或 Kindle 系统的根目录 /。注意,修改完成后需要重新开启插件才能生效。 扫描局域网ip地址: ```SHELL #扫描局域网ip sudo nmap -sP -PI -PT 192.168.0.0/24 ``` 结果: ```SHELL Nmap scan report for 192.168.0.100 Host is up (0.095s latency). MAC Address: 00:BB:3A:9E:69:7D (Amazon Technologies) ``` - 访问:[192.168.0.100:80] - 新建用户:qsbye(qsbye) - 新建用户:408(408)