GUI testing

This commit is contained in:
Frater 2026-02-01 21:24:40 +01:00
commit dc7ee9a6c9
4 changed files with 92 additions and 255 deletions

View file

@ -102,10 +102,6 @@ reset:
; Init du système GUI ; Init du système GUI
call gui_init_system call gui_init_system
; GFX RECTANGLE_FILL, 50, 50, 100, 30, 1
call build_interface call build_interface
main_loop: main_loop:
@ -147,7 +143,7 @@ build_interface:
jc .mem_full jc .mem_full
mov word [gs:si + widget.x], 100 mov word [gs:si + widget.x], 100
mov word [gs:si + widget.y], 10 mov word [gs:si + widget.y], 50
mov word [gs:si + widget.w], 80 mov word [gs:si + widget.w], 80
mov word [gs:si + widget.h], 20 mov word [gs:si + widget.h], 20
mov word [gs:si + widget.text_ofs], str_hello mov word [gs:si + widget.text_ofs], str_hello

View file

@ -402,14 +402,14 @@ cga_putc:
; ;
; write string from [DS:SI] to screen ; write string from [DS:SI] to screen
; ;
; --- Définition des arguments ---
%define .txt_seg word [bp+4]
%define .txt_ofs word [bp+6]
cga_write: cga_write:
push bp push bp
mov bp, sp mov bp, sp
push ax push ax
push ds
; --- Définition des arguments ---
%define .txt_seg word [bp+4]
%define .txt_ofs word [bp+6]
mov ax, .txt_seg mov ax, .txt_seg
mov ds, ax mov ds, ax
@ -424,9 +424,12 @@ cga_write:
jmp .loops jmp .loops
.done: .done:
pop ds
pop ax pop ax
leave leave
ret ret
%undef .txt_seg
%undef .txt_ofs
; ------------------------------------------------------------ ; ------------------------------------------------------------
; cga_putpixel (x,y,color) ; cga_putpixel (x,y,color)
@ -437,14 +440,14 @@ cga_write:
; ES = Target Segment (usually VIDEO_SEG) ; ES = Target Segment (usually VIDEO_SEG)
; BL = color (0=black, !=0=white) ; BL = color (0=black, !=0=white)
; ------------------------------------------------------------ ; ------------------------------------------------------------
; --- Définition des arguments ---
%define .x word [bp+4]
%define .y word [bp+6]
%define .color byte [bp+8]
cga_putpixel: cga_putpixel:
push bp push bp
mov bp, sp mov bp, sp
; --- Définition des arguments ---
%define .x word [bp+4]
%define .y word [bp+6]
%define .color byte [bp+8]
pusha pusha
mov cx, .x mov cx, .x
@ -464,14 +467,13 @@ cga_putpixel:
and byte [es:di], ah and byte [es:di], ah
.done: .done:
; clean defs
%undef .x
%undef .y
%undef .color
popa popa
leave leave
ret ret
; clean defs
%undef .x
%undef .y
%undef .color
; ------------------------------------------------------------ ; ------------------------------------------------------------
; cga_getpixel (x,y) ; cga_getpixel (x,y)
@ -1258,7 +1260,12 @@ cga_cursor_draw:
mov eax, 0xFFFFFFFF mov eax, 0xFFFFFFFF
mov ax, [gs:si] ; masque AND mov ax, [gs:si] ; masque AND
xchg al, ah ; Redresser pour l'ordre des pixels CGA xchg al, ah ; Redresser pour l'ordre des pixels CGA
bswap eax ;bswap eax ; equivalence i386+
xchg ah, al ; Échange les octets de poids faible (AL <-> AH)
rol eax, 16 ; Bascule les 16 bits de poids fort vers le bas
xchg ah, al ; Échange les nouveaux octets de poids faible
; fin bswap eax equivalence i386+
ror eax, cl ror eax, cl
or eax, [esp+2] ; Force les bits clippés à 1 (garde le fond) or eax, [esp+2] ; Force les bits clippés à 1 (garde le fond)
@ -1266,8 +1273,10 @@ cga_cursor_draw:
; --- MASQUE XOR (EBX) --- ; --- MASQUE XOR (EBX) ---
xor ebx, ebx xor ebx, ebx
mov bx, [gs:si+32] ; masque XOR mov bx, [gs:si+32] ; masque XOR
xchg bl, bh ;xchg bl, bh
bswap ebx ;bswap ebx
rol ebx, 16
xchg bh, bl
ror ebx, cl ror ebx, cl
mov edx, [esp+2] ; charge le masque de protection mov edx, [esp+2] ; charge le masque de protection
@ -1277,10 +1286,18 @@ cga_cursor_draw:
; --- LECTURE ET MODIFICATION (EDX) --- ; --- LECTURE ET MODIFICATION (EDX) ---
mov edx, [es:di] ; lecture des 16 bits a la position du curseur mov edx, [es:di] ; lecture des 16 bits a la position du curseur
bswap edx ; bswap edx
xchg dh, dl
rol edx, 16
xchg dh, dl
and edx, eax ; application du masque AND (AX) and edx, eax ; application du masque AND (AX)
xor edx, ebx ; application du masque XOR (BX) xor edx, ebx ; application du masque XOR (BX)
bswap edx ; bswap edx
xchg dh, dl
rol edx, 16
xchg dh, dl
mov [es:di], edx ; ecriture du résultat mov [es:di], edx ; ecriture du résultat
; --- GESTION DES BANQUES --- ; --- GESTION DES BANQUES ---

