如何在Linux下用NASM x64新建窗口并绘制像素?
在Linux x64 NASM中实现X11窗口绘制像素
核心思路
Linux桌面环境依赖X11窗口系统,直接通过汇编调用X11动态库函数,结合Linux系统调用读取文件,就能实现无C/C++依赖的窗口像素绘制。以下是完整实现步骤和代码示例:
实现步骤与代码
1. 汇编代码(pixel_drawer.asm)
section .data ; X11相关常量 display_name db 0 ; 使用默认显示(:0) window_title db "Pixel Image Viewer", 0 ; 文件路径(替换成你的0/1数据文件) file_path db "image_data.txt", 0 ; 错误提示 err_open db "Failed to open file", 10, 0 err_read db "Failed to read file", 10, 0 err_x11_init db "Failed to initialize X11", 10, 0 ; 图像参数(可根据文件内容调整,或从文件头读取) img_width equ 64 img_height equ 64 pixel_size equ 8 ; 每个逻辑像素放大8倍,避免太小看不清 section .bss display resq 1 window resq 1 gc resq 1 file_fd resq 1 file_buffer resb img_width * img_height ; 存储读取的0/1数据 section .text global _start ; 打印错误信息并退出 print_error: mov rsi, rdi call str_len mov rdx, rax mov rsi, rdi mov rdi, 1 mov rax, 1 syscall mov rdi, 1 mov rax, 60 syscall ; 计算字符串长度 str_len: mov rax, 0 .loop: cmp byte [rsi + rax], 0 je .done inc rax jmp .loop .done: ret _start: ; 1. 打开文件 mov rax, 2 ; sys_open mov rdi, file_path mov rsi, 0 ; O_RDONLY mov rdx, 0 syscall cmp rax, 0 jl print_error ; 打开失败,打印错误 mov [file_fd], rax ; 2. 读取文件内容 mov rax, 0 ; sys_read mov rdi, [file_fd] mov rsi, file_buffer mov rdx, img_width * img_height syscall cmp rax, img_width * img_height jne print_error ; 读取长度不符,报错 ; 关闭文件 mov rax, 3 ; sys_close mov rdi, [file_fd] syscall ; 3. 初始化X11连接 extern XOpenDisplay mov rdi, display_name call XOpenDisplay cmp rax, 0 je print_error mov [display], rax ; 获取默认屏幕 extern XDefaultScreen mov rdi, [display] call XDefaultScreen mov rsi, rax ; 屏幕号存到rsi,后续用 ; 创建窗口 extern XCreateSimpleWindow mov rdi, [display] mov rdx, 0 ; x坐标 mov rcx, 0 ; y坐标 mov r8, img_width * pixel_size ; 窗口宽度 mov r9, img_height * pixel_size ; 窗口高度 push 1 ; 边框宽度 push 0 ; 边框颜色(黑色) push 0xffffff ; 背景颜色(白色) call XCreateSimpleWindow mov [window], rax ; 设置窗口标题 extern XStoreName mov rdi, [display] mov rsi, [window] mov rdx, window_title call XStoreName ; 创建绘图上下文(GC) extern XCreateGC mov rdi, [display] mov rsi, [window] mov rdx, 0 mov rcx, 0 call XCreateGC mov [gc], rax ; 显示窗口 extern XMapWindow mov rdi, [display] mov rsi, [window] call XMapWindow ; 刷新显示 extern XFlush mov rdi, [display] call XFlush ; 4. 绘制像素 mov rbx, 0 ; 当前数据索引 mov rcx, 0 ; y坐标(逻辑像素) .draw_y_loop: cmp rcx, img_height je .draw_done mov rdx, 0 ; x坐标(逻辑像素) .draw_x_loop: cmp rdx, img_width je .next_y ; 获取当前像素值(0或1) mov al, byte [file_buffer + rbx] cmp al, '1' jne .skip_pixel ; 绘制放大后的像素块(pixel_size x pixel_size) mov r8, rdx imul r8, pixel_size mov r9, rcx imul r9, pixel_size mov r10, 0 .fill_x: cmp r10, pixel_size je .fill_y_done mov r11, 0 .fill_y: cmp r11, pixel_size je .next_fill_x ; 调用XDrawPoint绘制单个点 extern XDrawPoint mov rdi, [display] mov rsi, [window] mov rdx, [gc] mov rcx, r8 + r10 mov r8, r9 + r11 call XDrawPoint inc r11 jmp .fill_y .next_fill_x: inc r10 jmp .fill_x .fill_y_done: .skip_pixel: inc rbx inc rdx jmp .draw_x_loop .next_y: inc rcx jmp .draw_y_loop .draw_done: ; 5. 等待窗口关闭事件 extern XNextEvent stack_alloc equ 32 ; XEvent结构体大小至少32字节,栈对齐 sub rsp, stack_alloc .event_loop: mov rdi, [display] mov rsi, rsp call XNextEvent ; 检查是否是关闭事件(DestroyNotify) mov ax, word [rsp] ; event.type cmp ax, 17 ; DestroyNotify的类型值 jne .event_loop ; 6. 清理资源 extern XFreeGC mov rdi, [display] mov rsi, [gc] call XFreeGC extern XDestroyWindow mov rdi, [display] mov rsi, [window] call XDestroyWindow extern XCloseDisplay mov rdi, [display] call XCloseDisplay ; 退出程序 mov rax, 60 mov rdi, 0 syscall
2. 编译与运行
- 编译汇编文件:
nasm -f elf64 pixel_drawer.asm -o pixel_drawer.o - 链接(需要链接X11库):
ld -o pixel_drawer pixel_drawer.o -lX11 - 准备
image_data.txt文件,内容为连续的0和1,长度等于img_width * img_height(比如64x64就是4096个字符),示例片段:0000000000000000000000000000000000000000000000000000000000000000 0111111001111110011111100111111001111110011111100111111001111110 ... - 运行程序:
./pixel_drawer
关键细节说明
- X11函数调用:通过
extern声明X11库中的函数,链接时指定-lX11动态链接,无需依赖C标准库。 - System V AMD64调用约定:参数按
rdi, rsi, rdx, rcx, r8, r9顺序传递,超过6个的参数压栈,栈需保持16字节对齐。 - 像素放大:直接绘制单个点会太小,这里用
pixel_size将每个逻辑像素放大成方块,便于观察。 - 事件处理:通过
XNextEvent等待窗口关闭事件,确保程序正常退出并清理资源。
内容的提问来源于stack exchange,提问作者IsmellBeans
相关产品推荐
相关产品推荐

