40、资源管理器功能详解

资源管理器功能详解

1. 资源规范中的空白处理

在资源规范(ResourceSpec)里,名称或冒号前后的空白字符会被忽略。为了让值(Value)能以空白开头,“\space”(反斜杠加空格)会被识别并替换成空格字符,“\tab”(反斜杠加水平制表符)会被识别并替换成水平制表符。若要让值包含换行符,“\n”会被识别并替换成换行符。为了让值能跨多行写在文本文件里,“\newline”(反斜杠加换行符)会被识别并从值中移除。若要让值包含任意字符编码,“\nnn”(每个 n 是“0”-“7”范围内的数字字符)会被识别并替换成包含该序列指定八进制值的单字节。最后,“\newline”会被识别并替换成单个反斜杠。

例如,下面的资源行包含由四个字符组成的值:反斜杠、空字符、“z”和换行符:

magic.values: \\000\
z\n
2. 资源管理器匹配规则

确定哪个资源数据库条目与给定查询匹配的算法是资源管理器的核心。所有查询都必须完整指定所需资源的名称和类(不允许使用“ ”和“?”字符)。库支持完整名称或类最多包含 100 个组件。资源以部分指定的名称和类存储在数据库中,使用模式匹配结构。星号( )是松绑定,用于表示任意数量的中间组件,包括零个。句号(.)是紧绑定,用于分隔相邻组件。问号(?)用于匹配任意单个组件名称或类。数据库条目不能以松绑定结尾,必须指定最后一个组件(不能是“?”字符)。查找算法会在数据库中搜索与查询的完整名称和类最匹配(最具体)的条目。当有多个数据库条目匹配完整名称和类时,会使用优先级规则来选择一个。

完整名称和类从左到右(从层次结构的最高层到最低层)逐个组件进行扫描。在每一层,确定每个匹配条目的相应组件和/或绑定,并根据优先级规则比较这些匹配组件和绑定。在进入下一层之前,会在每一层应用每个规则,直到有一个规则选出一个条目。优先级规则如下:
- 包含匹配组件(无论是名称、类还是“?”字符)的条目优先于省略该层的条目(即通过松绑定匹配该层的条目)。
- 名称匹配的条目优先于类匹配的条目和使用“?”字符匹配的条目。类匹配的条目优先于使用“?”字符匹配的条目。
- 前面是紧绑定的条目优先于前面是松绑定的条目。

为说明这些规则,考虑以下资源数据库条目:
| 条目 | 资源数据库条目 |
| ---- | ---- |
| A | xmh Paned activeForeground: red |
| B | incorporate.Foreground: blue |
| C | xmh.toc
Command activeForeground: green |
| D | xmh.toc
?.Foreground: white |
| E | xmh.toc*Command.activeForeground: black |

考虑对以下资源的查询:

xmh.toc.messagefunctions.incorporate.activeForeground (名称)
Xmh.Paned.Box.Command.Foreground (类)

在每一层的匹配过程如下:
| 层次 | 名称组件 | 类组件 | 应用规则 | 排除条目 |
| ---- | ---- | ---- | ---- | ---- |
| 第一层 | xmh | Xmh | 规则 1 | B |
| 第二层 | toc | Paned | 规则 2 | A |
| 第三层 | messagefunctions | Box | 无 | 无 |
| 第四层 | incorporate | Command | 规则 2 | D |
| 第五层 | activeForeground | Foreground | 规则 3 | C |

3. 夸克(Quarks)

资源管理器的大多数使用场景都涉及将名称、类和表示类型定义为字符串常量。但在资源管理器中频繁使用字符串可能会很慢,因为在一些工具包中它被大量使用。为解决这个问题,在许多资源管理器函数中,使用字符串的简写形式代替字符串,这样可以进行简单比较而非字符串比较。字符串的简写名称称为夸克(quark),类型为 XrmQuark。有时,你可能想分配一个没有对应字符串的夸克。

夸克之于字符串,就像服务器中的原子之于字符串,但它的使用完全局限于你的应用程序。

要分配一个新的夸克,使用 XrmUniqueQuark 函数:

XrmQuark XrmUniqueQuark();

XrmUniqueQuark 函数分配的夸克保证不代表资源管理器已知的任何字符串。

每个名称、类和表示类型都被 typedef 为 XrmQuark:

typedef int XrmQuark, *XrmQuarkList;
typedef XrmQuark XrmName;
typedef XrmQuark XrmClass;
typedef XrmQuark XrmRepresentation;
#define NULLQUARK ((XrmQuark) 0)

列表表示为以空结尾的夸克数组,数组大小必须足够容纳使用的组件数量。

typedef XrmQuarkList XrmNameList;
typedef XrmQuarkList XrmClassList;

将字符串转换为夸克,可使用 XrmStringToQuark 或 XrmPermStringToQuark:

#define XrmStringToName(string) XrmStringToQuark(string)
#define XrmStringToClass(string) XrmStringToQuark(string)
#define XrmStringToRepresentation(string) XrmStringToQuark(string)
XrmQuark XrmStringToQuark(string);

若字符串不在主机可移植字符编码中,转换依赖于具体实现。XrmStringToQuark 的字符串参数不需要永久分配存储。XrmPermStringToQuark 和 XrmStringToQuark 类似,只是 Xlib 可以假设字符串参数是永久分配的,因此可以用作 XrmQuarkToString 返回的值。

对于任何给定的夸克,如果 XrmStringToQuark 返回非空值,后续所有调用都会返回相同的值(相同地址)。

将夸克转换为字符串,使用 XrmQuarkToString:

#define XrmNameToString(name)  XrmQuarkToString(name)
#define XrmClassToString(class)  XrmQuarkToString(name)
#define XrmRepresentationToString(type)  XrmQuarkToString(type)
char *XrmQuarkToString(quark);

返回值指向的字符串不能被修改或释放。如果该夸克没有对应的字符串,XrmQuarkToString 返回 NULL。对于任何给定的夸克,如果 XrmQuarkToString 返回非空值,后续所有调用都会返回相同的值(相同地址)。

将包含一个或多个组件的字符串转换为夸克列表,使用 XrmStringToQuarkList:

#define XrmStringToNameList(str,name)  XrmStringToQuarkList((str), (name))
#define XrmStringToClassList(str,class)  XrmStringToQuarkList((str), (class))
void XrmStringToQuarkList(string, quarks_return);

字符串必须是有效的 ResourceName 格式。若字符串不在主机可移植字符编码中,转换依赖于具体实现。

绑定列表是 XrmBindingList 类型的列表,用于指示名称或类列表的组件是紧绑定还是松绑定(即是否指定了中间组件的通配符):

typedef enum {XrmBindTightly, XrmBindLoosely} XrmBinding, *XrmBindingList;

XrmBindTightly 表示组件由句号分隔,XrmBindLoosely 表示组件由星号分隔。

将包含一个或多个组件的字符串转换为绑定列表和夸克列表,使用 XrmStringToBindingQuarkList:

XrmStringToBindingQuarkList(string, bindings_return, quarks_return);

组件名称在列表中由句号或星号分隔。字符串必须是有效的 ResourceName 格式。如果字符串不以句号或星号开头,则假定为紧绑定。例如,字符串“ a.b c”转换后如下:
| 类型 | 详情 |
| ---- | ---- |
| 夸克 | a, b, c |
| 绑定 | 松, 紧, 松 |

4. 创建和存储数据库

资源数据库是不透明类型 XrmDatabase。每个数据库值存储在 XrmValue 结构中,该结构由大小、地址和表示类型组成。大小以字节为单位指定。表示类型是一种按应用程序定义的类型标记数据的方式(例如,“font”或“color”字符串),与 C 数据类型或其类无关。XrmValue 结构定义如下:

typedef struct {
    unsigned int size;
    XPointer addr;
} XrmValue, *XrmValuePtr;

初始化资源管理器,使用 XrmInitialize:

void XrmInitialize();

从磁盘检索数据库,使用 XrmGetFileDatabase:

XrmDatabase XrmGetFileDatabase(filename);

指定的文件应包含有效 ResourceLine 格式的条目序列。如果无法打开指定文件,XrmGetFileDatabase 返回 NULL。