View file

@ -136,6 +136,9 @@ mouse_init:
mov ah,IRQ_ENABLED mov ah,IRQ_ENABLED
mov al, 12 mov al, 12
call pic_set_irq_mask call pic_set_irq_mask
mov ah,IRQ_ENABLED
mov al, 2
call pic_set_irq_mask
sti sti
pop ax pop ax
pop ds pop ds

View file

@ -31,16 +31,28 @@
%define GUI_STATE_PRESSED 3 ; Clic enfoncé %define GUI_STATE_PRESSED 3 ; Clic enfoncé
%define GUI_STATE_DISABLED 4 ; Grisé %define GUI_STATE_DISABLED 4 ; Grisé
; --- Event mask ---
%define EVENT_NONE 00000000b
%define EVENT_HOVER 00000001b
%define EVENT_LEFT_CLICK 00000010b
%define EVENT_RIGHT_CLICK 00000100b
%define EVENT_MIDDLE_CLICK 00001000b
%define EVENT_LEFT_RELEASE 00010000b
%define EVENT_RIGHT_RELEASE 00100000b
%define EVENT_ENTER 10000000b
; --- Structure d'un OBJET (Bouton, etc) --- ; --- Structure d'un OBJET (Bouton, etc) ---
struc widget struc widget
.state resb 1 ; État (0=libre, >0=utilisé) .state resb 1 ; État (0=libre, >0=utilisé)
.user_id resb 1 ; ID unique utilisateur .user_id resb 1 ; ID unique utilisateur
.updated resb 1 ; widget a-t-il été modifié ?
.x resw 1 ; Position X .x resw 1 ; Position X
.y resw 1 ; Position Y .y resw 1 ; Position Y
.w resw 1 ; Largeur .w resw 1 ; Largeur
.h resw 1 ; Hauteur .h resw 1 ; Hauteur
.text_ofs resw 1 ; Offset du texte .text_ofs resw 1 ; Offset du texte
.text_seg resw 1 ; Segment du texte .text_seg resw 1 ; Segment du texte
.event resb 1 ; found events (0 = none)
.event_click resw 1 ; adresse de la fonction "on click" .event_click resw 1 ; adresse de la fonction "on click"
; .event_hover resw 1 ; adresse de la fonction "on hover" ; .event_hover resw 1 ; adresse de la fonction "on hover"
; .event_release resw 1 ; adresse de la fonction "on release" ; .event_release resw 1 ; adresse de la fonction "on release"
@ -110,7 +122,8 @@ gui_alloc_widget:
.found: .found:
; On initialise le slot trouvé ; On initialise le slot trouvé
mov byte [gs:si + widget.state], GUI_STATE_NORMAL ; Marquer comme occupé mov byte [gs:si + widget.state], GUI_STATE_NORMAL ; Marquer comme occupé
mov byte [gs:si + widget.updated], 0 ; doit etre dessiné
; Reset des champs critiques pour éviter les déchets ; Reset des champs critiques pour éviter les déchets
mov word [gs:si + widget.event_click], 0 mov word [gs:si + widget.event_click], 0
@ -134,9 +147,9 @@ gui_free_widget:
mov ax, GUI_RAM_SEG mov ax, GUI_RAM_SEG
mov es, ax mov es, ax
xor di, di mov di, si
mov cx, (widget_size) mov cx, (widget_size)
shl cx, 2 ; cx / 4 shr cx, 2 ; cx / 4
xor eax, eax xor eax, eax
rep stosd ; Remplit tout de 0 rep stosd ; Remplit tout de 0
@ -163,6 +176,7 @@ gui_free_widget:
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
gui_process_all: gui_process_all:
pusha pusha
push ds
push gs push gs
mov ax, BDA_DATA_SEG mov ax, BDA_DATA_SEG
@ -183,13 +197,9 @@ gui_process_all:
cmp byte [gs:si + widget.state], GUI_STATE_FREE cmp byte [gs:si + widget.state], GUI_STATE_FREE
je .next_widget je .next_widget
; mov ah, [gs:si + widget.state] ; AH = Ancien état du widget
; --- Logique Widget --- ; --- Logique Widget ---
;push bx ; Sauver état boutons souris push bx ; Sauver état boutons souris
push ax
call gui_update_logic ; Vérifier collision/clic call gui_update_logic ; Vérifier collision/clic
;pop ax
; Si AL=1 (Clic relâché validé), exécuter le callback ; Si AL=1 (Clic relâché validé), exécuter le callback
cmp al, 1 cmp al, 1
@ -209,6 +219,8 @@ gui_process_all:
pop bx ; Restaurer boutons pop bx ; Restaurer boutons
; Dessiner (si l'état a changé ou pour refresh) ; Dessiner (si l'état a changé ou pour refresh)
;cmp byte [gs:si + widget.updated], 0
;je .next_widget
call gui_draw_single_widget call gui_draw_single_widget
.next_widget: .next_widget:
@ -216,6 +228,7 @@ gui_process_all:
dec di dec di
jnz .loop_widgets jnz .loop_widgets
pop gs
pop ds pop ds
popa popa
ret ret
@ -233,6 +246,8 @@ gui_update_logic:
cmp byte [gs:si + widget.state], GUI_STATE_DISABLED cmp byte [gs:si + widget.state], GUI_STATE_DISABLED
je .done je .done
mov byte [gs:si + widget.event], 0 ; no event found]
; --- Hit Test --- ; --- Hit Test ---
cmp cx, [gs:si + widget.x] cmp cx, [gs:si + widget.x]
jl .miss jl .miss
@ -248,16 +263,31 @@ gui_update_logic:
cmp dx, bp cmp dx, bp
jg .miss jg .miss
mov [gs:si + widget.updated], 0 ; request update
; reset "enter" event
and byte [gs:si + widget.event], ~EVENT_ENTER
; --- on est au moins dans le widget ---
test byte [gs:si + widget.event], EVENT_HOVER
jz .not_entering
; entering the widget
or byte [gs:si + widget.event], EVENT_ENTER
.not_entering:
or byte [gs:si + widget.event], EVENT_HOVER
; --- Hit: Souris sur le widget --- ; --- Hit: Souris sur le widget ---
test bl, 1 ; Clic gauche ? test bl, 1 ; Clic gauche ?
jz .released jz .released
; Clic enfoncé ; Clic enfoncé
mov byte [gs:si + widget.state], GUI_STATE_PRESSED mov byte [gs:si + widget.state], GUI_STATE_PRESSED
or byte [gs:si + widget.event], EVENT_LEFT_CLICK
jmp .done jmp .done
.released: .released:
; Bouton relâché. Était-il pressé avant ? ; Bouton relâché. Était-il pressé avant ?
and byte [gs:si + widget.event], ~EVENT_LEFT_CLICK
cmp byte [gs:si + widget.state], GUI_STATE_PRESSED cmp byte [gs:si + widget.state], GUI_STATE_PRESSED
jne .hover jne .hover
@ -272,6 +302,8 @@ gui_update_logic:
.miss: .miss:
mov byte [gs:si + widget.state], GUI_STATE_NORMAL mov byte [gs:si + widget.state], GUI_STATE_NORMAL
and byte [gs:si + widget.event], ~EVENT_HOVER
.done: .done:
ret ret
@ -285,34 +317,32 @@ gui_draw_single_widget:
mov cx, [gs:si + widget.w] mov cx, [gs:si + widget.w]
mov dx, [gs:si + widget.h] mov dx, [gs:si + widget.h]
inc [gs:si + widget.updated] ; dessiné
; Calcul X2, Y2 ; Calcul X2, Y2
mov di, ax add cx, ax
add di, cx add dx, bx
mov bp, bx
add bp, dx
; Dispatch selon état ; Dispatch selon état
mov al, [gs:si + widget.state] cmp byte [gs:si + widget.state], GUI_STATE_PRESSED
cmp al, GUI_STATE_PRESSED
je .paint_pressed je .paint_pressed
cmp al, GUI_STATE_HOVER cmp byte [gs:si + widget.state], GUI_STATE_HOVER
je .paint_hover je .paint_hover
.paint_normal: .paint_normal:
GFX RECTANGLE_FILL, ax, bx, di, bp, 1 ; Blanc GFX RECTANGLE_FILL, ax, bx, cx, dx, 1 ; Blanc
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 ; Bord Noir GFX RECTANGLE_DRAW, ax, bx, cx, dx, 0 ; Bord Noir
GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT
jmp .text jmp .text
.paint_hover: .paint_hover:
GFX RECTANGLE_FILL, ax, bx, di, bp, 1 GFX RECTANGLE_FILL, ax, bx, cx, dx, 1 ; Blanc
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 GFX RECTANGLE_DRAW, ax, bx, cx, dx, 0 ; Bord Noir
; Effet gras ; Effet gras
inc ax inc ax
inc bx inc bx
dec di dec cx
dec bp dec dx
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 GFX RECTANGLE_DRAW, ax, bx, cx, dx, 0
; Restauration coords pour le texte ; Restauration coords pour le texte
mov ax, [gs:si + widget.x] mov ax, [gs:si + widget.x]
@ -320,7 +350,7 @@ gui_draw_single_widget:
jmp .text_setup jmp .text_setup
.paint_pressed: .paint_pressed:
GFX RECTANGLE_FILL, ax, bx, di, bp, 0 ; Noir GFX RECTANGLE_FILL, ax, bx, cx, dx, 0 ; Noir
GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT
jmp .text jmp .text
@ -370,212 +400,3 @@ gui_draw_single_widget:
pop si pop si
popa popa
ret ret
; =============================================================================
; Fonction : gui_draw_button
; Description : Dessine un bouton selon son état (.state)
; Entrée : SI = Pointeur vers la structure widget (DS:SI)
; =============================================================================
gui_draw_button:
pusha
; Charger les propriétés depuis la structure
mov ax, [gs:si + widget.x]
mov bx, [gs:si + widget.y]
mov cx, [gs:si + widget.w]
mov dx, [gs:si + widget.h]
; Calculer X2 et Y2 pour le rectangle
mov di, ax ; X1
add di, cx ; X2 = X + W
mov bp, bx ; Y1
add bp, dx ; Y2 = Y + H
; Vérifier l'état
cmp byte [gs:si + widget.state], GUI_STATE_PRESSED
je .draw_pressed
cmp byte [gs:si + widget.state], GUI_STATE_HOVER
je .draw_hover
cmp byte [gs:si + widget.state], GUI_STATE_DISABLED
je .draw_disabled
.draw_normal:
; Fond BLANC, Bordure NOIRE, Texte NOIR
; 1. Effacer le fond (Rectangle plein blanc) -> On triche, on dessine un rect plein blanc
; Note: GFX RECTANGLE_FILL utilise une couleur 0 ou 1. 1 = Blanc.
GFX RECTANGLE_FILL, ax, bx, di, bp, 1 ; Fond blanc
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 ; Bordure noire
; Configuration texte : Noir sur Transparent
GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT
jmp .draw_text
.draw_hover:
; Comme normal, mais bordure plus épaisse ou autre effet
GFX RECTANGLE_FILL, ax, bx, di, bp, 1
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0
; Effet "Gras" sur le cadre (on dessine un 2eme rectangle à l'intérieur)
inc ax
inc bx
dec di
dec bp
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0
; Restaurer les coords pour le texte
mov ax, [gs:si + widget.x]
mov bx, [gs:si + widget.y]
mov di, ax
add di, [gs:si + widget.w]
GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT
jmp .draw_text
.draw_pressed:
; Fond NOIR, Texte BLANC (Inversé)
GFX RECTANGLE_FILL, ax, bx, di, bp, 0 ; Fond Noir
GFX RECTANGLE_DRAW, ax, bx, di, bp, 1 ; Bordure Blanche (optionnel)
; Configuration texte : Blanc sur Noir (ou transparent sur fond noir)
GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT
jmp .draw_text
.draw_disabled:
; Fond Blanc, Bordure en pointillé (difficile sans support pointillé),
; on va faire simple : Bordure noire, mais on n'écrit pas le texte ou texte gris ?
; Pour CGA mono, "Disabled" = Texte normal mais cadre incomplet ou hachuré.
; Faisons simple : Cadre normal, mais texte non affiché ou "..."
GFX RECTANGLE_FILL, ax, bx, di, bp, 1
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0
jmp .done ; Pas de texte pour montrer qu'il est désactivé
.draw_text:
; --- Centrage du texte ---
; Calcul simple : PosX = BoutonX + (LargeurBouton - (LenChaine * 8)) / 2
; Calcul simple : PosY = BoutonY + (HauteurBouton - 8) / 2
push si
; 1. Calculer longueur chaine
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_pos
inc cx
inc di
jmp .strlen
.calc_pos:
; CX = Longueur chaine
shl cx, 3 ; CX * 8 (largeur en pixels du texte)
mov ax, [gs:si + widget.w]
sub ax, cx ; Marge horizontale totale
shr ax, 1 ; Diviser par 2
add ax, [gs:si + widget.x] ; X final
mov bx, [gs:si + widget.h]
sub bx, 8 ; Hauteur fonte (8px)
shr bx, 1 ; Diviser par 2
add bx, [gs:si + widget.y] ; Y final
; 2. Dessiner
GFX GOTOXY, ax, bx
mov dx, [gs:si + widget.text_seg]
mov ax, [gs:si + widget.text_ofs]
GFX WRITE, dx, ax
pop si
.done:
popa
ret
; =============================================================================
; Fonction : gui_update_button
; Description : Met à jour l'état du bouton en fonction de la souris
; Entrée :
; SI = Pointeur vers widget
; CX = Mouse X
; DX = Mouse Y
; BL = Mouse Status (Bit 0 = Clic gauche)
; Sortie :
; AL = 1 si le bouton vient d'être cliqué (Relâché), 0 sinon
; =============================================================================
gui_update_button:
push bx
push cx
push dx
xor ax, ax ; AL = Return value (0)
; Si désactivé, on ne fait rien
cmp byte [gs:si + widget.state], GUI_STATE_DISABLED
je .quit
; 1. Test de collision (Hit Test)
; X >= Widget.X ?
cmp cx, [gs:si + widget.x]
jl .no_hit
; X <= Widget.X + Widget.W ?
mov di, [gs:si + widget.x]
add di, [gs:si + widget.w]
cmp cx, di
jg .no_hit
; Y >= Widget.Y ?
cmp dx, [gs:si + widget.y]
jl .no_hit
; Y <= Widget.Y + Widget.H ?
mov di, [gs:si + widget.y]
add di, [gs:si + widget.h]
cmp dx, di
jg .no_hit
; --- HIT ! La souris est sur le bouton ---
; Test du clic (BL bit 0)
test bl, 1
jz .hovering
; -> Clic maintenu
mov byte [gs:si + widget.state], GUI_STATE_PRESSED
jmp .draw_update
.hovering:
; Souris dessus, mais pas de clic
; Si on était en PRESSED juste avant, c'est un "CLICK" valide (Relâchement)
cmp byte [gs:si + widget.state], GUI_STATE_PRESSED
jne .just_hover
; C'est un click valide !
mov al, 1 ; Return CLICKED
mov byte [gs:si + widget.state], GUI_STATE_HOVER
jmp .draw_update
.just_hover:
mov byte [gs:si + widget.state], GUI_STATE_HOVER
jmp .draw_update
.no_hit:
; Souris en dehors
mov byte [gs:si + widget.state], GUI_STATE_NORMAL
.draw_update:
; Appelle la fonction de dessin pour mettre à jour le visuel
call gui_draw_button
.quit:
pop dx
pop cx
pop bx
ret