1 Star 0 Fork 0

岑剑伟 / rachal

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

Rachel

ESP32 迷你游戏机,手感很好

很复古,什么也做不到(悲)

项目目的:卖萌

元件都是 0603 哦,铁板烧就能焊

视频介绍:https://www.bilibili.com/video/BV1Ga4y1f7d3/

硬件开源:https://oshwhub.com/eedadada/mason

Rachel SDK

编译

RachelSDKPIO 工程, VS Code 下载 PlatformIO 插件,用 VS Code 打开文件夹即可

SDK 目录树

.
├── apps
│   ├── app_ble_gamepad               BLE 手柄
│   ├── app_music                     音乐播放器
│   ├── app_nofrendo                  NES 模拟器
│   ├── app_raylib_games              Raylib 游戏
│   ├── app_screencast                WiFi 投屏
│   ├── app_settings                  设置
│   ├── app_genshin                   __,__!
│   ├── app_template                  App 模板
│   ├── launcher                      启动器
│   ├── utils                         通用组件库
│   ├── assets                        公共资源
│   ├── tools                         App 相关工具(脚本)
│   └── apps.h                        App 安装回调
├── hal
│   ├── hal.cpp                       HAL 基类
│   ├── hal.h                         HAL 基类
│   ├── hal_rachel                    HAL Rachel 派生类
│   ├── hal_simulator                 HAL PC 模拟器派生类
│   └── lgfx_fx                       lgfx 派生类(拓展图形API)
├── rachel.cpp
└── rachel.h                          RachelSDK 入口

SD 卡目录树

NES 模拟器、音乐播放器等会尝试加载SD卡里指定目录的资源文件

.
├── buzz_music                        蜂鸣器音乐
│   ├── harrypotter.json
│   ├── nokia.json
│   ...
├── fonts                             字体
│   └── font_text_24.vlw
└── nes_roms                          NES ROM 文件
    ├── Kirby's Adventure (E).nes
    ├── Snow Bros (U).nes
	...

font_text_24.vlw 这个字体我用的是 Zpix很嗨好看,可以替换任何自己喜欢的

NES ROM 直接丢进去就行,不是很大的应该都能玩

SDK 结构

创建 App

自动创建

写了个 python 脚本用来简化 App 创建:

python3 ./src/rachel/apps/tools/app_generator.py

​ $ Rachel app generator > <

​ $ app name:

hello_world

​ $ file names:

​ $ - ../app_hello_world/app_hello_world.cpp

​ $ - ../app_hello_world/app_hello_world.h

​ $ app class name: AppHello_world

​ $ install app hello_world

​ $ done

App 就创建好了, 重新编译上传:

新创建的 App 基本模板如下,详细的生命周期和API可以参考 Mooncake 项目

// Like setup()...
void AppTemplate::onResume()
{
    spdlog::info("{} 启动", getAppName());
}


// Like loop()...
void AppTemplate::onRunning()
{    
    spdlog::info("咩啊");
    HAL::Delay(1000);

    _data.count++;
    if (_data.count > 5)
        destroyApp();
}

Mooncake 框架内部集成了 spdlog 日志库,当然你也可以继续用 cout, printf, Serial...

手动创建

  • 复制 src/rachel/apps/app_template 到同一目录并重命名: src/rachel/apps/app_hello_world
  • 将里面的 app_template.cppapp_template.h 重命名为 app_hello_world.cppapp_hello_world.h
  • 打开 app_hello_world.cppapp_hello_world.h ,将里面的所有 AppTemplate 替换成 AppHello_world
  • 打开 src/rachel/apps/apps.h
  • 添加 #include "app_hello_world/app_hello_world.h"
  • 添加 mooncake->installApp(new MOONCAKE::APPS::AppHello_world_Packer);
  • 编译上传

常用的 App API

destroyApp()

关闭 App,调用后会告诉框架你不玩了,框架会把你的 App 销毁释放,所以在 onRunning() 被阻塞的情况下是无效的

// 有效
void AppTemplate::onRunning()
{
    destroyApp();
}

// 无效
void AppTemplate::onRunning()
{    
    destroyApp();
    HAL::Delay(66666666666);
}

getAppName()

获取 App 名字,会返回你设置的 App 名字

