总结创建窗口全过程

用SDK创建窗口的过程,是程序和Windows交互的过程。创建过程的每一步都要用到Windows API。

第一步创建窗口类:即定义一个WNDCLASS对象,然后填充其内的变量。如:

WNDCLASS wc;

wc.style=CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc   = WndProc;//指向窗口过程函数的指针

......

定义好后,进入第二步注册窗口类:即调用Windows API RegisterClass(&wc)即可注册窗口类wc。

第三步创建窗口:这一步要用到CreateWindow(......)它返回一个窗口ID句柄。我们用HWND类型变量获得该值。以后要用到该窗口,将该变量导入即可。

第四步显示和更新窗口:即调用 ShowWindow (hwnd, iCmdShow)UpdateWindow (hwnd)显示和更新和hwnd对应的窗口。iCmdShow告诉它如何显示这个窗口。

第五步创建循环消息:

 while (GetMessage (&msg, NULL, 0, 0))//从消息队列中取出一条消息
 {
  TranslateMessage (&msg) ;//进行消息(如键盘消息)转换
  DispatchMessage (&msg); //分派消息到窗口的回调函数处理,(OS调用窗口回调函数进行处理)。
 }

这里调用了三个API,注释已经写得很清楚了。其中DispatchMessage调用的回调函数即是我们上面在创建窗口类时定义的wc.lpfnWndProc   = WndProc,WndProc(......)的这个语句中被定义为回调函数。我们一会还得定义 WndProc(......)。循环体退出的条件是当GetMessage()取出的消息是WM_QUIT时。其它消息则会进行处理。

第六步终止应用程序:

return msg.wParam ; //表示从PostQuitMessage返回的值

第七步完成回调函数(WndProc):

回调函数定义如下:

LRESULT CALLBACK WndProc (HWND hwnd,
                          UINT message,
                          WPARAM wParam,
                          LPARAM lParam)

{      switch (message)

       {case WM_XXXXX:

           //相应的处理方法

           return x;

         ......

       }     

       return DefWindowProc (hwnd, message, wParam, lParam) ;

}

完成...

       //如果没有定义相应的消息处理函数就用Windows默认的处理方法处理。

