From c5b2970fe90beb79cdb7af4601fdd91975eae80e Mon Sep 17 00:00:00 2001 From: Frater <13979047+saintfrater@users.noreply.github.com> Date: Thu, 5 Feb 2026 16:59:41 +0100 Subject: [PATCH] fixing button --- src/boot.asm | 18 +- src/drivers/gfx_cgam.asm | 147 +++--- src/drivers/mouse_ps2.asm | 9 +- src/services/gui_lib.asm | 909 +++++++++++++++++++++----------------- 4 files changed, 612 insertions(+), 471 deletions(-) diff --git a/src/boot.asm b/src/boot.asm index 01881cf..77741bc 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -135,10 +135,10 @@ build_interface: mov word [gs:si + widget.w], 80 mov word [gs:si + widget.h], 20 mov word [gs:si + widget.text_ofs], str_quit - mov byte [gs:si + widget.user_id], 1 ; Style "Bouton par défaut" (OK) + ; mov byte [gs:si + widget.user_id], 1 ; Style "Bouton par défaut" (OK) mov word [gs:si + widget.text_seg], cs ; Texte est dans la ROM mov word [gs:si + widget.event_click], on_click_quit ; Fonction à appeler - mov byte [gs:si + widget.type], WIDGET_TYPE_BUTTON + mov byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON ; Créer Bouton 2 "HELLO" call gui_alloc_widget @@ -150,7 +150,19 @@ build_interface: mov word [gs:si + widget.h], 20 mov word [gs:si + widget.text_ofs], str_hello mov word [gs:si + widget.text_seg], cs - mov byte [gs:si + widget.type], WIDGET_TYPE_BUTTON + mov byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON + + ; Créer Bouton 2 "HELLO" + call gui_alloc_widget + jc .mem_full + + mov word [gs:si + widget.x], 100 + mov word [gs:si + widget.y], 150 + mov word [gs:si + widget.w], 80 + mov word [gs:si + widget.h], 20 + mov word [gs:si + widget.text_ofs], str_hello + mov word [gs:si + widget.text_seg], cs + mov byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON ; Pas de callback ; Créer Slider (Drag) diff --git a/src/drivers/gfx_cgam.asm b/src/drivers/gfx_cgam.asm index b7b6e66..7ceb11d 100644 --- a/src/drivers/gfx_cgam.asm +++ b/src/drivers/gfx_cgam.asm @@ -73,7 +73,7 @@ %define WRITE 6 %define LINE_VERT 7 %define LINE_HORIZ 8 -%define RECTANGLE_DRAW 9 +%define RECTANGLE 9 %define RECTANGLE_FILL 10 %define RECTANGLE_ROUND 11 %define MOUSE_HIDE 12 @@ -90,7 +90,7 @@ ; Si vous changez le code du driver, tant que la table au début ; ne change pas d'ordre, la GUI n'a pas besoin d'être recompilée. ; ------------------------------------------------------------ - +align 2 graph_driver: dw cga_init ; init de la carte graphique dw cga_putpixel ; dessin d'un pixel @@ -107,26 +107,28 @@ graph_driver: dw cga_mouse_hide ; déplacement du curseur dw cga_mouse_show ; déplacement du curseur dw cga_mouse_cursor_move ; déplacement du curseur -; -; convert al -> AX aligned with "cl" -%macro ALING_BYTE 0 - mov ah,al - xor al,al - shr ax,cl - - xchg ah,al -%endmacro ; ============================================================================= ; SECTION : PATTERNS & FILL +; https://paulsmith.github.io/classic-mac-patterns/ ; ============================================================================= -align 8 -pattern_black dq 0x0000000000000000 -pattern_gray_dark dq 0xDDFF77FFDDFF77FF -pattern_gray_mid dq 0xDD77DD77DD77DD77 -pattern_gray_light dq 0xAA55AA55AA55AA55 -pattern_white_light dq 0x8800220088002200 -pattern_white dq 0xFFFFFFFFFFFFFFFF +; +; pattern ID +%define PATTERN_BLACK 0 +%define PATTERN_GRAY_DARK 1 +%define PATTERN_GRAY_MID 2 +%define PATTERN_GRAY_LIGHT 3 +%define PATTERN_WHITE_LIGHT 4 +%define PATTERN_WHITE 5 + +align 8 +pattern_8x8: + dq 0x0000000000000000 ; pattern_black (Tout à 0 = Noir) + dq 0x2200880022008800 ; pattern_gray_dark (Majorité de 0) + dq 0x2288228822882288 ; pattern_gray_mid + dq 0xAA55AA55AA55AA55 ; pattern_gray_light (50/50) + dq 0x77DD77DD77DD77DD ; pattern_white_light (Majorité de 1 = Clair) + dq 0xFFFFFFFFFFFFFFFF ; pattern_white (Tout à 1 = Blanc) ; ------------------------------------------------------------ ; initialise le mode graphique (via l'int 10h) @@ -141,7 +143,6 @@ cga_init: mov ah, 0x00 ; AH=00h set video mode mov al, GFX_MODE int 0x10 - mov byte [BDA_MOUSE + mouse.bkg_saved],0 ; flag image saved ; mov byte [BDA_GFX + gfx.cur_mode], 1 ; dessine un background "check-board" @@ -184,6 +185,7 @@ cga_calc_addr: shr ah, cl ret + ; --------------------------------------------------------------------------- ; gfx_set_writemode (mode) ; Défini le mode d'écriture : @@ -311,6 +313,14 @@ get_glyph_offset: ; - x non aligné (x&7 != 0) ; - écrit sur 2 bytes (di et di+1) dans chaque banque ; --------------------------------------------------------------------------- +; convert al -> AX aligned with "cl" +%macro ALING_BYTE 0 + mov ah,al + xor al,al + shr ax,cl + xchg ah,al +%endmacro + cga_putc: push bp mov bp, sp @@ -466,6 +476,10 @@ cga_putpixel: push bp mov bp, sp pusha + + mov ax, VIDEO_SEG + mov es, ax + call cga_mouse_hide ; Protection souris mov cx, .x mov dx, .y @@ -513,7 +527,10 @@ cga_getpixel: mov cx, .x mov dx, .y + mov ax, VIDEO_SEG + mov es, ax call cga_calc_addr + mov al, [es:di] and al, ah setnz al @@ -839,21 +856,29 @@ cga_draw_rect: %define .y1 word [bp+6] %define .x2 word [bp+8] %define .y2 word [bp+10] -%define .pat word [bp+12] +%define .pat_id word [bp+12] ; Variable locale pour l'index du motif (y % 8) -%define .pat_idx word [bp-2] +%define .pat_idx word [bp-2] +%define .x_end_idx word [bp-4] +%define .pattern_offset word [bp-6] cga_fill_rect: push bp mov bp, sp - sub sp, 2 ; Reserve espace pour .pat_idx + sub sp, 6 ; Reserve espace pour .pat_idx et .x_end_idx pusha push es mov ax, VIDEO_SEG mov es, ax + ; calcul de l'offset de départ du pattern + mov ax, .pat_id + shl ax, 3 ; * 8 + add ax, pattern_8x8 + mov .pattern_offset, ax + call cga_mouse_hide ; --- 1. Tri des coordonnées (X et Y) --- @@ -893,6 +918,7 @@ cga_fill_rect: mov cx, .x1 mov dx, .y1 + ; Note: cga_calc_addr attend CX=x, DX=y et retourne DI ; Si cga_calc_addr modifie BX, la sauvegarde [STACK A] est vitale. call cga_calc_addr ; DI = Offset début ligne @@ -917,6 +943,7 @@ cga_fill_rect: shr si, 3 ; SI = Index start byte mov cx, .x2 shr cx, 3 ; CX = Index end byte + mov .x_end_idx, cx ; Sauvegarde de l'index de fin pour la boucle horizontale ; --- 5. Récupérer la hauteur dans un registre sûr --- pop bx ; RESTAURER HAUTEUR DANS BX [STACK A] @@ -930,16 +957,13 @@ cga_fill_rect: push bx mov bx, .pat_idx push si - mov si, .pat + mov si, .pattern_offset mov dl, [cs:si + bx] ; DL = Pattern Byte pop si pop bx - ; Incrémenter index motif pour la prochaine ligne - inc word .pat_idx - and word .pat_idx, 7 - ; --- Remplissage Horizontal --- + mov cx, .x_end_idx ; Restaurer l'index de fin pour la ligne ; Cas Single Byte (Start == End) cmp si, cx @@ -975,6 +999,10 @@ cga_fill_rect: pop si pop di + ; Incrémenter index motif pour la prochaine ligne + inc word .pat_idx + and word .pat_idx, 7 + ; --- Passage ligne suivante (CGA Bank Switch) --- xor di, 0x2000 test di, 0x2000 @@ -1008,7 +1036,6 @@ cga_fill_rect: %undef .y2 %undef .pat_idx - ; ------------------------------------------------------------ ; cga_draw_rounded_frame ; dessiner un cadre arrondi @@ -1024,9 +1051,9 @@ cga_draw_rounded_frame: mov bp, sp pusha - ; call cga_mouse_hide + call cga_mouse_hide - ; Lignes horizontales (raccourcies de 2px de chaque côté) + ; Lignes horizontales (raccourcies de 2px pour laisser place à l'arrondi) mov ax, .x1 add ax, 2 mov bx, .x2 @@ -1034,7 +1061,7 @@ cga_draw_rounded_frame: GFX LINE_HORIZ, ax, bx, .y1, .color GFX LINE_HORIZ, ax, bx, .y2, .color - ; Lignes verticales (raccourcies de 2px) + ; Lignes verticales (raccourcies de 1px en haut et en bas) mov cx, .y1 add cx, 2 mov dx, .y2 @@ -1042,23 +1069,32 @@ cga_draw_rounded_frame: GFX LINE_VERT, .x1, cx, dx, .color GFX LINE_VERT, .x2, cx, dx, .color - ; Pixels de coins pour l'arrondi + ; Ajout des pixels de transition pour adoucir les coins (chanfrein) + mov ax, .x1 + inc ax ; x1 + 1 + mov bx, .y1 + inc bx ; y1 + 1 + GFX PUTPIXEL, ax, bx, .color ; Coin haut-gauche + mov ax, .x1 inc ax + mov bx, .y2 + dec bx ; y2 - 1 + GFX PUTPIXEL, ax, bx, .color ; Coin bas-gauche + + mov ax, .x2 + dec ax ; x2 - 1 mov bx, .y1 - inc bx - GFX PUTPIXEL, ax, bx, .color ; top left + inc bx ; y1 + 1 + GFX PUTPIXEL, ax, bx, .color ; Coin haut-droite + mov ax, .x2 dec ax - GFX PUTPIXEL, ax, bx, .color ; Top-right mov bx, .y2 - dec bx - GFX PUTPIXEL, ax, bx, .color ; bottom-right - mov ax, .x1 - inc ax - GFX PUTPIXEL, ax, bx, .color ; bottom-left + dec bx ; y2 - 1 + GFX PUTPIXEL, ax, bx, .color ; Coin bas-droite -; call cga_mouse_show + call cga_mouse_show popa leave @@ -1082,7 +1118,7 @@ cga_draw_rounded_frame: cga_mouse_hide: pushf ; Sauver l'état des flags (interrupts) cli ; Désactiver les interruptions (CRITIQUE) - push ax + pushad ; Sauvegarder TOUS les registres 32-bits push ds mov ax, BDA_DATA_SEG @@ -1095,14 +1131,11 @@ cga_mouse_hide: cmp byte [BDA_MOUSE + mouse.cur_counter], -1 jne .skip_restore ; Si on est à -2, -3... elle est déjà cachée - ; C'est la transition Visible -> Caché : On efface le curseur call cga_cursor_restorebg - ; On marque qu'on ne dessine plus - mov byte [BDA_MOUSE + mouse.bkg_saved], 0 .skip_restore: pop ds - pop ax + popad popf ; Restaure les interruptions (STI si elles étaient là) ret @@ -1113,7 +1146,7 @@ cga_mouse_hide: cga_mouse_show: pushf cli - push ax + pushad push ds mov ax, BDA_DATA_SEG @@ -1133,7 +1166,7 @@ cga_mouse_show: .skip_draw: pop ds - pop ax + popad popf ret @@ -1143,7 +1176,7 @@ cga_mouse_show: ; ; ------------------------------------------------------------ cga_mouse_cursor_move: - push ax + pushad push ds push es @@ -1171,7 +1204,7 @@ cga_mouse_cursor_move: .done: pop es pop ds - pop ax + popad ret ; ------------------------------------------------------------ @@ -1183,7 +1216,9 @@ cga_cursor_savebg: cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 ; le buffer n'a pas encore été restauré jne .done - mov byte [BDA_MOUSE + mouse.bkg_saved], 1 + push es + mov ax, VIDEO_SEG + mov es, ax ; mémoriser position courante (utilisée pour restore) mov cx, [BDA_MOUSE + mouse.x] @@ -1213,7 +1248,10 @@ cga_cursor_savebg: .next_line: loop .row_loop + pop es + .done: + mov byte [BDA_MOUSE + mouse.bkg_saved], 1 ; Flag mis à jour après la copie ret ; ------------------------------------------------------------ @@ -1224,7 +1262,9 @@ cga_cursor_restorebg: cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 je .done - mov byte [BDA_MOUSE + mouse.bkg_saved], 0 + push es + mov ax, VIDEO_SEG + mov es, ax ; restaurer depuis la dernière position sauvegardée mov di, [BDA_MOUSE + mouse.cur_addr_start] @@ -1245,7 +1285,9 @@ cga_cursor_restorebg: .next_line: loop .row_loop + pop es .done: + mov byte [BDA_MOUSE + mouse.bkg_saved], 0 ; Flag mis à jour après la restauration ret ; ============================================================ @@ -1405,4 +1447,3 @@ cga_cursor_draw: pop ds popad ret - diff --git a/src/drivers/mouse_ps2.asm b/src/drivers/mouse_ps2.asm index 7a4be8b..6396a03 100644 --- a/src/drivers/mouse_ps2.asm +++ b/src/drivers/mouse_ps2.asm @@ -55,10 +55,13 @@ mouse_reset: mov word [fs:BDA_MOUSE + mouse.x],320 ; vous pouvez aussi préciser le centre mov word [fs:BDA_MOUSE + mouse.y],100 ; vous pouvez aussi préciser le centre - mov byte [fs:BDA_MOUSE + mouse.cur_counter], 0 mov word [fs:BDA_MOUSE + mouse.cur_x], 0 mov word [fs:BDA_MOUSE + mouse.cur_y], 0 - mov byte [fs:BDA_MOUSE + mouse.cur_drawing], 0 + + + mov byte [BDA_MOUSE + mouse.cur_counter], -1 ; Force l'état caché au départ + mov byte [BDA_MOUSE + mouse.cur_drawing], 0 ; Reset du verrou de dessin + mov byte [BDA_MOUSE + mouse.bkg_saved],0 ; flag image saved mov ax, cs mov word [fs:BDA_MOUSE + mouse.cur_seg], ax @@ -312,7 +315,7 @@ isr_mouse_handler: ; --- BOUTONS --- mov al,bl ; extraction des boutons - and al, 00000111b ; Bits 0, 1, 2 = Gauche, Droite, Milieu + and al, 00000111b ; Bits 0, 1, 2 = Gauche, Droite, Milieu mov [BDA_MOUSE + mouse.status], al xor ax,ax diff --git a/src/services/gui_lib.asm b/src/services/gui_lib.asm index 2f3684b..3aaf471 100644 --- a/src/services/gui_lib.asm +++ b/src/services/gui_lib.asm @@ -32,37 +32,43 @@ %define GUI_STATE_DISABLED 4 ; Grisé ; --- Types de Widgets --- -%define WIDGET_TYPE_BUTTON 0 -%define WIDGET_TYPE_SLIDER 1 -%define WIDGET_TYPE_LABEL 2 +%define WIDGET_TYPE_BUTTON 0 +%define WIDGET_TYPE_SLIDER 1 +%define WIDGET_TYPE_LABEL 2 +%define WIDGET_TYPE_ROUND_BUTTON 3 + +; --- Widgets attributs --- +%define BUTTON_OK 1 ; --- Structure d'un OBJET (Bouton, etc) --- struc widget - .state resb 1 ; État (0=libre, >0=utilisé) - .type resb 1 ; Type (0=Button, 1=Slider...) - .oldstate resb 1 ; widget a-t-il été modifié ? - .user_id resb 1 ; ID unique utilisateur - .x resw 1 ; Position X - .y resw 1 ; Position Y - .w resw 1 ; Largeur - .h resw 1 ; Hauteur - .text_ofs resw 1 ; Offset du texte - .text_seg resw 1 ; Segment du texte - .event_click resw 1 ; adresse de la fonction "on click" + .state resb 1 ; État (0=libre, >0=utilisé) + .type resb 1 ; Type (0=Button, 1=Slider...) + .oldstate resb 1 ; widget a-t-il été modifié ? + .user_id resb 1 ; ID unique utilisateur + .x resw 1 ; Position X + .y resw 1 ; Position Y + .x2 resw 1 ; Position X2 calculée + .y2 resw 1 ; Position Y2 calculée + .w resw 1 ; Largeur + .h resw 1 ; Hauteur + .text_ofs resw 1 ; Offset du texte + .text_seg resw 1 ; Segment du texte + .event_click resw 1 ; adresse de la fonction "on click" ; .event_hover resw 1 ; adresse de la fonction "on hover" ; .event_release resw 1 ; adresse de la fonction "on release" ; .event_disable resw 1 ; adresse de la fonction "on disable" ; .event_enable resw 1 ; adresse de la fonction "on enable" - .event_drag resw 1 ; adresse de la fonction "on drag" + .event_drag resw 1 ; adresse de la fonction "on drag" - .attr_mode resb 1 ; 0=None, 1=Horiz, 2=Vert - .attr_min resw 1 ; Valeur/Position Min - .attr_max resw 1 ; Valeur/Position Max - .attr_val resw 1 ; Valeur/Position Actuelle - .attr_anchor resw 1 ; Offset interne pour le drag - .thumb_pct resb 1 ; Taille du curseur en % (1-100) + .attr_mode resb 1 ; 0=None, 1=Horiz, 2=Vert + .attr_min resw 1 ; Valeur/Position Min + .attr_max resw 1 ; Valeur/Position Max + .attr_val resw 1 ; Valeur/Position Actuelle + .attr_anchor resw 1 ; Offset interne pour le drag + .thumb_pct resb 1 ; Taille du curseur en % (1-100) - alignb 2 ; Alignement mémoire pour performance + alignb 2 ; Alignement mémoire pour performance endstruc ; ============================================================================= @@ -75,24 +81,24 @@ endstruc ; Entrée : DS doit pointer vers GUI_RAM_SEG ; ----------------------------------------------------------------------------- gui_init_system: - push eax - push cx - push es - push di + push eax + push cx + push es + push di - mov ax, GUI_RAM_SEG - mov es, ax + mov ax, GUI_RAM_SEG + mov es, ax - xor di, di - mov cx, (widget_size * GUI_MAX_WIDGETS) / 2 - xor ax, ax - rep stosw ; Plus sûr en mode 16 bits pour l'alignement + xor di, di + mov cx, (widget_size * GUI_MAX_WIDGETS) / 2 + xor ax, ax + rep stosw ; Plus sûr en mode 16 bits pour l'alignement - pop di - pop es - pop cx - pop eax - ret + pop di + pop es + pop cx + pop eax + ret ; ----------------------------------------------------------------------------- ; gui_alloc_widget @@ -102,44 +108,44 @@ gui_init_system: ; - Echec : CF=1 (Carry Set), GS:SI = Indéfini (ou 0) ; ----------------------------------------------------------------------------- gui_alloc_widget: - push ax - push cx - push bx + push ax + push cx + push bx - mov ax, GUI_RAM_SEG - mov gs, ax + mov ax, GUI_RAM_SEG + mov gs, ax - mov si, 0 ; Début du pool - mov cx, GUI_MAX_WIDGETS + mov si, 0 ; Début du pool + mov cx, GUI_MAX_WIDGETS .loop_find: - cmp byte [gs:si + widget.state], GUI_STATE_FREE - je .found ; Si state == 0, c'est libre ! + cmp byte [gs:si + widget.state], GUI_STATE_FREE + je .found ; Si state == 0, c'est libre ! - add si, widget_size ; Sinon on passe au suivant - loop .loop_find + add si, widget_size ; Sinon on passe au suivant + loop .loop_find - ; Pas trouvé (Plein) - stc ; set Carry Flag : erreur - jmp .done + ; Pas trouvé (Plein) + stc ; set Carry Flag : erreur + jmp .done .found: - ; On initialise le slot trouvé - mov byte [gs:si + widget.state], GUI_STATE_NORMAL ; Marquer comme occupé - mov byte [gs:si + widget.type], WIDGET_TYPE_BUTTON ; Type par défaut - mov byte [gs:si + widget.oldstate], 0 ; doit etre dessiné + ; On initialise le slot trouvé + mov byte [gs:si + widget.state], GUI_STATE_NORMAL ; Marquer comme occupé + mov byte [gs:si + widget.type], WIDGET_TYPE_BUTTON ; Type par défaut + mov byte [gs:si + widget.oldstate], 0 ; doit etre dessiné - ; Reset des champs critiques pour éviter les déchets - mov word [gs:si + widget.event_click], 0 - mov word [gs:si + widget.event_drag], 0 - mov byte [gs:si + widget.thumb_pct], 10 ; 10% par défaut + ; Reset des champs critiques pour éviter les déchets + mov word [gs:si + widget.event_click], 0 + mov word [gs:si + widget.event_drag], 0 + mov byte [gs:si + widget.thumb_pct], 10 ; 10% par défaut - clc ; Clear Carry Flag + clc ; Clear Carry Flag .done: - pop bx - pop cx - pop ax - ret + pop bx + pop cx + pop ax + ret ; ----------------------------------------------------------------------------- ; gui_free_widget @@ -147,25 +153,25 @@ gui_alloc_widget: ; Entrée : SI = Pointeur widget ; ----------------------------------------------------------------------------- gui_free_widget: - push eax - push es + push eax + push es - mov ax, GUI_RAM_SEG - mov es, ax + mov ax, GUI_RAM_SEG + mov es, ax - mov di, si - mov cx, (widget_size) - shr cx, 2 ; cx / 4 - xor eax, eax - rep stosd ; Remplit tout de 0 + mov di, si + mov cx, (widget_size) + shr cx, 2 ; cx / 4 + xor eax, eax + rep stosd ; Remplit tout de 0 - mov byte [es:si + widget.state], GUI_STATE_FREE - ; On efface visuellement le widget (optionnel, remplit de blanc) - ; Pour l'instant on le marque juste libre, il sera redessiné par-dessus. + mov byte [es:si + widget.state], GUI_STATE_FREE + ; On efface visuellement le widget (optionnel, remplit de blanc) + ; Pour l'instant on le marque juste libre, il sera redessiné par-dessus. - pop es - pop eax - ret + pop es + pop eax + ret ; ============================================================================= ; SECTION : MOTEUR D'ÉVÉNEMENTS (EVENT LOOP) @@ -181,67 +187,67 @@ gui_free_widget: ; BL = Mouse Buttons ; ----------------------------------------------------------------------------- gui_process_all: - pusha - push ds - push gs + pusha + push ds + push gs - mov ax, GUI_RAM_SEG - mov gs, ax + mov ax, GUI_RAM_SEG + mov gs, ax - ; Charger l'état de la souris pour toute la passe - push ds - mov ax, BDA_DATA_SEG - mov ds, ax - mov bl, [BDA_MOUSE + mouse.status] - pop ds + ; Charger l'état de la souris pour toute la passe + push ds + mov ax, BDA_DATA_SEG + mov ds, ax + mov bl, [BDA_MOUSE + mouse.status] + pop ds - mov si, 0 ; Pointeur début tableau - mov di, GUI_MAX_WIDGETS ; Compteur + mov si, 0 ; Pointeur début tableau + mov di, GUI_MAX_WIDGETS ; Compteur - .loop_widgets: - ; Est-ce que ce slot est occupé ? - cmp byte [gs:si + widget.state], GUI_STATE_FREE - je .next_widget + .loop_widgets: + ; Est-ce que ce slot est occupé ? + cmp byte [gs:si + widget.state], GUI_STATE_FREE + je .next_widget - ; --- Logique Widget --- - push bx ; Sauver état boutons souris - call gui_update_logic ; Vérifier collision/clic + ; --- Logique Widget --- + push bx ; Sauver état boutons souris + call gui_update_logic ; Vérifier collision/clic - ; Si AL=1 (Clic relâché validé), exécuter le callback - cmp al, 1 - jne .no_action + ; Si AL=1 (Clic relâché validé), exécuter le callback + cmp al, 1 + jne .no_action - ; Vérifier si un callback est défini - cmp word [gs:si + widget.event_click], 0 - je .no_action + ; Vérifier si un callback est défini + cmp word [gs:si + widget.event_click], 0 + je .no_action - ; APPEL DU CALLBACK (Fonction utilisateur) - ; On sauve les registres car le callback peut faire n'importe quoi - pusha - call word [gs:si + widget.event_click] - popa + ; APPEL DU CALLBACK (Fonction utilisateur) + ; On sauve les registres car le callback peut faire n'importe quoi + pusha + call word [gs:si + widget.event_click] + popa - .no_action: - pop bx ; Restaurer boutons + .no_action: + pop bx ; Restaurer boutons - mov al, byte [gs:si + widget.oldstate] - mov ah, byte [gs:si + widget.state] + mov al, byte [gs:si + widget.oldstate] + mov ah, byte [gs:si + widget.state] - ; Dessiner (si l'état a changé ou pour refresh) + ; Dessiner (si l'état a changé ou pour refresh) - cmp al,ah - je .next_widget ; Si != 0, le widget est à jour, on passe. - call gui_draw_single_widget + cmp al,ah + je .next_widget ; Si != 0, le widget est à jour, on passe. + call gui_draw_single_widget - .next_widget: - add si, widget_size - dec di - jnz .loop_widgets + .next_widget: + add si, widget_size + dec di + jnz .loop_widgets - pop gs - pop ds - popa - ret + pop gs + pop ds + popa + ret ; ============================================================================= ; SECTION : LOGIQUE INTERNE ET DESSIN @@ -251,367 +257,446 @@ gui_process_all: ; Entrée : SI=Widget, CX=MouseX, DX=MouseY, BL=Buttons ; Sortie : AL=1 si Clicked, 0 sinon. Met à jour [gs:si].state gui_update_logic: - push ds - mov ax, BDA_DATA_SEG - mov ds, ax + push ds + mov ax, BDA_DATA_SEG + mov ds, ax - mov al, byte [gs:si + widget.state] - cmp al, GUI_STATE_DISABLED - je .done + mov al, byte [gs:si + widget.state] + cmp al, GUI_STATE_DISABLED + je .done - xor ax, ax + xor ax, ax - ; les macros peuvent modifer les registres cx, dx & bx - mov cx, [BDA_MOUSE + mouse.x] - mov dx, [BDA_MOUSE + mouse.y] + ; les macros peuvent modifer les registres cx, dx & bx + mov cx, [BDA_MOUSE + mouse.x] + mov dx, [BDA_MOUSE + mouse.y] - ; --- Hit Test --- - cmp cx, [gs:si + widget.x] - jl .miss ; mouse.x < widget.x ? - mov bx, [gs:si + widget.x] - add bx, [gs:si + widget.w] - cmp cx, bx - jg .miss ; mouse.x >= widget.x ? + ; --- Hit Test --- + cmp cx, [gs:si + widget.x] + jl .miss ; mouse.x < widget.x ? + mov bx, [gs:si + widget.x] + add bx, [gs:si + widget.w] + cmp cx, bx + jg .miss ; mouse.x >= widget.x ? - cmp dx, [gs:si + widget.y] - jl .miss ; mouse.y < widget.y ? - mov bx, [gs:si + widget.y] - add bx, [gs:si + widget.h] - cmp dx, bx - jg .miss ; mouse.y >= widget.y ? + cmp dx, [gs:si + widget.y] + jl .miss ; mouse.y < widget.y ? + mov bx, [gs:si + widget.y] + add bx, [gs:si + widget.h] + cmp dx, bx + jg .miss ; mouse.y >= widget.y ? - ; Dispatch selon le type - cmp byte [gs:si + widget.type], WIDGET_TYPE_SLIDER - je .call_slider_logic + mov bl, [BDA_MOUSE + mouse.status] - call gui_logic_button - jmp .done + ; Dispatch selon le type + cmp byte [gs:si + widget.type], WIDGET_TYPE_SLIDER + jne .case_2 + call gui_logic_slider + jmp .done - .call_slider_logic: - call gui_logic_slider - jmp .done + .case_2: + cmp byte [gs:si + widget.type], WIDGET_TYPE_BUTTON + jne .case_3 + call gui_logic_button + jmp .done - .miss: - ; Si on n'est plus dessus, mais qu'on l'était avant (HOVER/PRESSED), il faut redessiner ! - cmp byte [gs:si + widget.state], GUI_STATE_NORMAL - je .done ; Déjà normal, rien à faire + .case_3: + cmp byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON + jne .case_4 + call gui_logic_button + jmp .done - ; Cas particulier : si c'est un slider en cours de drag, on continue la logique - cmp byte [gs:si + widget.type], WIDGET_TYPE_SLIDER - jne .reset_state - cmp byte [gs:si + widget.state], GUI_STATE_PRESSED - je .call_slider_logic + .case_4: + jmp .done + .miss: + ; Si on n'est plus dessus, mais qu'on l'était avant (HOVER/PRESSED), il faut redessiner ! + cmp byte [gs:si + widget.state], GUI_STATE_NORMAL + je .done ; Déjà normal, rien à faire - .reset_state: - mov byte [gs:si + widget.state], GUI_STATE_NORMAL - xor ax, ax + ; Cas particulier : si c'est un slider en cours de drag, on continue la logique +; cmp byte [gs:si + widget.type], WIDGET_TYPE_SLIDER +; jne .reset_state +; cmp byte [gs:si + widget.state], GUI_STATE_PRESSED +; je .gui_logic_slider - .done: - pop ds - ret + .reset_state: + mov byte [gs:si + widget.state], GUI_STATE_NORMAL + xor ax, ax + + .done: + pop ds + ret ; --- Logique spécifique Bouton --- gui_logic_button: - test bl, 1 ; Clic gauche ? - jz .released + test bl, 1 ; Clic gauche ? + jz .released - ; Clic enfoncé - mov byte [gs:si + widget.state], GUI_STATE_PRESSED - xor ax, ax - ret + ; Clic enfoncé + mov byte [gs:si + widget.state], GUI_STATE_PRESSED + xor ax, ax + ret - .released: - cmp byte [gs:si + widget.state], GUI_STATE_PRESSED - jne .hover + .released: + cmp byte [gs:si + widget.state], GUI_STATE_PRESSED + jne .hover - ; Clic validé ! - mov al, 1 - mov byte [gs:si + widget.state], GUI_STATE_HOVER - ret + ; Clic validé ! + mov al, 1 + mov byte [gs:si + widget.state], GUI_STATE_HOVER + ret - .hover: - mov byte [gs:si + widget.state], GUI_STATE_HOVER - xor ax, ax - ret + .hover: + mov byte [gs:si + widget.state], GUI_STATE_HOVER + xor ax, ax + ret ; --- Logique spécifique Slider --- gui_logic_slider: - test bl, 1 ; Bouton enfoncé ? - jz .released + test bl, 1 ; Bouton enfoncé ? + jz .released - ; Si on vient de cliquer (pas encore PRESSED), on initialise l'ancrage - cmp byte [gs:si + widget.state], GUI_STATE_PRESSED - je .do_drag + ; Si on vient de cliquer (pas encore PRESSED), on initialise l'ancrage + cmp byte [gs:si + widget.state], GUI_STATE_PRESSED + je .do_drag - mov byte [gs:si + widget.state], GUI_STATE_PRESSED + mov byte [gs:si + widget.state], GUI_STATE_PRESSED - ; Calcul de l'ancrage - mov ax, cx - sub ax, [gs:si + widget.attr_val] - mov [gs:si + widget.attr_anchor], ax - cmp byte [gs:si + widget.attr_mode], 2 - jne .do_drag - mov ax, dx - sub ax, [gs:si + widget.attr_val] - mov [gs:si + widget.attr_anchor], ax + ; Calcul de l'ancrage + mov ax, cx + sub ax, [gs:si + widget.attr_val] + mov [gs:si + widget.attr_anchor], ax + cmp byte [gs:si + widget.attr_mode], 2 + jne .do_drag + mov ax, dx + sub ax, [gs:si + widget.attr_val] + mov [gs:si + widget.attr_anchor], ax - .do_drag: - ; --- LOGIQUE DE DÉPLACEMENT --- - cmp byte [gs:si + widget.attr_mode], 1 - je .drag_h - cmp byte [gs:si + widget.attr_mode], 2 - je .drag_v - xor ax, ax - ret + .do_drag: + ; --- LOGIQUE DE DÉPLACEMENT --- + cmp byte [gs:si + widget.attr_mode], 1 + je .drag_h + cmp byte [gs:si + widget.attr_mode], 2 + je .drag_v + xor ax, ax + ret - .drag_h: - mov ax, cx - sub ax, [gs:si + widget.attr_anchor] ; Nouvelle pos = MouseX - Anchor - ; Clamp Min - cmp ax, [gs:si + widget.attr_min] - jge .chk_max_h - mov ax, [gs:si + widget.attr_min] - .chk_max_h: - ; Clamp Max - cmp ax, [gs:si + widget.attr_max] - jle .apply_pos - mov ax, [gs:si + widget.attr_max] - jmp .apply_pos + .drag_h: + mov ax, cx + sub ax, [gs:si + widget.attr_anchor] ; Nouvelle pos = MouseX - Anchor + ; Clamp Min + cmp ax, [gs:si + widget.attr_min] + jge .chk_max_h + mov ax, [gs:si + widget.attr_min] + .chk_max_h: + ; Clamp Max + cmp ax, [gs:si + widget.attr_max] + jle .apply_pos + mov ax, [gs:si + widget.attr_max] + jmp .apply_pos - .drag_v: - mov ax, dx - sub ax, [gs:si + widget.attr_anchor] - ; Clamp Min - cmp ax, [gs:si + widget.attr_min] - jge .chk_max_v - mov ax, [gs:si + widget.attr_min] + .drag_v: + mov ax, dx + sub ax, [gs:si + widget.attr_anchor] + ; Clamp Min + cmp ax, [gs:si + widget.attr_min] + jge .chk_max_v + mov ax, [gs:si + widget.attr_min] - .chk_max_v: - ; Clamp Max - cmp ax, [gs:si + widget.attr_max] - jle .apply_pos - mov ax, [gs:si + widget.attr_max] - jmp .apply_pos + .chk_max_v: + ; Clamp Max + cmp ax, [gs:si + widget.attr_max] + jle .apply_pos + mov ax, [gs:si + widget.attr_max] + jmp .apply_pos - .apply_pos: - cmp ax, [gs:si + widget.attr_val] - je .no_change - mov [gs:si + widget.attr_val], ax + .apply_pos: + cmp ax, [gs:si + widget.attr_val] + je .no_change + mov [gs:si + widget.attr_val], ax - ; Appel du callback on_drag - cmp word [gs:si + widget.event_drag], 0 - je .force_redraw - pusha - call word [gs:si + widget.event_drag] - popa + ; Appel du callback on_drag + cmp word [gs:si + widget.event_drag], 0 + je .force_redraw + pusha + call word [gs:si + widget.event_drag] + popa - .force_redraw: - mov byte [gs:si + widget.oldstate], 255 ; Force le redessin + .force_redraw: + mov byte [gs:si + widget.oldstate], 255 ; Force le redessin - .no_change: - xor ax, ax - ret + .no_change: + xor ax, ax + ret - .released: - mov byte [gs:si + widget.state], GUI_STATE_HOVER - xor ax, ax - ret + .released: + mov byte [gs:si + widget.state], GUI_STATE_HOVER + xor ax, ax + ret -; (Interne) Dessine le widget pointé par SI + + +; Dessine le widget pointé par SI gui_draw_single_widget: - pusha + pusha - mov al, [gs:si + widget.state] - cmp al, [gs:si + widget.oldstate] - je .done + GFX MOUSE_HIDE ; On cache la souris AVANT de commencer le dessin du widget - mov [gs:si + widget.oldstate], al ; marque comme à jour + mov al, [gs:si + widget.state] + cmp al, [gs:si + widget.oldstate] + je .done - ; Chargement coords - mov ax, [gs:si + widget.x] - mov bx, [gs:si + widget.y] + mov [gs:si + widget.oldstate], al ; marque comme à jour - mov cx, [gs:si + widget.w] - mov dx, [gs:si + widget.h] + ; Chargement coords + mov ax, [gs:si + widget.x] + mov bx, [gs:si + widget.y] - ; Calcul X2, Y2 - add cx, ax - add dx, bx + mov cx, [gs:si + widget.w] + mov dx, [gs:si + widget.h] - ; Dispatch selon le type - cmp byte [gs:si + widget.type], WIDGET_TYPE_SLIDER - jne .not_slider - call draw_slider - jmp .done + ; Calcul X2, Y2 + add cx, ax + add dx, bx - .not_slider: - cmp byte [gs:si + widget.type], WIDGET_TYPE_BUTTON - jne .done - call draw_button + mov [gs:si + widget.x2], cx + mov [gs:si + widget.y2], dx - .done: - popa - ret + ; Dispatch selon le type + cmp byte [gs:si + widget.type], WIDGET_TYPE_SLIDER + jne .case_2 + call draw_slider + jmp .done + + .case_2: + cmp byte [gs:si + widget.type], WIDGET_TYPE_BUTTON + jne .case_3 + call draw_button + jmp .done + + .case_3: + cmp byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON + jne .case_4 + call draw_round_button +; jmp .done + + .case_4: + + + + .done: + GFX MOUSE_SHOW ; On réaffiche la souris APRES, capturant le widget fini + popa + ret draw_slider: - ; 1. Dessiner la piste (Track) - GFX RECTANGLE_FILL, ax, bx, cx, dx, pattern_gray_mid - GFX RECTANGLE_DRAW, ax, bx, cx, dx, 0 + ; 1. Dessiner la piste (Track) + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE_LIGHT + GFX RECTANGLE, ax, bx, cx, dx, 0 - ; 2. Calculer la taille du curseur (Thumb) - push ax ; Sauvegarde coords track (X1, Y1, X2, Y2) - push bx - push cx - push dx + ; 2. Calculer la taille du curseur (Thumb) + push ax ; Sauvegarde coords track (X1, Y1, X2, Y2) + push bx + push cx + push dx - cmp byte [gs:si + widget.attr_mode], 2 - je .calc_v + cmp byte [gs:si + widget.attr_mode], 2 + je .calc_v - ; --- Horizontal --- - mov ax, [gs:si + widget.w] - xor bh, bh - mov bl, [gs:si + widget.thumb_pct] - mul bx - mov bx, 100 - div bx ; AX = Thumb Width + ; --- Horizontal --- + mov ax, [gs:si + widget.w] + xor bh, bh + mov bl, [gs:si + widget.thumb_pct] + mul bx + mov bx, 100 + div bx ; AX = Thumb Width - mov bx, [gs:si + widget.y] ; BX = Y1 - mov dx, bx - add dx, [gs:si + widget.h] ; DX = Y2 - mov cx, [gs:si + widget.attr_val] ; CX = X1 - add ax, cx ; AX = X1 + Width = X2 - xchg ax, cx ; AX = X1, CX = X2 - jmp .draw_thumb + mov bx, [gs:si + widget.y] ; BX = Y1 + mov dx, bx + add dx, [gs:si + widget.h] ; DX = Y2 + mov cx, [gs:si + widget.attr_val] ; CX = X1 + add ax, cx ; AX = X1 + Width = X2 + xchg ax, cx ; AX = X1, CX = X2 + jmp .draw_thumb - .calc_v: - ; --- Vertical --- - mov ax, [gs:si + widget.h] - xor bh, bh - mov bl, [gs:si + widget.thumb_pct] - mul bx - mov bx, 100 - div bx ; AX = Thumb Height + .calc_v: + ; --- Vertical --- + mov ax, [gs:si + widget.h] + xor bh, bh + mov bl, [gs:si + widget.thumb_pct] + mul bx + mov bx, 100 + div bx ; AX = Thumb Height - mov bx, [gs:si + widget.attr_val] ; BX = Y1 - mov dx, bx - add dx, ax ; DX = Y2 - mov ax, [gs:si + widget.x] ; AX = X1 - mov cx, ax - add cx, [gs:si + widget.w] ; CX = X2 + mov bx, [gs:si + widget.attr_val] ; BX = Y1 + mov dx, bx + add dx, ax ; DX = Y2 + mov ax, [gs:si + widget.x] ; AX = X1 + mov cx, ax + add cx, [gs:si + widget.w] ; CX = X2 - .draw_thumb: - ; 3. Dessiner le curseur (Thumb) - GFX RECTANGLE_FILL, ax, bx, cx, dx, pattern_white - GFX RECTANGLE_DRAW, ax, bx, cx, dx, 0 - add sp, 8 ; Nettoyer la pile des push ax..dx - GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT - call draw_text - ret + .draw_thumb: + ; 3. Dessiner le curseur (Thumb) + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE + GFX RECTANGLE, ax, bx, cx, dx, 0 + add sp, 8 ; Nettoyer la pile des push ax..dx + GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT + call draw_text + ret draw_button: - ; Dispatch selon état - cmp byte [gs:si + widget.state], GUI_STATE_PRESSED - je .paint_pressed - cmp byte [gs:si + widget.state], GUI_STATE_HOVER - je .paint_hover + ; Dispatch selon état + cmp byte [gs:si + widget.state], GUI_STATE_PRESSED + je .paint_pressed + cmp byte [gs:si + widget.state], GUI_STATE_HOVER + je .paint_hover - .paint_normal: - GFX RECTANGLE_FILL, ax, bx, cx, dx, pattern_white +; .paint_normal: +; GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE - ; Si user_id == 1, on dessine le style "OK" (Double bordure épaisse) - test byte [gs:si + widget.user_id], 1 - jnz .draw_default_style + ; Si user_id == 1, on dessine le style "OK" (Double bordure épaisse) + ;test byte [gs:si + widget.attr_mode], BUTTON_OK + ;jnz .draw_default_style - GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 - jmp .draw_text_now +; GFX RECTANGLE, ax, bx, cx, dx, 0 +; jmp .done - .draw_default_style: - ; Bordure extérieure épaisse (2px) - GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 - inc ax - inc bx - dec cx - dec dx - GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 + .draw_default_style: + ; Bordure extérieure épaisse (2px) + GFX RECTANGLE, ax, bx, cx, dx, 0 + inc ax + inc bx + dec cx + dec dx + GFX RECTANGLE, ax, bx, cx, dx, 0 - ; Bordure intérieure fine (après un gap de 1px blanc) - add ax, 2 - add bx, 2 - sub cx, 2 - sub dx, 2 - GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 + ; Bordure intérieure fine (après un gap de 1px blanc) + add ax, 2 + add bx, 2 + sub cx, 2 + sub dx, 2 + GFX RECTANGLE, ax, bx, cx, dx, 0 + jmp .done - .draw_text_now: - ; Recharger les coordonnées de base pour le texte - mov ax, [gs:si + widget.x] - mov bx, [gs:si + widget.y] + .paint_hover: + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE_LIGHT + GFX RECTANGLE, ax, bx, cx, dx, 0 + jmp .done - GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT - call draw_text - jmp .done + .paint_pressed: + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_BLACK + GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT - .paint_hover: - GFX RECTANGLE_FILL, ax, bx, cx, dx, pattern_gray_mid - GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 + .done: + ; on affiche le texte + ; Recharger les coordonnées de base pour le texte + mov ax, [gs:si + widget.x] + mov bx, [gs:si + widget.y] - ; Restauration coords pour le texte - mov ax, [gs:si + widget.x] - mov bx, [gs:si + widget.y] - call draw_text - jmp .done + GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT + call draw_text + ret - .paint_pressed: - GFX RECTANGLE_FILL, ax, bx, cx, dx, pattern_black - GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT - ; Recharger les coordonnées car GFX a pu modifier les registres - mov ax, [gs:si + widget.x] - mov bx, [gs:si + widget.y] - call draw_text +; +; dessine un bouton arrondi +; +draw_round_button: + ; Dispatch selon état + cmp byte [gs:si + widget.state], GUI_STATE_PRESSED + je .paint_pressed + cmp byte [gs:si + widget.state], GUI_STATE_HOVER + je .paint_hover - .done: - ret + .paint_normal: + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE + mov di, 0 ; Couleur Noire + call .draw_round_borders + GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT + jmp .draw_text_now + .paint_hover: + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE + mov di, 0 ; Couleur Noire + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE_LIGHT + call .draw_round_borders + GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT + jmp .draw_text_now + + .paint_pressed: + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_BLACK + mov di, 1 ; Bordure Blanche sur fond noir + call .draw_round_borders + GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT + jmp .draw_text_now + + .draw_text_now: + ; Recharger les coordonnées de base pour le texte + mov ax, [gs:si + widget.x] + mov bx, [gs:si + widget.y] + call draw_text + jmp .done + + .done: + ret + +.draw_round_borders: + ; Bordure principale + GFX RECTANGLE_ROUND, ax, bx, cx, dx, di + ; Si style "OK" (attr_mode & BUTTON_OK), on ajoute la double bordure + ;test byte [gs:si + widget.attr_mode], BUTTON_OK + ;jz .done_borders + add ax, 2 + add bx, 2 + sub cx, 2 + sub dx, 2 + GFX RECTANGLE_ROUND, ax, bx, cx, dx, di + .done_borders: + ret + +; +; affiche le texte au centre du widget gs:si +; draw_text: - ; Petit hack pour recentrer le texte après l'effet gras - mov cx, [gs:si + widget.w] - mov dx, [gs:si + widget.h] + ; Petit hack pour recentrer le texte après l'effet gras + mov cx, [gs:si + widget.w] + mov dx, [gs:si + widget.h] - .text: - ; --- Centrage Texte (Simplifié) --- - mov es, [gs:si + widget.text_seg] - mov di, [gs:si + widget.text_ofs] - xor cx, cx + .text: + ; --- Centrage Texte (Simplifié) --- + mov es, [gs:si + widget.text_seg] + mov di, [gs:si + widget.text_ofs] + xor cx, cx - .strlen: - cmp byte [es:di], 0 - je .calc - inc cx - inc di - jmp .strlen + .strlen: + cmp byte [es:di], 0 + je .calc + inc cx + inc di + jmp .strlen - .calc: - shl cx, 3 ; Largeur texte px - push ax ; Sauve X original - mov ax, [gs:si + widget.w] - sub ax, cx - shr ax, 1 - pop dx ; Récupère X original (poussé depuis AX) - add ax, dx ; AX = X final centré + .calc: + shl cx, 3 ; Largeur texte px + push ax ; Sauve X original + mov ax, [gs:si + widget.w] + sub ax, cx + shr ax, 1 + pop dx ; Récupère X original (poussé depuis AX) + add ax, dx ; AX = X final centré - push ax ; Sauvegarde X final pour GOTOXY + push ax ; Sauvegarde X final pour GOTOXY - mov bx, [gs:si + widget.h] - sub bx, 8 - shr bx, 1 - add bx, [gs:si + widget.y] ; Y final + mov bx, [gs:si + widget.h] + sub bx, 8 + shr bx, 1 + add bx, [gs:si + widget.y] ; Y final - pop cx ; CX = X Final, BX = Y Final + pop cx ; CX = X Final, BX = Y Final - GFX GOTOXY, cx, bx + GFX GOTOXY, cx, bx - mov dx, [gs:si + widget.text_seg] - mov ax, [gs:si + widget.text_ofs] - GFX WRITE, dx, ax - ret + mov dx, [gs:si + widget.text_seg] + mov ax, [gs:si + widget.text_ofs] + GFX WRITE, dx, ax + ret \ No newline at end of file