// 你的 App 头文件里:
class AppHello_world_Packer : public APP_PACKER_BASE
{
    // 这里修改你的 App 名字:
    std::string getAppName() override { return "文明讲礼外乡人"; }
    ...
}

getAppIcon()

获取 App 图标,启动器在渲染画面时会调用

// 你的 App 头文件里:
class AppHello_world_Packer : public APP_PACKER_BASE
{
    ...
    // 这里修改你的 App 图标(有默认图标)
    void* getAppIcon() override { return (void*)image_data_icon_app_default; }
    ...
}

mcAppGetDatabase()

获取数据库实例,是一个简单的 RAMKV 数据库,可以用于 App 退出数据保存、多 App 间的数据共享(当然断电没),详细用法参考这里

void AppTemplate::onResume()
{
    // 看看数据库里有没有这个 key
    if (mcAppGetDatabase()->Exist("开了?"))
    {
        // 数据库里拿出来, 看看开了几次
        int how_many = mcAppGetDatabase()->Get("开了?")->value<int>();
        spdlog::info("开了 {} 次", how_many);
		
        // 加上这一次, 写进数据库
        how_many++;
        mcAppGetDatabase()->Put<int>("开了?", how_many);
    }
    // 没有就创建一个
    else
        mcAppGetDatabase()->Add<int>("开了?", 1);
}

mcAppGetFramework()

获取 Mooncake 框架实例,一般用来写启动器.. 比如这里.

// 看看安装了几个 App
auto installed_app_num = mcAppGetFramework()->getAppRegister().getInstalledAppNum();
spdlog::info("安装了 {} 个 App", installed_app_num);

// 看看他们都叫什么
for (const auto& app_packer : mcAppGetFramework()->getAppRegister().getInstalledAppList())
{
    spdlog::info("{}", app_packer->getAppName());
}

HAL 硬件抽象层

HAL为单例模式,SDK初始化时会注入一个HAL实例.

  • 对于 HAL Rachel ,按住 按键A 开机,会暂停在初始化界面,可以查看详细的HAL初始化log
  • 如果有不同底层硬件需求,只需派生新的HAL对象,重写 API 方法 (override) 并在初始化时注入即可

Include

#include "{path to}/hal/hal.h"

显示 API

// 获取屏幕驱动实例
HAL::GetDisplay();

// 获取全屏Buffer实例
HAL::GetCanvas();

// 推送全屏buffer到显示屏
HAL::CanvasUpdate();

// 渲染FPS面板
HA::RenderFpsPanel();

显示驱动使用 LovyanGFX,详细的图形API可以参考原项目示例

系统 API

// 延时(毫秒)
HAL::Delay(unsigned long milliseconds);

// 获取系统运行毫秒数
HAL::Millis();

// 关机
HAL::PowerOff();

// 重启
HAL::Reboot();

// 设置RTC时间
HAL::SetSystemTime(tm dateTime);

// 获取当前时间
HAL::GetLocalTime();

// 优雅地抛个蓝屏
HAL::PopFatalError(std::string msg);

HAL Rachel 在初始化时会以RTC时间调整系统时间,所以时间相关的POSIX标准API都可以正常使用

外设 API

// 刷新IMU数据
HAL::UpdateImuData();

// 获取IMU数据
HAL::GetImuData();

// 蜂鸣器开始哔哔
HAL::Beep(float frequency, uint32_t duration);

// 蜂鸣器别叫了
HAL::BeepStop();

// 检查SD卡是否可用
HAL::CheckSdCard();

// 获取按键状态
HAL::GetButton(GAMEPAD::GamePadButton_t button);

// 获取任意按键状态
HAL::GetAnyButton();

系统配置 API

// 从内部FS导入系统配置
HAL::LoadSystemConfig();

// 保存系统配置到内部FS
HAL::SaveSystemConfig();

// 获取系统配置
HAL::GetSystemConfig();

// 设置系统配置
HAL::SetSystemConfig(CONFIG::SystemConfig_t cfg);

// 以系统配置刷新设备
HAL::UpdateSystemFromConfig();

通用组件库

一些比较有用的通用封装库放在了这里 rachel/apps/utils/system

选择菜单

创建一个选择菜单

Include

#include "{path to}/utils/system/ui/ui.h"

Example

