diff --git a/src/boot-test.asm b/src/boot-test.asm deleted file mode 100644 index e5d7859..0000000 --- a/src/boot-test.asm +++ /dev/null @@ -1,209 +0,0 @@ -; Compilation: nasm -f bin bios.asm -o bios.bin -; Taille: 64 KB - -BITS 16 - -section .text -org 0x0000 - -%define BIOS_DATA_SEG 0x0050 - -; ------------------------------------------------------------------ -; POINT D'ENTRÉE DU BIOS (Cible du jump du reset vector) -; ------------------------------------------------------------------ -start: - cli - cld - xor ax, ax - mov ds, ax - mov es, ax - mov ss, ax - mov sp, 0x7000 - - ; Créer une table "cohérente" de handler - call setup_ivt - - ; Initialisation du PIC (Indispensable pour IRQ0/IRQ1) - mov al, 0x11 - out 0x20, al - out 0xA0, al - mov al, 0x08 ; IRQ0-7 -> INT 08h-0Fh - out 0x21, al - mov al, 0x70 ; IRQ8-15 -> INT 70h-77h - out 0xA1, al - - mov al, 0x04 - out 0x21, al - mov al, 0x02 - out 0xA1, al - - mov al, 0x01 - out 0x21, al - out 0xA1, al - - ; Scan et Initialisation des options ROMS (VGA BIOS, etc) - call setup_load_rom - - ; initialise l'écran en mode texte - mov ax,0x0003 - int 0x10 - - ; Installation des ISR - mov word [0x08*4], timer_isr - mov word [0x08*4+2], 0xF000 - - mov word [0x09*4], keyboard_isr - mov word [0x09*4+2], 0xF000 - - ; Démasquer IRQ0 et IRQ1 - mov al, 0xFC - out 0x21, al - sti - - mov ax, BIOS_DATA_SEG - mov es, ax - mov di, 0x0 ; Adresse physique de tes variables - mov cx, 4 - xor ax,ax - rep stosb ; Met 4 octets à zéro - -main_loop: - mov ax,BIOS_DATA_SEG - mov fs,ax - - mov al, [fs:0x00] ; Timer - mov di, 0 - call draw_hex_byte - - mov al, [fs:0x01] ; Clavier - mov di, 6 - call draw_hex_byte - - mov al, [fs:0x02] ; Clavier - mov di, 12 - call draw_hex_byte - - jmp main_loop - -; ------------------------------------------------------------------ -; HANDLERS & HELPERS -; ------------------------------------------------------------------ -default_isr: - iret - - -timer_isr: - push ax - push gs - - mov ax, BIOS_DATA_SEG - mov gs, ax - - inc byte [gs:0x00] - - mov al, 0x20 - out 0x20, al - - pop gs - pop ax - iret - -keyboard_isr: - push ax - push gs - - mov ax, BIOS_DATA_SEG - mov gs, ax - - in al, 0x60 ; Ack clavier - mov byte [gs:0x02],al - - inc byte [gs:0x01] - - mov al, 0x20 - out 0x20, al - - pop gs - pop ax - iret - -draw_hex_byte: - push ax - - mov bx, 0xb800 - mov es,bx - - shr al, 4 - call .draw_nibble - pop ax -; add di, 2 - -.draw_nibble: - and al, 0x0F - add al, '0' - cmp al, '9' - jbe .print - add al, 7 -.print: - mov ah, 0x0F ; Blanc sur Noir - stosw - ret - -; ---------------------------------------------------------------------------- -; construire une table "vide" de pointeur ISR -; ---------------------------------------------------------------------------- -setup_ivt: - ; installation de la table d'interrupts - xor ax, ax - mov es, ax ; ES = 0000h -> base IVT - xor di, di ; DI = 0000h -> offset IVT - - mov ax, default_isr ; offset du handler - mov dx, cs ; segment du handler (ROM) - - mov cx, 256 ; 256 vecteurs -.fill: - stosw ; write offset (AX) -> [ES:DI], DI += 2 - xchg ax, dx ; AX = segment, DX = offset - stosw ; write segment (AX) -> [ES:DI], DI += 2 - xchg ax, dx ; AX = offset, DX = segment - loop .fill - ; fin de la table d'interrupt - ret - -; --------------------------------------------------------------------------- -; Détection les ROM supplementaires -; Teste la RAM de 0xC000 jusqu’à 0xE000, par pas de 2KB. -; -; Si une ROM est détectée, le code de la ROM sera appelé. -; --------------------------------------------------------------------------- -setup_load_rom: - mov bx, 0xC000 -.scanloop: - mov gs, bx - cmp word[gs:0x0000],0xAA55 ; ROM signature - jne .nextblock - - ; appel le code (en 0x0003) de la ROM - push cs - push word .nextblock ; address de retour - - push bx ; segment - push 0x0003 ; offset entry rom - retf ; call far ds:0x0003 -.nextblock: - add bx, 0x80 ; add 2kb - cmp bx, 0xE000 - jbe .scanloop - ret - -; ------------------------------------------------------------------ -; Padding jusqu'au reset vector -; ------------------------------------------------------------------ -times 0xFFF0 - ($ - $$) db 0xFF - - -reset_vector: - jmp 0xF000:start - db '06/01/2026' - db 0 ; le stub tient dans 16 octets (ou moins) \ No newline at end of file diff --git a/src/boot.asm b/src/boot.asm index 63550c2..26170ca 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -26,7 +26,6 @@ ; configuration du bios ; ; --------------------------------------------------------------------------- -%define DEBUG_PORT 0xe9 ; 0x402 ou 0xe9 ; initial stack %define STACK_SEG 0x0800 ; segment de base @@ -45,25 +44,15 @@ section .text %include "./common/cursor.asm" ; definition du BDA -%include "./bda.asm" +%include "./common/bda.asm" -; %include "./services/macros.asm" %include "./common/debug_cga.asm" %include "./drivers/gfx_cgam.asm" +%include "./services/gui_lib.asm" %include "./drivers/mouse_ps2.asm" %include "./drivers/keyboard_ps2.asm" %include "./services/generic.asm" -%include "./services/gui_window.asm" - -err_vganok db 'VGA Not Initialized',0 - -cpt_txt db '0123456789A123456789B123456789C123456789D123456789E123456789F123456789G123456789',0 -helloworld db 'Hello World !',0 - -W_T db '1 - TEXT WHITE - TRANSPARENT',0 -B_T db '2 - TEXT BLACK - TRANSPARENT',0 -W_B db '3 - TEXT WHITE - ON BLACK',0 -B_W db '4 - TEXT BLACK - ON WHITE',0 +; %include "./services/gui_window.asm" reset: cli @@ -111,7 +100,12 @@ reset: push cs pop ds - GUI WINDOW, 50,50,250,100,helloworld +setup_gui: + ; Initialiser le segment de données correct pour le texte du bouton + mov ax, cs ; ou DS selon où sont tes chaines + mov [btn_quit + widget.text_seg], ax + + ; GUI WINDOW, 50,50,250,100,helloworld ; GFX RECTANGLE_DRAW, 50,50,250,100,0 @@ -126,6 +120,25 @@ reset: mov ax,BDA_DATA_SEG mov ds,ax endless: + + ; 1. Récupérer souris + mov ax, [BDA_MOUSE + mouse.x] + mov cx, ax ; CX = Souris X + mov ax, [BDA_MOUSE + mouse.y] + mov dx, ax ; DX = Souris Y + mov bl, [BDA_MOUSE + mouse.status] ; BL = Boutons + + ; 2. Gérer le bouton + mov si, btn_quit ; SI pointe sur l'objet + call gui_update_button ; Met à jour l'état et redessine si besoin + + ; 3. Vérifier si cliqué (AL = 1) + cmp al, 1 + je action_quitter + + ; 4. Afficher le curseur souris (Important : après avoir dessiné le bouton) + ; GFX MOUSE_MOVE + GFX TXT_MODE, GFX_TXT_BLACK GFX GOTOXY, 0, 180 @@ -139,6 +152,10 @@ endless: jmp endless +action_quitter: + hlt + jmp action_quitter + ; ----------------------------------------------------------- ; timer ISR (IRQ -> INT) @@ -158,6 +175,21 @@ timer_isr: pop ax iret +; Déclaration d'un bouton "Quitter" +btn_quit: + istruc widget + at widget.x, dw 10 + at widget.y, dw 10 + at widget.w, dw 80 + at widget.h, dw 20 + at widget.state, db GUI_STATE_NORMAL + at widget.text_ofs, dw str_quit + at widget.text_seg, dw 0 ; Sera rempli au runtime (CS ou DS) + at widget.id, db 1 + iend + +str_quit db "QUITTER", 0 + ; ------------------------------------------------------------------ ; Padding jusqu'au reset vector ; ------------------------------------------------------------------ diff --git a/src/bda.asm b/src/common/bda.asm similarity index 73% rename from src/bda.asm rename to src/common/bda.asm index d230208..b58a8b4 100644 --- a/src/bda.asm +++ b/src/common/bda.asm @@ -24,7 +24,7 @@ ; https://wiki.nox-rhea.org/back2root/ibm-pc-ms-dos/hardware/informations/bios_data_area ; -%define BDA_SEGMENT 0x0040 ; segment BDA +%define BDA_SEGMENT 0x0040 ; segment BDA ; custom vars %define BDA_INFO_MEM_SIZE 0x0014 ; Memory size in Kbytes @@ -46,34 +46,33 @@ %define BKG_TOTAL_BYTES (BKG_TOTAL_DWORDS * 4) ; 64 struc mouse - .buffer resb 4 ; i8042 input buffer - .idx resb 1 ; index in the buffer - .packetlen resb 1 ; max buffer len (3 ou 4) - .status resb 1 ; mouse status (button etc) - .wheel resb 1 ; if a packet size is 4; experimental - .x resw 1 ; - .y resw 1 ; - ; cursor management - .cur_x resw 1 ; preservation des x,y du background - .cur_y resw 1 ; - .cur_addr_start resw 1 ; adresse de départ de la sauvegarde - - .cur_drawing resb 1 ; - .cur_counter resb 1 ; compteur de fois que le curseur a été caché (0 = visible, <0 = invisible) - .cur_seg resw 1 ; segment / offset of the pointer - .cur_ofs resw 1 ; - .bkg_saved resb 1 ; background saved - alignb 4 ; alignement 4 bytes - .bkg_buffer resd 16 ; buffer for saved background + .buffer resb 4 ; i8042 input buffer + .idx resb 1 ; index in the buffer + .packetlen resb 1 ; max buffer len (3 ou 4) + .status resb 1 ; mouse status (button etc) + .wheel resb 1 ; if a packet size is 4; experimental + .x resw 1 ; + .y resw 1 ; + ; cursor management + .cur_x resw 1 ; preservation des x,y du background + .cur_y resw 1 ; + .cur_addr_start resw 1 ; adresse de départ de la sauvegarde + .cur_drawing resb 1 ; + .cur_counter resb 1 ; compteur de fois que le curseur a été caché (0 = visible, <0 = invisible) + .cur_seg resw 1 ; segment / offset of the pointer + .cur_ofs resw 1 ; + .bkg_saved resb 1 ; background saved + alignb 4 ; alignement 4 bytes + .bkg_buffer resd 16 ; buffer for saved background endstruc struc gfx - .cur_x resw 1 ; x,y en pixel - .cur_y resw 1 - .cur_offset resw 1 ; offset calculé pour le prochain caractère - .cur_line_ofs resw 1 ; "interligne" +2000h ou -2000h - .cur_shift resb 1 ; x&7 (0..7) - .cur_mode resb 1 ; + .cur_x resw 1 ; x,y en pixel + .cur_y resw 1 + .cur_offset resw 1 ; offset calculé pour le prochain caractère + .cur_line_ofs resw 1 ; "interligne" +2000h ou -2000h + .cur_shift resb 1 ; x&7 (0..7) + .cur_mode resb 1 ; endstruc %define BDA_MOUSE 0x0000 diff --git a/src/common/debug_txt.asm b/src/common/debug_txt.asm index 1999bd5..c1e07e8 100644 --- a/src/common/debug_txt.asm +++ b/src/common/debug_txt.asm @@ -20,6 +20,7 @@ ; along with this program. If not, see . ; ; ============================================================================= +%define DEBUG_PORT 0xe9 ; 0x402 ou 0xe9 debug_puts: .next: diff --git a/src/drivers/gfx_cgam.asm b/src/drivers/gfx_cgam.asm index dbdd7be..dda21c7 100644 --- a/src/drivers/gfx_cgam.asm +++ b/src/drivers/gfx_cgam.asm @@ -215,8 +215,8 @@ cga_set_charpos: mov bp, sp ; --- Définition des arguments --- - %define .x word [bp+4] - %define .y word [bp+6] + %define .x word [bp+4] + %define .y word [bp+6] pusha push fs @@ -260,6 +260,10 @@ cga_set_charpos: mov [fs:BDA_GFX + gfx.cur_offset], bx pop fs + + ; clean defs + %undef .x + %undef .y popa leave ret @@ -460,6 +464,11 @@ cga_putpixel: and byte [es:di], ah .done: + + ; clean defs + %undef .x + %undef .y + %undef .color popa leave ret @@ -491,6 +500,10 @@ cga_getpixel: pop es pop di + + ; clean defs + %undef .x + %undef .y leave ret @@ -592,6 +605,12 @@ cga_line_vertical: call cga_mouse_show pop es + + ; clean defs + %undef .x + %undef .y1 + %undef .y2 + %undef .color popa leave ; Leave gère le 'mov sp, bp / pop bp' ret @@ -735,6 +754,11 @@ cga_line_horizontal: .done: call cga_mouse_show pop es + + ; clean defs + %undef .x1 + %undef .x2 + %undef .y popa leave ret @@ -787,6 +811,10 @@ cga_draw_rect: GFX LINE_VERT, .x1, .y1, .y2, .color GFX LINE_VERT, .x2, .y1, .y2, .color + ; clean defs + %undef .x1 + %undef .y1 + %undef .x2 popa leave ret @@ -938,6 +966,11 @@ cga_fill_rect: call cga_mouse_show pop es + + ; clean defs + %undef .x1 + %undef .y1 + %undef .x2 popa leave ret diff --git a/src/services/gui_lib.asm b/src/services/gui_lib.asm new file mode 100644 index 0000000..3fd48e5 --- /dev/null +++ b/src/services/gui_lib.asm @@ -0,0 +1,546 @@ +; ============================================================================= +; Project : Custom BIOS / ROM +; File : gui_lib.asm +; Author : frater +; +; License : GNU General Public License v3.0 or later (GPL-3.0+) +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; +; ============================================================================= + +; --- Configuration Mémoire --- +%define GUI_RAM_SEG 0x0800 ; Segment de données UI +%define GUI_MAX_WIDGETS 32 ; Nombre max de widgets simultanés + +; --- Drapeaux & États --- +%define GUI_STATE_FREE 0 ; Le slot est vide (mémoire dispo) +%define GUI_STATE_NORMAL 1 ; Affiché, repos +%define GUI_STATE_HOVER 2 ; Souris dessus +%define GUI_STATE_PRESSED 3 ; Clic enfoncé +%define GUI_STATE_DISABLED 4 ; Grisé + +; --- Structure d'un OBJET (Bouton, etc) --- +struc widget + .state resb 1 ; État (0=libre, >0=utilisé) + .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 + .state resb 1 ; État (0=Normal, 1=Hover, 2=Pressed, 3=Disabled) + .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" + alignb 2 ; Alignement mémoire pour performance +endstruc + +; ============================================================================= +; SECTION : GESTION MÉMOIRE (ALLOCATION / LIBÉRATION) +; ============================================================================= + +; ----------------------------------------------------------------------------- +; gui_init_system +; Initialise toute la mémoire des widgets à 0 (Libre) +; Entrée : DS doit pointer vers GUI_RAM_SEG +; ----------------------------------------------------------------------------- +gui_init_system: + pushad + push ds + + mov ax, GUI_RAM_SEG + mov ds, ax + + mov di, 0 ; On commence au début du segment 0x0800 + mov cx, (widget_size * GUI_MAX_WIDGETS) + shl cx, 2 ; cx / 4 + xor eax, eax + rep stosd ; Remplit tout de 0 + + pop ds + popad + ret + +; ----------------------------------------------------------------------------- +; gui_alloc_widget +; Cherche un slot vide et retourne son adresse +; Sortie : +; - Succès : CF=0 (Carry Clear), SI = Offset du widget +; - Echec : CF=1 (Carry Set), SI = Indéfini (ou 0) +; ----------------------------------------------------------------------------- +gui_alloc_widget: + push cx + push bx + push ds + + mov ax, GUI_RAM_SEG + mov ds, ax + + mov si, 0 ; Début du pool + mov cx, GUI_MAX_WIDGETS + +.loop_find: + cmp byte [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 + + ; Pas trouvé (Plein) + stc ; set Carry Flag : erreur + jmp .done + +.found: + ; On initialise le slot trouvé + mov byte [si + widget.state], GUI_STATE_NORMAL ; Marquer comme occupé + + ; Reset des champs critiques pour éviter les déchets + mov word [si + widget.callback], 0 + + clc ; Clear Carry Flag +.done: + pop ds + pop bx + pop cx + ret + +; ----------------------------------------------------------------------------- +; gui_free_widget +; Libère un widget +; Entrée : SI = Pointeur widget +; ----------------------------------------------------------------------------- +gui_free_widget: + mov byte [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. + ret + + +; ============================================================================= +; SECTION : MOTEUR D'ÉVÉNEMENTS (EVENT LOOP) +; ============================================================================= + +; ----------------------------------------------------------------------------- +; gui_process_all +; 1. Redessine tous les widgets actifs +; 2. Vérifie les clics et appelle les callbacks +; Entrée : +; CX = Mouse X +; DX = Mouse Y +; BL = Mouse Buttons +; ----------------------------------------------------------------------------- +gui_process_all: + pusha + + mov si, 0 ; Pointeur début tableau + mov di, GUI_MAX_WIDGETS ; Compteur + + .loop_widgets: + ; Est-ce que ce slot est occupé ? + cmp byte [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 + + ; 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 [si + widget.callback], 0 + je .no_action + + ; APPEL DU CALLBACK (Fonction utilisateur) + ; On sauve les registres car le callback peut faire n'importe quoi + pusha + call word [si + widget.callback] + popa + + .no_action: + pop bx ; Restaurer boutons + + ; Dessiner (si l'état a changé ou pour refresh) + call gui_draw_single_widget + + .next_widget: + add si, widget_size + dec di + jnz .loop_widgets + + popa + ret + +; ============================================================================= +; SECTION : LOGIQUE INTERNE ET DESSIN +; ============================================================================= + +; (Interne) Met à jour l'état d'un seul widget +; Entrée : SI=Widget, CX=MouseX, DX=MouseY, BL=Buttons +; Sortie : AL=1 si Clicked, 0 sinon. Met à jour [SI].state +gui_update_logic: + xor ax, ax + + cmp byte [si + widget.state], GUI_STATE_DISABLED + je .done + + ; --- Hit Test --- + cmp cx, [si + widget.x] + jl .miss + mov bp, [si + widget.x] + add bp, [si + widget.w] + cmp cx, bp + jg .miss + + cmp dx, [si + widget.y] + jl .miss + mov bp, [si + widget.y] + add bp, [si + widget.h] + cmp dx, bp + jg .miss + + ; --- Hit: Souris sur le widget --- + test bl, 1 ; Clic gauche ? + jz .released + + ; Clic enfoncé + mov byte [si + widget.state], GUI_STATE_PRESSED + jmp .done + +.released: + ; Bouton relâché. Était-il pressé avant ? + cmp byte [si + widget.state], GUI_STATE_PRESSED + jne .hover + + ; Clic validé ! + mov al, 1 + mov byte [si + widget.state], GUI_STATE_HOVER + jmp .done + +.hover: + mov byte [si + widget.state], GUI_STATE_HOVER + jmp .done + +.miss: + mov byte [si + widget.state], GUI_STATE_NORMAL +.done: + ret + +; (Interne) Dessine le widget pointé par SI +gui_draw_single_widget: + pusha + + ; Chargement coords + mov ax, [si + widget.x] + mov bx, [si + widget.y] + mov cx, [si + widget.w] + mov dx, [si + widget.h] + + ; Calcul X2, Y2 + mov di, ax + add di, cx + mov bp, bx + add bp, dx + + ; Dispatch selon état + mov al, [si + widget.state] + cmp al, GUI_STATE_PRESSED + je .paint_pressed + cmp al, GUI_STATE_HOVER + je .paint_hover + +.paint_normal: + GFX RECTANGLE_FILL, ax, bx, di, bp, 1 ; Blanc + GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 ; Bord Noir + GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT + jmp .text + +.paint_hover: + GFX RECTANGLE_FILL, ax, bx, di, bp, 1 + GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 + ; Effet gras + inc ax + inc bx + dec di + dec bp + GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 + + ; Restauration coords pour le texte + mov ax, [si + widget.x] + mov bx, [si + widget.y] + jmp .text_setup + +.paint_pressed: + GFX RECTANGLE_FILL, ax, bx, di, bp, 0 ; Noir + GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT + jmp .text + +.text_setup: + ; Petit hack pour recentrer le texte après l'effet gras + mov cx, [si + widget.w] + mov dx, [si + widget.h] + +.text: + ; --- Centrage Texte (Simplifié) --- + push si + mov es, [si + widget.text_seg] + mov di, [si + widget.text_ofs] + xor cx, cx +.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, [si + widget.w] + sub ax, cx + shr ax, 1 + pop bx ; Récupère X original dans BX (oups, on veut ajouter) + add ax, bx ; X final + + push ax ; X final prêt + + mov bx, [si + widget.h] + sub bx, 8 + shr bx, 1 + add bx, [si + widget.y] ; Y final + + pop cx ; CX = X Final, BX = Y Final + + GFX GOTOXY, cx, bx + + mov dx, [si + widget.text_seg] + mov ax, [si + widget.text_ofs] + GFX WRITE, dx, ax + + pop si + popa + 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, [si + widget.x] + mov bx, [si + widget.y] + mov cx, [si + widget.w] + mov dx, [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 [si + widget.state], GUI_STATE_PRESSED + je .draw_pressed + + cmp byte [si + widget.state], GUI_STATE_HOVER + je .draw_hover + + cmp byte [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, [si + widget.x] + mov bx, [si + widget.y] + mov di, ax + add di, [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, [si + widget.text_seg] + mov di, [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, [si + widget.w] + sub ax, cx ; Marge horizontale totale + shr ax, 1 ; Diviser par 2 + add ax, [si + widget.x] ; X final + + mov bx, [si + widget.h] + sub bx, 8 ; Hauteur fonte (8px) + shr bx, 1 ; Diviser par 2 + add bx, [si + widget.y] ; Y final + + ; 2. Dessiner + GFX GOTOXY, ax, bx + + mov dx, [si + widget.text_seg] + mov ax, [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 [si + widget.state], GUI_STATE_DISABLED + je .quit + + ; 1. Test de collision (Hit Test) + ; X >= Widget.X ? + cmp cx, [si + widget.x] + jl .no_hit + + ; X <= Widget.X + Widget.W ? + mov di, [si + widget.x] + add di, [si + widget.w] + cmp cx, di + jg .no_hit + + ; Y >= Widget.Y ? + cmp dx, [si + widget.y] + jl .no_hit + + ; Y <= Widget.Y + Widget.H ? + mov di, [si + widget.y] + add di, [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 [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 [si + widget.state], GUI_STATE_PRESSED + jne .just_hover + + ; C'est un click valide ! + mov al, 1 ; Return CLICKED + mov byte [si + widget.state], GUI_STATE_HOVER + jmp .draw_update + +.just_hover: + mov byte [si + widget.state], GUI_STATE_HOVER + jmp .draw_update + +.no_hit: + ; Souris en dehors + mov byte [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 \ No newline at end of file diff --git a/src/services/gui_window.asm b/src/services/gui_window.asm index ee8b267..57b73a9 100644 --- a/src/services/gui_window.asm +++ b/src/services/gui_window.asm @@ -73,10 +73,10 @@ draw_window_source: mov bp, sp ; --- Arguments --- - %define .x word [bp+4] - %define .y word [bp+6] - %define .w word [bp+8] - %define .h word [bp+10] + %define .xa word [bp+4] + %define .ya word [bp+6] + %define .wa word [bp+8] + %define .ha word [bp+10] %define .title word [bp+12] pusha @@ -89,19 +89,19 @@ draw_window_source: ; ---------------------------------------------------- ; On dessine un rectangle noir décalé de +1,+1 ; x1+1, y1+1, x2+1, y2+1 -> NOIR - mov ax, .x - add ax, .w + mov ax, .xa + add ax, .wa inc ax ; x2 + 1 mov bx, ax ; BX = right limit - mov ax, .y - add ax, .h + mov ax, .ya + add ax, .ha inc ax ; y2 + 1 mov dx, ax ; DX = bottom limit - mov ax, .x + mov ax, .xa inc ax ; x1 + 1 - mov cx, .y + mov cx, .ya inc cx ; y1 + 1 ; Appel cga_fill_rect(x+1, y+1, x+w+1, y+h+1, BLACK) @@ -118,17 +118,17 @@ draw_window_source: ; ---------------------------------------------------- ; On dessine un rectangle BLANC par dessus l'ombre ; Cela crée le corps et "découpe" l'ombre pour ne laisser que le bord visible. - mov bx, .x - add bx, .w ; BX = x2 - mov dx, .y - add dx, .h ; DX = y2 + mov bx, .xa + add bx, .wa ; BX = x2 + mov dx, .ya + add dx, .ha ; DX = y2 ; Remplissage Blanc (Corps) push word 1 ; Couleur 1 (Blanc) push dx push bx - push word .y - push word .x + push word .ya + push word .xa call cga_fill_rect add sp, 10 @@ -136,8 +136,8 @@ draw_window_source: push word 0 ; Couleur 0 (Noir) push dx push bx - push word .y - push word .x + push word .ya + push word .xa call cga_draw_rect add sp, 10 @@ -148,13 +148,13 @@ draw_window_source: %define TITLE_HEIGHT 18 ; Dessiner la ligne de séparation sous le titre - mov cx, .y + mov cx, .ya add cx, TITLE_HEIGHT push word 0 ; Couleur Noir push cx ; Y de la ligne push bx ; X2 (largeur fenêtre) - push word .x ; X1 + push word .xa ; X1 call cga_line_horizontal add sp, 8 @@ -162,11 +162,11 @@ draw_window_source: ; On dessine une ligne noire une ligne sur deux à l'intérieur du titre ; De y+1 à y+17, step 2 - mov si, .y + mov si, .ya inc si ; Commence à y+1 inc si ; Premier trait noir à y+2 (laissons 1px blanc sous le cadre haut) - mov di, .y + mov di, .ya add di, TITLE_HEIGHT dec di ; Fin juste avant la ligne de séparation @@ -177,7 +177,7 @@ draw_window_source: push word 0 ; Noir push si ; Y courant push bx ; X2 - push word .x ; X1 + push word .xa ; X1 call cga_line_horizontal add sp, 8 @@ -191,9 +191,9 @@ draw_window_source: ; Petit carré blanc à gauche avec contour noir ; Position: x+6, y+4, taille 9x9 (exemple) - mov cx, .x + mov cx, .xa add cx, 6 ; Box X1 - mov dx, .y + mov dx, .ya add dx, 4 ; Box Y1 mov si, cx @@ -236,9 +236,9 @@ draw_window_source: .calc_center: ; b) Calcul X Centré = X_win + (W_win/2) - (Len * 8 / 2) - mov ax, .w + mov ax, .wa shr ax, 1 ; W / 2 - add ax, .x ; Centre de la fenêtre + add ax, .xa ; Centre de la fenêtre mov bx, cx ; Sauver Len shl cx, 2 ; Len * 4 (car Len * 8 / 2 = Len * 4) @@ -249,7 +249,7 @@ draw_window_source: mov cx, ax ; Text X start sub cx, 4 ; Padding gauche (Rect X1) - mov dx, .y + mov dx, .ya add dx, 2 ; Rect Y1 (juste sous le cadre haut) push bx ; Sauve longueur char pour plus tard @@ -261,7 +261,7 @@ draw_window_source: add si, ax add si, 4 ; Padding droite (Rect X2) - mov di, .y + mov di, .ya add di, TITLE_HEIGHT dec di ; Rect Y2 (juste au dessus ligne séparation) @@ -282,7 +282,7 @@ draw_window_source: ; GFX GOTOXY (X, Y) -> Y centré dans la barre de titre ; Centre barre ~9px. Font 8px. Y = Win_Y + 5 - mov dx, .y + mov dx, .ya add dx, 5 ; Note: GFX GOTOXY utilise des arguments pile, attention à l'ordre API @@ -319,10 +319,10 @@ draw_window: mov bp, sp ; --- Arguments --- - %define .x word [bp+4] - %define .y word [bp+6] - %define .w word [bp+8] - %define .h word [bp+10] + %define .xa word [bp+4] + %define .ya word [bp+6] + %define .wa word [bp+8] + %define .ha word [bp+10] %define .title word [bp+12] ; ---- Variables Locales --- @@ -332,19 +332,19 @@ draw_window: ; ---------------------------------------------------- ; On dessine un rectangle noir décalé de +1,+1 ; x1+1, y1+1, x2+1, y2+1 -> NOIR - mov ax, .x - add ax, .w + mov ax, .xa + add ax, .wa inc ax ; x2 + 1 mov bx, ax ; BX = right limit - mov ax, .y - add ax, .h + mov ax, .ya + add ax, .ha inc ax ; y2 + 1 mov dx, ax ; DX = bottom limit - mov ax, .x + mov ax, .xa inc ax ; x1 + 1 - mov cx, .y + mov cx, .ya inc cx ; y1 + 1 GFX RECTANGLE_DRAW, ax, cx, bx, dx, 0