将数据库副本存储到磁盘,使用 XrmPutFileDatabase:

void XrmPutFileDatabase(database, stored_db);

文本以有效 ResourceLine 格式的条目序列写入文件。包含不在主机可移植字符编码中的资源名称或不在数据库区域设置编码中的值的条目,将以依赖于具体实现的方式写入。写入条目的顺序依赖于具体实现。表示类型不是“String”的条目会被忽略。

获取显示器与屏幕无关的资源指针,使用 XResourceManagerString:

char *XResourceManagerString(display);

返回的字符串由 Xlib 拥有,客户端不应释放。如果没有该属性,返回 NULL。

获取屏幕特定资源的指针,使用 XScreenResourceString:

char *XScreenResourceString(screen);

调用者负责使用 XFree 释放返回的字符串。如果没有该属性,返回 NULL。

从字符串创建数据库,使用 XrmGetStringDatabase:

XrmDatabase XrmGetStringDatabase(data);

XrmGetStringDatabase 与 XrmGetFileDatabase 类似,只是从字符串而不是文件中读取信息。

获取数据库的区域设置名称,使用 XrmLocaleOfDatabase:

char *XrmLocaleOfDatabase(database);

返回的区域设置名称字符串由 Xlib 拥有,客户端不应修改或释放。

销毁资源数据库并释放其分配的内存,使用 XrmDestroyDatabase:

void XrmDestroyDatabase(database);

如果数据库为 NULL,XrmDestroyDatabase 会立即返回。

将资源数据库与显示器关联,使用 XrmSetDatabase:

void XrmSetDatabase(display, database);

之前与显示器关联的数据库(如果有)不会被销毁。

获取与显示器关联的资源数据库,使用 XrmGetDatabase:

XrmDatabase XrmGetDatabase(display);

如果尚未设置数据库,返回 NULL。

5. 合并资源数据库

将资源文件的内容合并到数据库中,使用 XrmCombineFileDatabase:

Status XrmCombineFileDatabase(filename, target_db, override);

如果文件和数据库中的条目使用相同的指定符,当 override 为 True 时,文件中的条目将替换数据库中的条目;否则,文件中的条目将被丢弃。如果文件无法读取,返回零状态;否则,返回非零状态。如果 target_db 为 NULL,XrmCombineFileDatabase 会创建并返回一个新的数据库。

将一个数据库的内容合并到另一个数据库中,使用 XrmCombineDatabase:

void XrmCombineDatabase(source_db, target_db, override);

如果两个数据库中的条目使用相同的指定符,当 override 为 True 时,source_db 中的条目将替换 target_db 中的条目;否则,source_db 中的条目将被丢弃。如果 target_db 为 NULL,XrmCombineDatabase 会直接将 source_db 存储到其中。否则,source_db 会在合并时被销毁,但 target_db 指向的数据库不会被销毁。

使用覆盖语义将一个数据库的内容合并到另一个数据库中,使用 XrmMergeDatabases:

void XrmMergeDatabases(source_db, target_db);

调用 XrmMergeDatabases 函数等同于调用 XrmCombineDatabase 函数且 override 参数为 True。

6. 查找资源

从资源数据库中检索资源,使用 XrmGetResource、XrmQGetResource 或 XrmQGetSearchResource:

Bool XrmGetResource(database, str_name, str_class, str_type_return, value_return);
Bool XrmQGetResource(database, quark_name, quark_class, quark_type_return, value_return);

XrmGetResource 和 XrmQGetResource 函数从指定数据库中检索资源。如果找到资源,返回 True;否则,返回 False。

大多数应用程序和工具包不会随机在资源数据库中探查资源。X 工具包对资源数据库的访问模式很规范。通常会进行 1 到 20 次探查,每次探查只有最后一个名称/类不同。XrmGetResource 函数最坏情况下是 2n 算法,其中 n 是名称/类列表的长度。应用程序程序员可以通过预取可能匹配名称/类列表第一部分的数据库级别列表来改进。

获取数据库级别列表,使用 XrmQGetSearchList:

Bool XrmQGetSearchResource(database, names, classes, list_return, list_length);

如果 list_return 足够大以容纳搜索列表,XrmQGetSearchList 返回 True;否则,返回 False。调用者必须分配的搜索列表大小取决于存储在数据库中的资源指定符的级别和通配符数量。最坏情况下的长度是 3n,其中 n 是 names 或 classes 中名称或类组件的数量。

资源管理器功能详解

7. 操作流程总结

为了更清晰地展示资源管理器的各项操作,下面将主要操作流程以 mermaid 流程图的形式呈现。

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px

    A([开始]):::startend --> B(初始化资源管理器):::process
    B --> C{选择操作}:::decision
    C -->|创建数据库| D[/从文件创建数据库/]:::process
    C -->|创建数据库| E[/从字符串创建数据库/]:::process
    C -->|存储数据库| F(将数据库存储到磁盘):::process
    C -->|合并数据库| G(合并文件到数据库):::process
    C -->|合并数据库| H(合并数据库到数据库):::process
    C -->|查找资源| I(从数据库查找资源):::process
    D --> J(使用数据库):::process
    E --> J
    F --> K(操作完成):::process
    G --> J
    H --> J
    I --> J
    J --> L(销毁数据库):::process
    L --> M([结束]):::startend

这个流程图展示了资源管理器的主要操作流程,包括初始化、创建数据库、存储数据库、合并数据库、查找资源以及最终销毁数据库。用户可以根据自己的需求选择相应的操作。

8. 操作示例

下面通过一个简单的示例来演示如何使用资源管理器的部分功能。

#include <X11/Xresource.h>

int main() {
    // 初始化资源管理器
    XrmInitialize();

    // 从文件创建数据库
    XrmDatabase database = XrmGetFileDatabase("resource_file");
    if (database == NULL) {
        // 处理文件打开失败的情况
        return 1;
    }

    // 定义资源名称和类
    char *str_name = "xmh.toc.messagefunctions.incorporate.activeForeground";
    char *str_class = "Xmh.Paned.Box.Command.Foreground";
    char *str_type;
    XrmValue value;

    // 查找资源
    Bool found = XrmGetResource(database, str_name, str_class, &str_type, &value);
    if (found) {
        // 处理找到的资源
        printf("Resource found! Type: %s, Value size: %u\n", str_type, value.size);
    } else {
        // 处理未找到资源的情况
        printf("Resource not found.\n");
    }

    // 销毁数据库
    XrmDestroyDatabase(database);

    return 0;
}

这个示例代码展示了如何初始化资源管理器、从文件创建数据库、查找资源以及销毁数据库。在实际应用中,可以根据具体需求进行更多操作,如合并数据库、将数据库存储到文件等。

9. 注意事项

在使用资源管理器时,有一些注意事项需要牢记:
- 字符编码 :在进行字符串到夸克的转换以及数据库的创建和操作时,如果字符串不在主机可移植字符编码中,转换和操作结果可能依赖于具体实现,需要特别注意。
- 内存管理 :对于一些函数返回的字符串或指针,如 XResourceManagerString 返回的字符串由 Xlib 拥有,客户端不应释放;而 XScreenResourceString 返回的字符串需要调用者使用 XFree 释放。在销毁数据库时,要确保不再使用相关资源,避免内存泄漏。
- 数据库操作 :在合并数据库时,要明确 override 参数的含义,根据需求选择合适的合并方式。同时,要注意数据库中表示类型不是“String”的条目在存储到文件时会被忽略。

10. 总结

资源管理器是一个强大的工具,它提供了丰富的功能来管理和操作资源数据库。通过本文的介绍,我们了解了资源规范中的空白处理、匹配规则、夸克的使用、数据库的创建和存储、合并以及资源的查找等重要内容。在实际应用中,我们可以根据具体需求灵活运用这些功能,提高资源管理的效率和准确性。

同时,我们也看到了操作流程的复杂性和注意事项的重要性。在使用资源管理器时,要仔细遵循相关规则和注意事项,确保程序的稳定性和可靠性。希望本文能对读者在理解和使用资源管理器方面有所帮助。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值