using namespace SYSTEM::UI;

// 创建选择菜单
auto select_menu = SelectMenu();

// 创建选项列表
std::vector<std::string> items = {
    "[WHAT 7 TO PLAY]",
    "Jenshin Import",
    "Light Soul",
    "Grand Cop Manual",
    "Super Maliao",
    "Quit"
};

// 等待选择
auto selected_index = select_menu.waitResult(items);
spdlog::info("selected: {}", items[selected_index]);

进度条窗口

创建一个带有进度条的窗口(u1s1, 现在应该算是页面)

Include

#include "{path to}/utils/system/ui/ui.h"

Example

using namespace SYSTEM::UI;

for (int i = 0; i < 100; i++)
{
    ProgressWindow("正在检测智商..", i);
    HAL::CanvasUpdate();
    HAL::Delay(20);
}

蜂鸣器音乐播放器

参考 arduino-songs 的 json 格式蜂鸣器音乐播放器,json 格式音乐示例

Include

#include "{path to}/utils/system/audio/audio.h"

Example

using namespace SYSTEM::AUDIO;

// 播放SD路径上的json音乐文件
BuzzMusicPlayer::playFromSdCard("/buzz_music/nokia.json");

按钮

参考 Button 的按键库

Include

#include "{path to}/utils/system/inputs/inputs.h"

Example

using namespace SYSTEM::INPUTS;

auto button_a = Button(GAMEPAD::BTN_A);

while (1)
{
    if (button_a.pressed())
        spdlog::info("button a was pressed");
    if (button_a.released())
        spdlog::info("button a was released");
    if (button_a.toggled())
        spdlog::info("button a was toggled");
    HAL::Delay(20);
}

深入

更深入些的具体框架和实现,睡不着可以看看

HAL Rachel

HAL Rachel 派生自 HAL,提供了 HAL 中的 APIarduino-esp32 上的具体实现

目录树

.
├── components                        各外设的初始化和 API 实现
│   ├── hal_display.cpp
│   ├── hal_fs.cpp
│   ├── hal_gamepad.cpp
│   ├── hal_i2c.cpp
│   ├── hal_imu.cpp
│   ├── hal_power.cpp
│   ├── hal_rtc.cpp
│   ├── hal_sdcard.cpp
│   └── hal_speaker.cpp
├── hal_config.h                      引脚定义, 内部 log 定义等
├── hal_rachel.h                      类声明
└── utils
    └── m5unified                     非常好用的一些 ESP32 外设抽象

初始化流程

HAL 在被注入时会调用 init()HAL Rachel 重写的 init() 即为初始化流程:

inline void init() override
{
    _power_init();                    // 电源管理初始化
    _disp_init();                     // 显示屏初始化
    _gamepad_init();                  // 手柄按键初始化
    _spk_init();                      // 扬声器(蜂鸣器)初始化
    _i2c_init();                      // I2C 初始化
    _rtc_init();                      // RTC 初始化
    _imu_init();                      // IMU 初始化
    _fs_init();                       // 内部 Flash 文件系统初始化
    _sdcard_init();                   // SD 卡文件系统初始化
    _system_config_init();            // 系统配置初始化
    _sum_up();                        // 总结
}
  • 内部 Flash 文件系统使用 LittleFS ,目前只是用于系统设置的保存, 所以分区只给了 256 kB
  • loadTextFont24() 这个 API 的设计目的是用于更好看的(支持中文)文本显示需求,实现方式是从SD卡读取 vlw 字体,所以使用这个字体后,渲染画面耗时会变长
  • 当然有很多方法可以让上面这个API也适用于快速刷新的画面,不过对我来说这个自带字体够用了,启动器和选择菜单都是用的这个
  • RTC 和 IMU 这两个外设都可以在 M5Unified 这个库中找到现成好用的驱动和抽象,我只是从其中抽离出来根据需求做对接

HAL Simulator

因为 LovyanGFX 支持 SDL 作显示后端,因此要实现一个 PC 上的 HAL 实现基本什么都不用做(确信),一个头文件搞定。RachelSDK 的模拟器工程在这里

RachelSDK 初始化流程