<think>我们正在使用Wayland协议和xdg-shell来创建一个窗口。根据引用[1]和引用[2],我们需要以下步骤: 1. 生成xdg-shell协议的头文件和实现文件(xdg-shell-client-protocol.h和xdg-shell-protocol.c)。我们可以从已有的资源中获取,或者使用wayland-scanner工具从协议文件生成。 2. 编写客户端代码,包括连接到Wayland显示,注册全局对象,创建xdg_wm_base,创建surface,创建xdg_surface和xdg_toplevel,以及处理事件。 3. 编译时链接wayland-client库。 引用[1]提供了一个示例代码的概述,但没有给出完整代码。引用[2]提到了如何获取必要的协议文件。 下面是一个完整的示例步骤: ### 步骤1:生成xdg-shell协议文件 使用`wayland-scanner`生成xdg-shell协议的客户端代码: ```bash wayland-scanner private-code < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > xdg-shell-protocol.c wayland-scanner client-header < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > xdg-shell-client-protocol.h ``` 如果协议文件路径不同,请相应调整。 ### 步骤2:编写客户端代码(client.c) 以下是一个简单的Wayland客户端,它创建一个窗口并显示一个网格背景(基于引用[1]的代码,并补充完整): ```c #include <wayland-client.h> #include "xdg-shell-client-protocol.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <unistd.h> #include <fcntl.h> #include <time.h> #define WIDTH 640 #define HEIGHT 480 static struct wl_display *display = NULL; static struct wl_compositor *compositor = NULL; static struct wl_surface *surface = NULL; static struct wl_buffer *buffer = NULL; static struct xdg_wm_base *xdg_wm_base = NULL; static struct xdg_surface *xdg_surface = NULL; static struct xdg_toplevel *xdg_toplevel = NULL; static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); } static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_configure, }; static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) { xdg_wm_base_pong(xdg_wm_base, serial); } static const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = xdg_wm_base_ping, }; static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, wl_compositor_interface.name) == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); } } static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { // 全局对象移除 } static const struct wl_registry_listener registry_listener = { .global = registry_handle_global, .global_remove = registry_handle_global_remove, }; static void create_buffer() { int stride = WIDTH * 4; int size = stride * HEIGHT; int fd = open("/dev/shm/wayland-shm", O_RDWR | O_CREAT | O_EXCL, 0600); if (fd < 0) { perror("open"); exit(1); } unlink("/dev/shm/wayland-shm"); if (ftruncate(fd, size) < 0) { perror("ftruncate"); close(fd); exit(1); } uint32_t *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { perror("mmap"); close(fd); exit(1); } struct wl_shm_pool *pool = wl_shm_create_pool(wl_display_get_shm(display), fd, size); buffer = wl_shm_pool_create_buffer(pool, 0, WIDTH, HEIGHT, stride, WL_SHM_FORMAT_XRGB8888); wl_shm_pool_destroy(pool); close(fd); // 绘制网格背景 for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { uint32_t color = 0xFF000000; // 黑色背景 if ((x / 20) % 2 == (y / 20) % 2) { color = 0xFFFFFFFF; // 白色方块 } data[y * WIDTH + x] = color; } } munmap(data, size); } int main(int argc, char **argv) { display = wl_display_connect(NULL); if (!display) { fprintf(stderr, "无法连接到Wayland显示\n"); return 1; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, &registry_listener, NULL); wl_display_roundtrip(display); // 等待注册全局对象 if (compositor == NULL || xdg_wm_base == NULL) { fprintf(stderr, "缺少必要的全局对象\n"); return 1; } surface = wl_compositor_create_surface(compositor); xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_toplevel_set_title(xdg_toplevel, "Wayland窗口示例"); wl_surface_commit(surface); create_buffer(); wl_surface_attach(surface, buffer, 0, 0); wl_surface_commit(surface); while (wl_display_dispatch(display) != -1) { // 事件循环 } // 清理 xdg_toplevel_destroy(xdg_toplevel); xdg_surface_destroy(xdg_surface); wl_surface_destroy(surface); wl_buffer_destroy(buffer); xdg_wm_base_destroy(xdg_wm_base); wl_compositor_destroy(compositor); wl_registry_destroy(registry); wl_display_disconnect(display); return 0; } ``` ### 步骤3:编译 使用以下命令编译: ```bash cc -o client client.c xdg-shell-protocol.c -lwayland-client -lrt ``` ### 运行 运行生成的可执行文件: ```bash ./client ``` ### 注意事项 1. 代码中使用了共享内存(shm)来创建缓冲区,并绘制了一个网格背景。 2. 窗口标题设置为"Wayland窗口示例"。 3. 事件循环中只是简单地分发事件,没有处理窗口关闭事件。实际应用中需要添加事件处理(例如,处理xdg_toplevel的close事件)。 ### 关闭窗口 目前这个示例没有处理关闭事件,因此关闭窗口需要使用外部方式(如kill)。要支持关闭,可以添加`xdg_toplevel`的监听器,处理`close`事件,并在收到该事件时退出循环。 ### 添加关闭事件处理 修改代码以支持关闭事件: 在全局变量后添加: ```c static int running = 1; ``` 添加`xdg_toplevel`监听器: ```c static void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states) { // 可以处理配置事件(如窗口大小变化) } static void xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { running = 0; } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_configure, .close = xdg_toplevel_close, }; ``` 在创建`xdg_toplevel`后添加监听器: ```c xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); xdg_toplevel_set_title(xdg_toplevel, "Wayland窗口示例"); ``` 修改事件循环: ```c while (wl_display_dispatch(display) != -1 && running) { // 事件循环 } ``` 这样,当用户点击窗口的关闭按钮时,合成器会发送关闭事件,程序会退出循环。 ### 总结 以上是一个使用Wayland和xdg-shell创建窗口的完整示例。它展示了从生成协议文件到编写客户端代码,再到编译和运行的全过程。 §§相关问题§§ 1. Wayland客户端如何处理多个窗口? 2. 如何在Wayland窗口中绘制OpenGL内容? 3. Wayland的xdg-shell协议中弹出窗口(popup)如何创建?</think>### Wayland xdg-shell 创建窗口完整教程 以下是使用 Wayland 协议和 xdg-shell 创建窗口的完整步骤和示例代码: #### 1. **准备环境** ```bash # 安装依赖 sudo apt install libwayland-dev libwayland-bin wayland-protocols ``` #### 2. **生成协议代码** ```bash wayland-scanner client-header \ < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml \ > xdg-shell-client-protocol.h wayland-scanner private-code \ < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml \ > xdg-shell-protocol.c ``` #### 3. **创建客户端代码 (`client.c`)** ```c #include <wayland-client.h> #include "xdg-shell-client-protocol.h" #include <stdio.h> #include <string.h> #include <sys/mman.h> #include <unistd.h> #include <fcntl.h> #define WIDTH 640 #define HEIGHT 480 // 全局对象 static struct wl_display *display = NULL; static struct wl_compositor *compositor = NULL; static struct wl_surface *surface = NULL; static struct wl_buffer *buffer = NULL; static struct xdg_wm_base *xdg_wm_base = NULL; static struct xdg_surface *xdg_surface = NULL; static struct xdg_toplevel *xdg_toplevel = NULL; // XDG WM Base 监听器 static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) { xdg_wm_base_pong(xdg_wm_base, serial); } static const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = xdg_wm_base_ping, }; // XDG Surface 监听器 static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); } static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_configure, }; // 注册全局对象 static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, wl_compositor_interface.name) == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); } } static const struct wl_registry_listener registry_listener = { .global = registry_handle_global, .global_remove = NULL, }; // 创建共享内存缓冲区 static void create_buffer() { int stride = WIDTH * 4; // 32位 ARGB int size = stride * HEIGHT; // 创建共享内存文件 int fd = open("/dev/shm/wayland-shm", O_RDWR | O_CREAT | O_EXCL, 0666); ftruncate(fd, size); uint32_t *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // 绘制蓝色背景 for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { data[y * WIDTH + x] = 0xFF0000FF; // ARGB: 蓝色 } } struct wl_shm_pool *pool = wl_shm_create_pool(wl_display_get_shm(display), fd, size); buffer = wl_shm_pool_create_buffer(pool, 0, WIDTH, HEIGHT, stride, WL_SHM_FORMAT_XRGB8888); wl_shm_pool_destroy(pool); close(fd); munmap(data, size); } int main() { // 连接到 Wayland 显示服务器 display = wl_display_connect(NULL); if (!display) { fprintf(stderr, "无法连接到 Wayland 显示服务器\n"); return 1; } // 获取注册表 struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, &registry_listener, NULL); wl_display_roundtrip(display); // 等待全局对象注册 // 创建表面 surface = wl_compositor_create_surface(compositor); // 创建 XDG 表面 xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); // 创建顶层窗口 xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_toplevel_set_title(xdg_toplevel, "Wayland 窗口示例"); // 提交初始状态 wl_surface_commit(surface); wl_display_roundtrip(display); // 等待配置事件 // 创建缓冲区 create_buffer(); // 附加缓冲区到表面 wl_surface_attach(surface, buffer, 0, 0); wl_surface_commit(surface); // 主事件循环 while (wl_display_dispatch(display) != -1) { // 保持窗口打开 } // 清理资源 xdg_toplevel_destroy(xdg_toplevel); xdg_surface_destroy(xdg_surface); wl_surface_destroy(surface); wl_buffer_destroy(buffer); wl_registry_destroy(registry); wl_display_disconnect(display); return 0; } ``` #### 4. **编译和运行** ```bash cc -o client client.c xdg-shell-protocol.c -lwayland-client -lrt ./client ``` #### 关键步骤说明: 1. **连接显示服务器**:`wl_display_connect(NULL)` 建立与 Wayland 合成器的连接[^1] 2. **获取全局对象**:通过注册表获取 `wl_compositor` 和 `xdg_wm_base` 3. **创建表面**:`wl_compositor_create_surface()` 创建基础表面 4. **创建 XDG 表面**:`xdg_wm_base_get_xdg_surface()` 创建符合规范的窗口表面[^4] 5. **创建顶层窗口**:`xdg_surface_get_toplevel()` 创建可管理窗口 6. **设置窗口属性**:如 `xdg_toplevel_set_title()` 设置标题 7. **创建缓冲区**:使用共享内存(WL_SHM)创建像素缓冲区[^2] 8. **提交表面**:`wl_surface_commit()` 将更改提交到服务器 9. **事件循环**:`wl_display_dispatch()` 处理来自服务器的事件 #### 常见问题解决: - **协议文件缺失**:确保 `xdg-shell.xml` 路径正确(通常在 `/usr/share/wayland-protocols/stable/`) - **共享内存错误**:检查 `/dev/shm` 权限,或改用 `memfd_create()`[^2] - **窗口不显示**:确认正确调用 `wl_surface_commit()` 和事件循环 - **关闭窗口**:需实现 `xdg_toplevel` 的 close 事件处理 > 此示例创建了一个固定大小的蓝色窗口,实际应用中可扩展为处理输入事件、调整大小等功能。Qt 等高级框架封装了这些底层操作[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值