flat assembler
Message board for the users of flat assembler.
Index
> Linux > XCB full window example (with button and framebuffer access) |
| Author |
|
|
Jessé 13 Aug 2026, 17:36
So far, quite inspired by this excelent post, I've created a full window example, but using the (supposedly) more efficient 'libxcb' instead of 'libX11', which showcases:
- Advanced window properties, like, 32-bit RGBA color handling, fixed size, title name, and more; - A shared memory "framebuffer" that can be drawn pixel-by-pixel by software, which actually will be copied to window real framebuffer on paint; - A custom background, generated by software, using a quite cool stub idea that occured to me when trying to deal with 'OK son, now you've got a 32-bit RGBA window framebuffer, so, whats next?' question; - A custom icon which, as of the custom background, is uniquely drawn at every single run, because its result is based on random data from RNG unit, as well as TSC values; - "Widgets", done in the Microsoft way (everything is a window), and handling quite the same. A fully functional and animated button, and also a label; - Using Cairo to render text, so it will be more modern looking; - Filtering and handling events, as well as an easy "click event" example, emulated by control over BUTTON_PRESS and BUTTON_RELEASE events. Here, the complete source for rapid reference: Code: format ELF64 ; Dynamic address ELF include 'fastcall3.inc' ; All available at: https://github.com/Jesse-6/fastcall include 'stdio.inc' ; include 'xcb.inc' ; include 'xcb-shm.inc' ; include 'xcb-icccm.inc' ; include 'cairo.inc' ; ; Short offset names for those structures define_offset art XCB_INTERN_ATOM_REPLY_T define_offset bpe XCB_BUTTON_PRESS_EVENT_T define_offset bre XCB_BUTTON_PRESS_EVENT_T define_offset cme XCB_CLIENT_MESSAGE_EVENT_T define_offset eet XCB_EXPOSE_EVENT_T define_offset ene XCB_ENTER_NOTIFY_EVENT_T define_offset get XCB_GENERIC_EVENT_T define_offset lne XCB_ENTER_NOTIFY_EVENT_T define_offset mne XCB_MOTION_NOTIFY_EVENT_T define_offset qer XCB_QUERY_EXTENSION_REPLY_T define_offset scr XCB_SCREEN_T define_offset xdi XCB_DEPTH_ITERATOR_T define_offset xdt XCB_DEPTH_T define_offset xge XCB_GENERIC_ERROR_T define_offset xvi XCB_VISUALTYPE_ITERATOR_T define_offset xvt XCB_VISUALTYPE_T ; Window parameters WIDTH = 400 HEIGHT = 240 ; Button parameters B_WIDTH = (WIDTH / 3) - 10 B_HEIGHT = (HEIGHT / 6) ; Label parameters L_WIDTH = WIDTH - 40 L_HEIGHT = 20 ; Start by parsing command line _code Start entry: cmp [rsp], dword 2 jb @1f ja .err mov r11, [rsp+16] cmp [r11], dword '-B1' ; use algorithm 1 to draw background jne @f or [bg_mode], 1 jmp @1f @@ cmp [r11], dword '-B2' ; use algorithm 2 to draw background (default) jne @f and [bg_mode], 0 jmp @1f @@ cmp [r11], dword '-B3' ; use algorithm 1 and remove random dots jne @f or [bg_mode], 11b jmp @1f @@ cmp [r11], dword '-B4' ; use algorithm 2 and remove random dots jne .err or [bg_mode], 10b @1 libc.StartMain(Main); ; Start main() using libc (this is a macro!) .err: fprintf(stderr, "Invalid argument."\n \ "Accepted arguments: -B1 | -B2 | -B3 | -B4."\n); exit(-1); ; main() routine Main: push 0 push r13 push r12 push r15 push rbx push r14 push rbp ; Create a connection xcb_connect(NULL, NULL); test rax, rax jnz @f fprintf(stderr, "Could not connect to X server."\n); mov [rsp+48], byte 1 jmp .end @@ mov r15, rax ; Render 5 x 5 icon (with a centered, random colors 3 x 3 square) lea rdi, [dyn_icon] ; Generate random icon mov edx, 0_FF_000000h mov rax, 00000005_00000005h stosq xor eax, eax stosq stosq stosq mov rsi, rdi add rdi, 12 stosq add rdi, 12 stosq add rdi, 12 stosq stosq stosq mov rdi, rsi mov ecx, 3 @@ pause rdrand rax ; Avoiding AMD "no zero at size" problem jnc @b or eax, edx ; by only using 32 bit of 64-bit generated value stosd loop @b mov ecx, 3 add rdi, 8 @@ pause rdrand rax jnc @b or eax, edx stosd loop @b mov ecx, 3 add rdi, 8 @@ pause rdrand rax jnc @b or eax, edx stosd loop @b ; Iterate once to obtain current screen xcb_get_setup(r15); xcb_setup_roots_iterator(rax); mov r14, rax ; Find 32-bit, RGBA visual ID (if supported) @bss p.visual xq ? xcb_screen_allowed_depths_iterator(rax); push rdx push rax @@ mov r10, [rsp+xdi.data] cmp [r10+xdt.depth], 32 je @1f test [rsp+xdi.rem], -1 jz @f xcb_depth_next(rsp); jmp @b @@ fprintf(stderr, "Couldn't find 32-bit color depth support."\n); xcb_disconnect(r15); add rsp, 16 mov [rsp+48], byte 1 jmp Main.end @1 xcb_depth_visuals_iterator(r10); mov ebx, [rax+xvt.visual_id] ; visual_id; rax = *xcb_visualtype_t mov [p.visual], rax ; save this for Cairo surface add rsp, 16 ; Check support for MIT-SHM (copy to framebuffer rendering) xcb_get_extension_data(r15, &xcb_shm_id); test rax, rax jnz @f @2 fprintf(stderr, "Couldn't find MIT-SHM support."\n); xcb_disconnect(r15); jmp Main.end @@ test [rax+qer.present], -1 jz @2b @bss window xd ? @bss colormap xd ? xcb_generate_id(r15); mov [window], eax xcb_generate_id(r15); mov [colormap], eax ; Create atoms for window icon and window close request xcb_intern_atom(r15, FALSE, 12, "WM_PROTOCOLS"); mov ebp, eax xcb_intern_atom(r15, FALSE, 16, "WM_DELETE_WINDOW"); push rax push rbp xcb_intern_atom(r15, FALSE, 12, "_NET_WM_ICON"); mov ebp, eax ; Needed: create a new colormap for 32-bit mode colors xcb_create_colormap(r15, XCB_COLORMAP_ALLOC_NONE, colormap, [r14+scr.root], ebx); ; Configured window attributes mov edx, XCB_EVENT_MASK_EXPOSURE or XCB_EVENT_MASK_POINTER_MOTION mov eax, [colormap] mov rcx, 90200000_90200000h shl rax, 32 or rdx, rax push rdx push rcx mov r9, rsp ; Create main window xcb_create_window(r15, 32, window, [r14+scr.root], 0, 0, WIDTH, HEIGHT, 0, \ XCB_WINDOW_CLASS_INPUT_OUTPUT, ebx, XCB_CW_BACK_PIXEL or \ XCB_CW_BORDER_PIXEL or XCB_CW_COLORMAP or XCB_CW_EVENT_MASK, \ r9); ; Create child window, which will be a button xcb_generate_id(r15); mov r10d, [colormap] mov [button], eax mov [button.cmap], r10d xcb_create_window_checked(r15, XCB_COPY_FROM_PARENT, button, window, (WIDTH / 3) + 5, \ HEIGHT - (HEIGHT / 3), B_WIDTH, B_HEIGHT, 0, \ XCB_WINDOW_CLASS_INPUT_OUTPUT, ebx, XCB_CW_BACK_PIXEL or \ XCB_CW_COLORMAP or XCB_CW_OVERRIDE_REDIRECT or XCB_CW_EVENT_MASK, \ button.attr); xcb_request_check(r15, eax); test rax, rax jz @f movzx edx, [rax+xge.error_code] fprintf(stderr, "Fail to create button: %u"\n, edx); xcb_disconnect(r15); add rsp, 32 jmp Main.end ; Create child window, which will be a label @@ xcb_generate_id(r15); mov r11d, [colormap] mov [label], eax mov [label.cmap], r11d xcb_create_window_checked(r15, XCB_COPY_FROM_PARENT, label, window, 20, \ 20, L_WIDTH, L_HEIGHT, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, \ ebx, XCB_CW_BACK_PIXEL or XCB_CW_COLORMAP or \ XCB_CW_OVERRIDE_REDIRECT or XCB_CW_EVENT_MASK, \ label.attr); xcb_request_check(r15, eax); test rax, rax jz @f movzx edx, [rax+xge.error_code] fprintf(stderr, "Fail to create label: %u"\n, edx); xcb_disconnect(r15); add rsp, 32 jmp Main.end @@ add rsp, 16 ; Set minimum and maximum window sizes to be the same, so resizing window ; and maximize button are disabled (window with fixed size) xcb_icccm_size_hints_set_min_size(win_attr, WIDTH, HEIGHT); xcb_icccm_size_hints_set_max_size(win_attr, WIDTH, HEIGHT); mov [win_attr.flags], \ XCB_ICCCM_SIZE_HINT_P_MIN_SIZE or XCB_ICCCM_SIZE_HINT_P_MAX_SIZE xcb_icccm_set_wm_size_hints(r15, window, XCB_ATOM_WM_NORMAL_HINTS, win_attr); ; Gather replies from previously requested atoms xcb_intern_atom_reply(r15, ebp, NULL); mov r13, rax pop rbp pop rax xcb_intern_atom_reply(r15, eax, NULL); xchg rax, rbp xcb_intern_atom_reply(r15, eax, NULL); mov r14, rax ; Apply close event, icon, window title and iconified title to main window xcb_change_property(r15, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME, \ XCB_ATOM_STRING, 8, 27, "XCB Direct Framebuffer Demo"); xcb_change_property_checked(r15, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_ICON_NAME, \ XCB_ATOM_STRING, 8, 23, "XCB FB Demo - minimized"); xcb_request_check(r15, eax); test rax, rax jz @f movzx edx, [rax+xge.error_code] fprintf(stderr, "Error while applying iconified name: %u"\n, edx); @@ xcb_change_property(r15, XCB_PROP_MODE_REPLACE, window, [r13+art.atom], \ XCB_ATOM_CARDINAL, 32, dyn_icon.n, dyn_icon); xcb_change_property(r15, XCB_PROP_MODE_REPLACE, window, [r14+art.atom], \ XCB_ATOM_ATOM, 32, 1, rbp+art.atom); mov ebp, [rbp+art.atom] ; Create XCB graphic context, so we can render (copy) to framebuffer @bss gc xd ? xcb_generate_id(r15); mov [gc], eax xcb_create_gc(r15, eax, window, 0, NULL); ; Create and attach shared memory to be the manual render area @bss shmid xd ? shmget(IPC_PRIVATE, WIDTH * HEIGHT * 4, IPC_CREAT or 0666o); mov [shmid], eax shmat(eax, NULL, 0); mov r12, rax ; r12 = *framebuffer fprintf(stderr, "Copy to framebuffer at address: 0x%lX. Size: %u bytes."\n, \ rax, WIDTH * HEIGHT * 4); ; Attach it to XCB SHM, so it can be acccessed to the server-side (I guess) @bss shmsegid xd ? xcb_generate_id(r15); mov [shmsegid], eax xcb_shm_attach(r15, eax, shmid, 0); shmctl(shmid, IPC_RMID, NULL); ; Set remove on detach ; Render background algorithm 1 test [bg_mode], 1b jz @2f mov ecx, (WIDTH * HEIGHT) / 2 mov rdx, 0FF_000500_FF_000500h mov r10, 0FF_161514_FF_161514h mov rdi, r12 @@ pause rdrand rax or rax, rdx and rax, r10 stosq loop @b jmp @3f ; Render background algorithm 2 @2 mov rdi, r12 mov r10, 0_C6_17171C_C6_17171Ch mov r11, 0_FF_000916_FF_000916h mov r8, 0_1357_9BDF_2468_ACE0h rdtsc mov cl, ah shl rax, 32 or rdx, rax rol rax, cl xor r8, rdx mov ecx, WIDTH * HEIGHT / 2 @@ pause rdtsc shl rdx, 32 or rax, rdx xor r8, rax ror rax, cl xchg r8, rax and rax, r10 or rax, r11 stosq sfence loop @b ; Random dots (symmetric) blend mask @3 test [bg_mode], 10b jnz @4f mov ecx, WIDTH * HEIGHT @@ pause rdrand esi jnc @b shr esi, 10 cmp esi, WIDTH * HEIGHT jae @3f mov edx, 000_840210h mov r9d, [rsi*4+r12] shl r9, 2 add r9, rdx shr r9, 3 or r9d, 0FF_000000h and r9d, 0FF_77643Eh mov [rsi*4+r12], r9d neg rsi mov [r12-4+rsi*4+WIDTH*HEIGHT*4], r9d @3 loop @b @4 nop @bss button.surface xq ? @bss button.cr xq ? @bss ?label.surface xq ? @bss ?label.cr xq ? ; Setup cairo to render texts at our "widgets" cairo_xcb_surface_create(r15, button, p.visual, B_WIDTH, B_HEIGHT); mov [button.surface], rax cairo_create(rax); mov [button.cr], rax cairo_xcb_surface_create(r15, label, p.visual, L_WIDTH, L_HEIGHT); mov [label.surface], rax cairo_create(rax); mov [label.cr], rax cairo_select_font_face(rax, "Mono", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_select_font_face(button.cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(label.cr, 11.0); cairo_set_font_size(button.cr, 12.0); nop nop ; Map all windows, so they can be seen xcb_map_window(r15, window); xcb_map_window(r15, button); xcb_map_window(r15, label); xcb_flush(r15); ; Label window will be updated on every event loop iteration @1 xcb_clear_area(r15, FALSE, label, 0, 0, L_WIDTH, L_HEIGHT); cairo_move_to(label.cr, 3.0, 14.0); snprintf(buff1, 127, "Events: %u | Clicks: %u | Cursor x:%u y:%u", \ events, clicks, pointer.x, pointer.y); cairo_set_source_rgb(label.cr, 1.0, 1.0, 1.0); cairo_show_text(label.cr, buff1); xcb_flush(r15); ; Event loop idle catcher xcb_wait_for_event(r15); test rax, rax jz @1f inc [events] mov r14, rax mov ch, [rax+get.response_type] and ch, not 80h cmp ch, XCB_CLIENT_MESSAGE ; Close request will arrive as a client message jne @f cmp [r14+cme.data32+0*4], ebp ; WM_DELETE_WINDOW atom je @2f jmp @9f @@ cmp ch, XCB_EXPOSE ; Expose event (where to draw windows) jne @f mov r11d, [r14+eet.window] cmp r11d, [label] je @9f ; Do nothing for label, as we already update it every loop ; "Retext" button, because we are animating background using XCB native method @3 cmp r11d, [button] jne @3f cairo_set_source_rgb(button.cr, 1.0, 1.0, 1.0); cairo_move_to(button.cr, (B_WIDTH / 2.0) - 30.0, (B_HEIGHT / 2.0) + 6.0); cairo_show_text(button.cr, "Click me!"); xcb_flush(r15); jmp @9f ; Default expose: redraw window background by copying SHM memory @3 xcb_shm_put_image(r15, window, gc, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT, 0, 0, 32, \ XCB_IMAGE_FORMAT_Z_PIXMAP, 0, shmsegid, 0); xcb_flush(r15); jmp @9f @@ cmp ch, XCB_ENTER_NOTIFY ; Enter window event jne @f mov r11d, [r14+ene.event] ; Window ID cmp r11d, [button] jne @9f ; Animate button by making it clear whenever pointer enters its area xcb_change_window_attributes(r15, r11d, XCB_CW_BACK_PIXEL, button.bg_enter); xcb_clear_area(r15, TRUE, button, 0, 0, B_WIDTH, B_HEIGHT); xcb_flush(r15); jmp @9f @@ cmp ch, XCB_LEAVE_NOTIFY ; Leave window event jne @f mov r10d, [r14+lne.event] cmp r10d, [button] jne @9f ; Restore button background to previous whenever point leaves its area and [clicks.flag], not 11b ; Clear both flags xcb_change_window_attributes(r15, r10d, XCB_CW_BACK_PIXEL, button.attr); xcb_clear_area(r15, TRUE, button, 0, 0, B_WIDTH, B_HEIGHT); xcb_flush(r15); fprintf(stderr, <13,27,"[2K",0>); ; Reset information string at CLI jmp @9f @@ cmp ch, XCB_BUTTON_PRESS ; Mouse button down event jne @f cmp [r14+bpe.detail], 1 ; Selecting only main button jne @4f or [clicks.flag], 11b ; Map click event by: down followed by up ; Animate button (on down) by darkening its background xcb_change_window_attributes(r15, button, XCB_CW_BACK_PIXEL, button.bg_click); xcb_clear_area(r15, TRUE, button, 0, 0, B_WIDTH, B_HEIGHT); xcb_flush(r15); @rdata evtfmt: xb 13, '%s event: flags: %08B | X: %u | Y: %u ', 0 @4 movzx ecx, [r14+bpe.detail] movzx r8d, [r14+bpe.event_x] movzx r9d, [r14+bpe.event_y] fprintf(stderr, evtfmt, "Press ", ecx, r8d, r9d); jmp @9f @@ cmp ch, XCB_BUTTON_RELEASE ; Mouse button up event jne @f cmp [r14+bpe.detail], 1 ; Selecting only main button jne @4f btr [clicks+4], 0 ; if previously pressed, then 'click' jnc @5f inc [clicks] ; clicked handling here! ; Restore button background, so it seems to be released @5 xcb_change_window_attributes(r15, button, XCB_CW_BACK_PIXEL, button.bg_enter); xcb_clear_area(r15, TRUE, button, 0, 0, B_WIDTH, B_HEIGHT); xcb_flush(r15); @4 movzx ecx, [r14+bpe.detail] movzx r8d, [r14+bpe.event_x] movzx r9d, [r14+bpe.event_y] fprintf(stderr, evtfmt, "Release", ecx, r8d, r9d); or [clicks.flag], 10b ; Use bit 1 to sync line at terminal jmp @9f @@ cmp ch, XCB_MOTION_NOTIFY jne @f mov r9d, [window] cmp [r14+mne.event], r9d jne @f movzx ecx, [r14+mne.event_x] movzx edx, [r14+mne.event_y] mov [pointer.x], ecx mov [pointer.y], edx jmp @9f @@ @9 free(r14); ; Event structure must always be freed after processing it! jmp @1b @rdata farewell: xb 10, 'Graceful close request accepted, cleaning up and finishing...', \ 10, 0 ; Click window close button will bring it here, instead of abort @2 test [clicks.flag], 10b ; Check if it needs a newline char lea rdx, [farewell] lea rsi, [rdx+1] cmovnz rsi, rdx fprintf(stderr, rsi); ; Release all allocated and created stuff @1 cairo_destroy(button.cr); cairo_destroy(label.cr); cairo_surface_destroy(button.surface); cairo_surface_destroy(label.surface); xcb_destroy_window(r15, label); xcb_destroy_window(r15, button); xcb_destroy_window(r15, window); xcb_free_colormap(r15, colormap); xcb_shm_detach(r15, shmsegid); shmdt(r12); xcb_disconnect(r15); Main.end: pop rbp pop r14 pop rbx pop r15 pop r12 pop r13 pop rax ; Return previously pushed 0 value at beginning ret _bss align 16 dyn_icon: xd ?, ? .bitmap: xd *(5 * 5) .n = ($ - dyn_icon) / 4 win_attr XCB_SIZE_HINTS_T buff1: xb *128 _rdata align 16 _data align 16 events xd 0 clicks xd 0 .flag xb 0 bg_mode xb 0 button xd 0 .attr: .bgd xd 0FF_323232h .ovr xd 0 .evt xd XCB_EVENT_MASK_EXPOSURE or \ XCB_EVENT_MASK_ENTER_WINDOW or \ XCB_EVENT_MASK_LEAVE_WINDOW or \ XCB_EVENT_MASK_BUTTON_PRESS or \ XCB_EVENT_MASK_BUTTON_RELEASE .cmap xd 0 .bg_enter: xd 0FF_4C4C4Ch .bg_click: xd 0FF_232620h ?label xd 0 ; Using ?label to not conflict with fasmg 'label' keyword .attr: .bgd xd 0FF_323232h .ovr xd 0 .evt xd XCB_EVENT_MASK_EXPOSURE .cmap xd 0 pointer: .x xd 0 .y xd 0 Written at my current comfort zone syntax, but I know you guys can easily convert it (if you want), because you are amazing assembly code writers, and probably (like me) amazing C code interpreters... An important point I need to share is: for this to work with Wayland, you need to define XCB_CW_BORDER_PIXEL to the main window attributes even when not using a border at all: at XLibre (I suppose Xorg too), it isn't needed to define a border color for a border you don't have control (the compositor controls it, at least with plasma desktop). Tested on XLibre and on Wayland, as I am running it on Artix Linux with Plasma desktop. Any suggestions are welcome, as well as a Wayland-based code doing the same stuff (if one can really do it in assembly; by the way I'm currently researching do stuff with Wayland, too), so we can actually compare X with Wayland, regarding raw window management and performance, based on measurable facts, not only emotions about one favorite window system. So, to simplify: code snippets (that actually work) are warmly welcome, speculation about 'you should use this or that', not. By the way, I'll let for you guys to uncover that cool (or not) software-generated background I've talked above, without spoiling it with prints (again, if you want). Cheers,
|
|||||||||||
|
|
Jessé 15 Aug 2026, 10:29
I did another example, this one less "graphical", but with an important concept, which is one of the key points on using 'libxcb' according to their own statement: its multithreading capabilities.
What you'll see at this, can never, even in one wildest dreams, be done with GTK, for example. Every label you see at the window, is created, rendered, updated and destroyed by its own thread: everything in parallel, and completely async (as one can see by a random value to be the sleep time at the loop): Code: format ELF64 include 'fastcall3.inc' include 'stdio.inc' include 'xcb.inc' include 'xcb-icccm.inc' include 'cairo.inc' define_offset ar XCB_INTERN_ATOM_REPLY_T define_offset cm XCB_CLIENT_MESSAGE_EVENT_T define_offset s XCB_SCREEN_T define_offset ee XCB_EXPOSE_EVENT_T define_offset e XCB_GENERIC_ERROR_T define_offset ge XCB_GENERIC_EVENT_T _code Start entry: libc.StartMain(Main); Thread_label: push 0 ; ¹ push rbx push r14 push r12 push r15 push rbp push r13 mov ebx, edi ; ² received parameter from creator ; Every thread creates, updates and destroys its own label window! xcb_generate_id(connection); lea r13, [label] mov [r13+rbx*4], eax imul r10d, ebx, 30 add r10d, 10 mov r8, [screen] xcb_create_window_checked(connection, XCB_COPY_FROM_PARENT, eax, window, 10, \ r10w, 380, 20, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, \ [r8+s.root_visual], label_n.mask, label_n); xcb_request_check(connection, eax); test rax, rax jz @f movzx ecx, [rax+e.error_code] fprintf(stderr, "Thread %u failed to create label with error code: %u"\n, ebx+1, ecx); mov [rsp+48], byte 1 jmp .end @@ xcb_map_window(connection, [r13+rbx*4]); xcb_flush(connection); cairo_xcb_surface_create(connection, [r13+rbx*4], ptr_visual, 380, 20); mov r12, rax cairo_create(rax); mov r14, rax cairo_select_font_face(rax, "Mono", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(r14, 11.0); cairo_set_source_rgb(r14, 1.0, 1.0, 1.0); sub rsp, 128 call .draw_ACK xor ebp, ebp ; Generate a unique value to be the wait time @@ pause rdrand r15d jnc @b and r15d, 07_FFFFh inc r15d ; Counter update loop @1 lea r9d, [ebx+4] ; Check thread update flag based on lock btr [p.flags], r9d ; received parameter at start (²) jnc @f call .draw_ACK fprintf(stderr, "Label %u fully redrawn."\n, ebx+1); @@ xcb_clear_area(connection, FALSE, [r13+rbx*4], 224, 0, 380 - 224, 20); snprintf(rsp, 127, "Counter %u: %010u", ebx, ebp); cairo_move_to(r14, 224.0, 14.0); cairo_show_text(r14, rsp); xcb_flush(connection); inc ebp usleep(r15d); test [flags], 1 jz @1b .finish: add rsp, 128 fprintf(stderr, "Thread %u is about to exit now..."\n, ebx+1); cairo_destroy(r14); cairo_surface_destroy(r12); xcb_destroy_window(connection, [r13+rbx*4]); xcb_flush(connection); .end: pop r13 pop rbp pop r15 pop r12 pop r14 pop rbx pop rax ; ¹ pthread return value is a qword! ret ; Redraw acknowledge message whenever needed, so it shall not get lost ; by repaint events .draw_ACK: sub rsp, 8 xcb_clear_area(connection, FALSE, [r13+rbx*4], 0, 0, 180, 20); cairo_move_to(r14, 8.0, 14.0); snprintf(rsp+16, 127, "Thread %u here!", ebx+1); cairo_show_text(r14, rsp+16); xcb_flush(connection); add rsp, 8 ret Main: push 0 push rbx push rbp sub rsp, 32 xcb_connect(NULL, NULL); test rax, rax jnz @f fprintf(stderr, "Failed to connect to X server."\n); mov [rsp+48], byte 1 jmp .end ; Working with standard, 24-bit RGB mode this time @@ mov rbp, rax ; connection mov [connection], rax xcb_get_setup(rax); xcb_setup_roots_iterator(rax); mov rbx, rax ; screen mov [screen], rax xcb_screen_allowed_depths_iterator(rax); xcb_depth_visuals_iterator(rax); mov [ptr_visual], rax ; pointer to structure for Cairo parameter xcb_generate_id(rbp); mov [window], eax @rdata .wm_proto: xb 'WM_PROTOCOLS' @rdata .wmp_size = $ - .wm_proto @rdata .wm_delete: xb 'WM_DELETE_WINDOW' @rdata .wmd_size = $ - .wm_delete xcb_intern_atom(rbp, FALSE, .wmp_size, .wm_proto); mov [rsp], eax xcb_intern_atom(rbp, FALSE, .wmd_size, .wm_delete); mov [rsp+8], rax xcb_create_window_checked(rbp, XCB_COPY_FROM_PARENT, window, [rbx+s.root], 0, 0, \ 400, 130, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, \ [rbx+s.root_visual], XCB_CW_BACK_PIXEL, &window.bg); xcb_request_check(rbp, eax); test rax, rax jz @f movzx edx, [rax+e.error_code] fprintf(stderr, "Couldn't create window: %u"\n, edx); xcb_disconnect(rbp); mov [rsp+48], byte 2 jmp .end @@ xcb_change_property(rbp, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME, \ XCB_ATOM_STRING, 8, 28, "Multithread with XCB example"); mov [window.prop.flags], \ XCB_ICCCM_SIZE_HINT_P_MIN_SIZE or XCB_ICCCM_SIZE_HINT_P_MAX_SIZE xcb_icccm_size_hints_set_min_size(window.prop, 400, 130); xcb_icccm_size_hints_set_max_size(window.prop, 400, 130); xcb_icccm_set_wm_size_hints(rbp, window, XCB_ATOM_WM_NORMAL_HINTS, window.prop); xcb_intern_atom_reply(rbp, [rsp], NULL); mov [rsp], rax xcb_intern_atom_reply(rbp, [rsp+8], NULL); mov [rsp+8], rax mov rcx, [rsp] mov edx, [rax+ar.atom] mov [rsp+16], edx xcb_change_property(rbp, XCB_PROP_MODE_REPLACE, window, [rcx+ar.atom], \ XCB_ATOM_ATOM, 32, 1, rax+ar.atom); free([rsp]); ; Requested atom structures need to be freed after usage! free([rsp+8]); ; xcb_map_window(rbp, window); xcb_flush(rbp); prefetcht2 [flags] prefetcht2 byte [window] prefetcht2 [label - 16] prefetcht2 [label_n - 4] pthread_create(&thread.1, NULL, Thread_label, 0); pthread_create(&thread.2, NULL, Thread_label, 1); pthread_create(&thread.3, NULL, Thread_label, 2); pthread_create(&thread.4, NULL, Thread_label, 3); ; Events loop (all windows' events are gathered here) .poll: xcb_wait_for_event(rbp); test rax, rax jz .exit mov rbx, rax mov dl, [rax+ge.response_type] and dl, 7Fh cmp dl, XCB_EXPOSE ; Expose event jne @f mov r10d, [rax+ee.window] mov cl, [flags] mov r9b, 1000_0000b mov al, 0100_0000b mov dl, 0010_0000b mov r8b, 0001_0000b or r9d, ecx or eax, ecx or edx, ecx or r8d, ecx cmp r10d, [label.1] ; Do nothing about the label windows: cmove ecx, r8d cmp r10d, [label.2] ; only sign a flag for their respective cmove ecx, edx cmp r10d, [label.3] ; thread to do something, like draw cmove ecx, eax cmp r10d, [label.4] ; the acknowledge message again cmove ecx, r9d xchg cl, [flags] jmp .default @@ cmp dl, XCB_CLIENT_MESSAGE jne .default mov eax, [rax+cm.data32+0*4] cmp eax, [rsp+16] je .graceful .default: free(rbx); jmp .poll .graceful: free(rbx); .exit: lock or [flags], 1 pthread_join(thread.1, NULL); pthread_join(thread.2, NULL); pthread_join(thread.3, NULL); pthread_join(thread.4, NULL); xcb_destroy_window(rbp, window); xcb_flush(rbp); xcb_disconnect(rbp); .end: add rsp, 32 pop rbp pop rbx pop rax ret _rdata window.bg dd 00_11_11_11h label_n: .bg dd 00_18_18_18h .ev dd XCB_EVENT_MASK_EXPOSURE .mask = XCB_CW_BACK_PIXEL or XCB_CW_EVENT_MASK ; constant property, not data _data p.flags: flags db 0 _bss window dd ? .prop XCB_SIZE_HINTS_T align 64 connection dq ? screen dq ? ptr_visual dq ? ?label: .1 dd ? .2 dd ? .3 dd ? .4 dd ? align 8 thread: .1 dq ? .2 dq ? .3 dq ? .4 dq ? One can easily state the value of handling GUI with multithreading, instead of overloading a single CPU with all graphical tasks (GTK, I'm looking at you!). So, sometimes (always, tbh), lowering the API/ABI level can be much more advantageous, regarding performance. Apparently, this also can't be done with Xlib, but I not even try to test if it works. Let me know if I'm wrong on this statement.
|
|||||||||||
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2026, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.