有 HAL 把底层抽象架空,剩下的都是 C++ 自由发挥了(当然有些 App 还是直接用了平台特定 API, 比如 NES 模拟器用了 ESP32 的分区读写 API, 如果这些都给做上抽象就太浪费时间了~, 条件编译隔开就好, 不妨碍整体框架的通用性)

RachelSDK 的初始化在这里,具体如下:

...

// 根据平台注入具体 HAL 
#ifndef ESP_PLATFORM
HAL::Inject(new HAL_Simulator);
#else
HAL::Inject(new HAL_Rachel);
#endif

// 初始化 Mooncake 调度框架
_mooncake = new Mooncake;
_mooncake->init();

// 安装启动器 (嗯,启动器也是 App )
auto launcher = new APPS::Launcher_Packer;
_mooncake->installApp(launcher);

// 安装其他 App (设置、模拟器...)
rachel_app_install_callback(_mooncake);

// 创建启动器
_mooncake->createApp(launcher);

...

初始化完后, 由 Mooncake 框架接管,完成各个 App 的各个生命周期的调度,放个生命周期简图:

App Launcher

启动器,由 SDK 启动的第一个 App,用来启动 App 的 App(?)

目录树

.
├── assets                            静态资源
│   └── launcher_bottom_panel.hpp
├── launcher.cpp                      App Launcher 实现
├── launcher.h                        App Launcher 声明
└── view
    ├── app_anim.cpp                  App 打开关闭动画
    ├── menu.cpp                      启动器菜单
    └── menu_render_callback.hpp      启动器菜单渲染回调

打开 launcher.cpp

onCreate ,这个地方只会在启动器被创建时调用一次,所以负责自己属性的配置和资源申请等:

void Launcher::onCreate()
{
    ...
        
    // 允许后台运行
    setAllowBgRunning(true);
    // 允许创建后自动启动
    startApp();
    
    // 创建菜单(这个菜单就是安装了的 App 的列表的抽象, 后面渲染部分会详细讲)
    _create_menu();
}

onResume 会在启动器刚创建,或者从后台切到前台时被调用,所以放一些渲染前的准备,控件信息刷新..

void Launcher::onResume()
{
    ...

    // 切字体..
    HAL::LoadLauncherFont24();
    HAL::GetCanvas()->setTextScroll(false);
    
    // 更新状态栏的时间文本
    _update_clock(true);
}

onRunning ,没有其他 App 打开时,启动器读取输入..刷新菜单、控件.. 渲染画面..

void Launcher::onRunning()
{
    _update_clock();
    _update_menu();
}

偷偷点进去 _update_menu() 然后看看这里,可以看到当启动器需要打开一个 App 的时候干了什么:

...

// 看看开了哪一个
auto selected_item = _data.menu->getSelector()->getTargetItem();

// Skip launcher 
selected_item++;
// 获取选中的 App 的 App Packer
auto app_packer = mcAppGetFramework()->getInstalledAppList()[selected_item];

// 用他来创建和打开这个 App, 
if (mcAppGetFramework()->createAndStartApp(app_packer))
{
    ...
    // 将启动器压进后台
    closeApp();
}

...

倒回来看 onRunningBG ,启动器在后台时居然在..

void Launcher::onRunningBG()
{
    // 如果只剩下启动器一个 App 在运行(也就是说之前打开的 App 已经退出销毁了)
    if (mcAppGetFramework()->getAppManager().getCreatedAppNum() == 1)
    {
        ...
            
        // 将启动器推回前台
        mcAppGetFramework()->startApp(this);
        
        ...
    }
}

这里的判断方式其实会伴随一些限制,比如我不能在启动器在前台的同时,有其他 App 在后台搞事。因为启动器回到前台的条件就是只有他一个 App (好霸道),不过暂时也没这需求~

SmoothMenu

讲启动器的渲染之前要先插播一下 SmoothMenu 这个带简单路径插值的菜单抽象库

菜单

这只是个简单的菜单,所以可以分为三部分:

  • 菜单(Menu):就是菜单,存着有什么菜可以点
  • 选择器(Selector):你的手指,用来👉菜
  • 摄像机(Camera):你的眼睛,用来盯着你的手指

然后发散一点,将菜单里的每一道菜(Item),想象成坐标轴上的一个点 item(x, y),那菜单就变成了一系列点的集合: [item_1, item_2, item_3...]

然后你的手指👉的地方也是一点 selector(x, y) ,当你想吃第二道菜的时候,就可以指向 selector(item_2) ,告诉别人你对这道菜有意思(就意思意思)

到这里已经可以用了:按键 DOWN 按下的时候,👉从 selector(item_1) 跳到 selector(item_2) ,搞定

那摄像机用来干嘛捏,屏幕和眼睛一样有范围限制,所以菜单特别长的时候,眼睛要跟着👉动

插值

因为👉运动和数学大题一样要有过程,所以👉从 item_1(x1, y1)item_2(x2, y2) 要给上过程插值

我这里的插值实现是对 lvgl_anim 的封装(读书人的事怎么能叫抄呢(恼)):

// 参数: 动画曲线(贝塞尔), 开始值, 结束值, 过程时间
void setAnim(LV_ANIM_PATH_t path, int32_t startValue, int32_t endValue, int32_t time);

// 根据时间返回当前值
int32_t getValue(int32_t currentTime);

看完上面这两个 API 应该都明啦,只需要这样:

anim_x.setAnim(Q, x1, x2, 1);
anim_y.setAnim(Q, y1, y2, 1);

while (1)
{
    current_time = 宜家几点;
    selector(anim_x.getValue(current_time), anim_y.getValue(current_time));
}

👉就可以Q弹地从 item_1 运动到 item_2 了~

然后再发散一点,能不能给所有坐标都套上插值捏,就有了菜单打开关闭动画.. 长菜单滚动动画..

渲染

到这里我们已经抽象出来了一坨飞来飞去的坐标,直接把这坨坐标甩给用户就行了,渲染回调函数:

virtual void renderCallback(
    const std::vector<Item_t*>& menuItemList,        // 菜单
    const RenderAttribute_t& selector,               // 👉
    const RenderAttribute_t& camera                  // 摄像机
) {}

依次在坐标上渲染相应的目标,一个(帧)菜单页面就完成了

App Launcher 渲染

这下再回来看启动器的渲染就很清晰啦

首先是菜单的创建:

...
  
// 看看安装了什么
for (const auto& app : mcAppGetFramework()->getAppRegister().getInstalledAppList())
{
    // 跳过自己
    if (app->getAddr() == getAppPacker())
        continue;

    // 把 App 塞进菜单里
    _data.menu->getMenu()->addItem(
        // App 的名字
        app->getAppName(),
        // App 的 X 坐标
        THEME_APP_ICON_GAP + i * (THEME_APP_ICON_WIDTH + THEME_APP_ICON_GAP),
        // App 的 Y 坐标 (这里 Y 为恒定是因为我菜单是横着走的)
        THEME_APP_ICON_MARGIN_TOP,
        // 这东西有多宽 (图标宽)
        THEME_APP_ICON_WIDTH,
        // 这东西有多高 (图标高)
        THEME_APP_ICON_HEIGHT,
        // 把图标的的指针也塞进去
        app->getAppIcon()
    );
    i++;
}

...

然后看对应的渲染回调

...

// 首先引入了 X 偏移量, 是因为我只需要按下按键后图标们滚动, 相当于👉不动菜单动
// 所以把坐标系原点从菜单转换到👉就行
_x_offset = -(selector.x) + HAL::GetCanvas()->width() / 2 - THEME_APP_ICON_WIDTH_HALF;

// 遍历菜单里所有的东西
for (const auto& item : menuItemList)
{
    // 这里引入了 Y 偏移量, 是为了实现被选中的 App 图标比没选中的高, 就跟斗地主一样~
    _y_offset = std::abs(selector.x - item->x) / 3;

    // 最后根据坐标渲染 App 图标就大功告成了
    HAL::GetCanvas()->pushImage(
        item->x + _x_offset, 
        item->y + _y_offset,
        THEME_APP_ICON_WIDTH, 
        THEME_APP_ICON_HEIGHT, 
        (const uint16_t*)(item->userData)
    );

    ...
}

...
<<<<<<< HEAD MIT License Copyright (c) 2023 Forairaaaaa Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ======= Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. >>>>>>> e4d747f7da9fbd53758ddbfb9285a1f209d9a02a

简介

git from github 展开 收起
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/genvex/rachal.git
git@gitee.com:genvex/rachal.git
genvex
rachal
rachal
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891