diff --git a/src/common/chartable-1.asm b/src/alternatives/chartable-1.asm similarity index 100% rename from src/common/chartable-1.asm rename to src/alternatives/chartable-1.asm diff --git a/src/boot.asm b/src/boot.asm index 4cadbcf..6789b65 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -42,17 +42,19 @@ section .text %include "./common/chartable.asm" %include "./common/cursor.asm" +%include "./common/generic.asm" ; definition du BDA %include "./common/bda.asm" -; %include "./common/debug_cga.asm" +; drivers %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" + +; GUI +%include "./gui/lib.asm" +%include "./gui/draw.asm" reset: cli diff --git a/src/common/bda.asm b/src/common/bda.asm index b58a8b4..2615e85 100644 --- a/src/common/bda.asm +++ b/src/common/bda.asm @@ -88,45 +88,6 @@ endstruc ; 0x03C00-0x08000 : stack segment ; 0x - - -; ----------------------------------------------------------------------------------- -; PC components I/O ports -; ----------------------------------------------------------------------------------- - -; controleur clavier/souris i8042 (AT-PS/2) -%define i8042_PS2_DATA 0x60 -%define i8042_PS2_CTRL 0x64 - -; PIC 8259 ports (Programmable Interrupt Controller) -; PIC 1 : Master -%define i8259_MASTER_CMD 0x20 -%define i8259_MASTER_DATA 0x21 -; PIC 2 : Slave -%define i8259_SLAVE_CMD 0xA0 -%define i8259_SLAVE_DATA 0xA1 - -; i8259 Commands -%define ICW1_ICW4 0x01 ; Indicates that ICW4 will be present -%define ICW1_SINGLE 0x02 ; Single (cascade) mode -%define ICW1_INTERVAL4 0x04 ; Call address interval 4 (8) -%define ICW1_LEVEL 0x08 ; Level triggered (edge) mode -%define ICW1_INIT 0x10 ; Initialization - required! - -%define ICW4_8086 0x01 ; 8086/88 (MCS-80/85) mode -%define ICW4_AUTO 0x02 ; Auto (normal) EOI -%define ICW4_BUF_SLAVE 0x08 ; Buffered mode/slave -%define ICW4_BUF_MASTER 0x0C ; Buffered mode/master -%define ICW4_SFNM 0x10 ; Special fully nested (not) -%define PIC_EOI 0x20 - -; Remap : IRQ0..7 -> 0x08..0x0F, IRQ8..15 -> 0x70..0x77 -%define i8259_MASTER_INT 0x08 -%define i8259_SLAVE_INT 0x70 - -%define IRQ_ENABLED 0x00 -%define IRQ_DISABLED 0x01 - ; ----------------------------------------------------------------------------------- ; bda_setup ; Initialise la BDA à zéro diff --git a/src/services/generic.asm b/src/common/generic.asm similarity index 61% rename from src/services/generic.asm rename to src/common/generic.asm index c624480..92a0d88 100644 --- a/src/services/generic.asm +++ b/src/common/generic.asm @@ -20,6 +20,44 @@ ; ; ============================================================================= + +; ----------------------------------------------------------------------------------- +; PC components I/O ports +; ----------------------------------------------------------------------------------- + +; controleur clavier/souris i8042 (AT-PS/2) +%define i8042_PS2_DATA 0x60 +%define i8042_PS2_CTRL 0x64 + +; PIC 8259 ports (Programmable Interrupt Controller) +; PIC 1 : Master +%define i8259_MASTER_CMD 0x20 +%define i8259_MASTER_DATA 0x21 +; PIC 2 : Slave +%define i8259_SLAVE_CMD 0xA0 +%define i8259_SLAVE_DATA 0xA1 + +; i8259 Commands +%define ICW1_ICW4 0x01 ; Indicates that ICW4 will be present +%define ICW1_SINGLE 0x02 ; Single (cascade) mode +%define ICW1_INTERVAL4 0x04 ; Call address interval 4 (8) +%define ICW1_LEVEL 0x08 ; Level triggered (edge) mode +%define ICW1_INIT 0x10 ; Initialization - required! + +%define ICW4_8086 0x01 ; 8086/88 (MCS-80/85) mode +%define ICW4_AUTO 0x02 ; Auto (normal) EOI +%define ICW4_BUF_SLAVE 0x08 ; Buffered mode/slave +%define ICW4_BUF_MASTER 0x0C ; Buffered mode/master +%define ICW4_SFNM 0x10 ; Special fully nested (not) +%define PIC_EOI 0x20 + +; Remap : IRQ0..7 -> 0x08..0x0F, IRQ8..15 -> 0x70..0x77 +%define i8259_MASTER_INT 0x08 +%define i8259_SLAVE_INT 0x70 + +%define IRQ_ENABLED 0x00 +%define IRQ_DISABLED 0x01 + ; --------------------------------------------------------------------------- ; Détection les ROM supplementaires ; Teste la RAM de 0xC000 jusqu’à 0xE000, par pas de 2KB. @@ -66,11 +104,11 @@ setup_check_vga: ; Comparer avec default_isr (offset) et CS (segment) mov dx, default_isr ; DX = offset default_isr (dans notre CS) cmp bx, dx - jne .init_ok ; vecteur different, bios initialisé + jne .init_ok ; vecteur different, bios initialisé cmp cx, ax jne .init_ok - mov ax,cs ; vecteur identique, sans doute pas d'initialisation + mov ax,cs ; vecteur identique, sans doute pas d'initialisation mov ds,ax .init_ok: ret @@ -81,19 +119,19 @@ setup_check_vga: ivt_setup: ; installation de la table d'interrupts xor ax, ax - mov es, ax ; ES = 0000h -> base IVT - xor di, di ; DI = 0000h -> offset IVT + 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 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 + 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 @@ -109,17 +147,17 @@ ivt_setvector: push es push di - shl ax,2 ; ax=id *4 + shl ax,2 ; ax=id *4 mov di,ax xor ax,ax - mov es, ax ; ES = 0000h -> base IVT + mov es, ax ; ES = 0000h -> base IVT - mov ax, bx ; offset du handler + mov ax, bx ; offset du handler cli - stosw ; write offset (AX) -> [ES:DI], DI += 2 - xchg ax, dx ; AX = segment, DX = offset - stosw ; write segment (AX) -> [ES:DI], DI += 2 + stosw ; write offset (AX) -> [ES:DI], DI += 2 + xchg ax, dx ; AX = segment, DX = offset + stosw ; write segment (AX) -> [ES:DI], DI += 2 sti pop di @@ -153,17 +191,17 @@ pic_init: out i8259_SLAVE_CMD, al mov al, i8259_MASTER_INT - out i8259_MASTER_DATA, al ; ICW2: Master PIC vector offset + out i8259_MASTER_DATA, al ; ICW2: Master PIC vector offset mov al, i8259_SLAVE_INT - out i8259_SLAVE_DATA, al ; ICW2: Slave PIC vector offset + out i8259_SLAVE_DATA, al ; ICW2: Slave PIC vector offset ; Master: cascade (IRQ2 => 1 << IRQ2 = 0x04) mov al, 0x04 ; master: slave on IRQ2: 0100b - out i8259_MASTER_DATA, al ; ICW3: tell Master PIC that there is a slave PIC at IRQ2 + out i8259_MASTER_DATA, al ; ICW3: tell Master PIC that there is a slave PIC at IRQ2 ; Slave: numéro de ligne sur master (2) - mov al, 0x02 ; slave: cascade identity 2 - out i8259_SLAVE_DATA, al ; ICW3: tell Slave PIC its cascade identity (0000 0010) + mov al, 0x02 ; slave: cascade identity 2 + out i8259_SLAVE_DATA, al ; ICW3: tell Slave PIC its cascade identity (0000 0010) ; ICW4: 8086 mode mov al, ICW4_8086 @@ -181,7 +219,7 @@ pic_init: pic_set_irq_mask: pusha - mov cl, al ; CL = numéro d'IRQ + mov cl, al ; CL = numéro d'IRQ ; Par défaut, PIC Maître (Data port) mov dx, i8259_MASTER_DATA @@ -189,30 +227,30 @@ pic_set_irq_mask: jl .is_master ; Si IRQ >= 8, on passe sur le PIC Esclave - sub cl, 8 ; Ajuste l'index du bit (8-15 -> 0-7) + sub cl, 8 ; Ajuste l'index du bit (8-15 -> 0-7) ; Port Data du PIC Esclave mov dx, i8259_SLAVE_DATA .is_master: ; Préparation du masque de bit mov bl, 1 - shl bl, cl ; BL contient maintenant le bit correspondant (ex: IRQ 3 -> 00001000) + shl bl, cl ; BL contient maintenant le bit correspondant (ex: IRQ 3 -> 00001000) - in al, dx ; Lire l'IMR actuel depuis le port (0x21 ou 0xA1) + in al, dx ; Lire l'IMR actuel depuis le port (0x21 ou 0xA1) - test ah, ah ; AH est-il à 1 (Disable) ? + test ah, ah ; AH est-il à 1 (Disable) ? jnz .do_disable .do_enable: - not bl ; Inverser le masque (ex: 11110111) - and al, bl ; Mettre le bit à 0 (Unmask) + not bl ; Inverser le masque (ex: 11110111) + and al, bl ; Mettre le bit à 0 (Unmask) jmp .write_back .do_disable: - or al, bl ; Mettre le bit à 1 (Mask) + or al, bl ; Mettre le bit à 1 (Mask) .write_back: - out dx, al ; Écrire le nouveau masque dans l'IMR + out dx, al ; Écrire le nouveau masque dans l'IMR popa ret diff --git a/src/gui/draw.asm b/src/gui/draw.asm new file mode 100644 index 0000000..aa8875c --- /dev/null +++ b/src/gui/draw.asm @@ -0,0 +1,491 @@ +; ============================================================================= +; 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 . +; +; ============================================================================= + +; +; https://github.com/buserror/libmui +; + +; Dessine le widget pointé par SI +gui_draw_single_widget: + pusha + + GFX MOUSE_HIDE ; On cache la souris AVANT de commencer le dessin du widget + + mov al, [gs:si + widget.state] + cmp al, [gs:si + widget.oldstate] + je .done + + mov [gs:si + widget.oldstate], al ; marque comme à jour + + ; Chargement coords + mov ax, [gs:si + widget.x] + mov bx, [gs:si + widget.y] + + mov cx, [gs:si + widget.w] + mov dx, [gs:si + widget.h] + + ; Calcul X2, Y2 + add cx, ax + add dx, bx + + mov [gs:si + widget.x2], cx + mov [gs:si + widget.y2], dx + + ; 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: + cmp byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX + jne .case_5 + call draw_checkbox + jmp .done + + .case_5: + + + .done: + GFX MOUSE_SHOW ; On réaffiche la souris APRES, capturant le widget fini + popa + ret + +; --- Locals Mapping --- +%define .thumb_x1 word [bp-2] +%define .thumb_y1 word [bp-4] +%define .thumb_x2 word [bp-6] +%define .thumb_y2 word [bp-8] +%define .track_x1 word [bp-10] +%define .track_y1 word [bp-12] +%define .track_x2 word [bp-14] +%define .track_y2 word [bp-16] +draw_slider: + push bp + mov bp, sp + sub sp, 16 ; Reserve space for locals + + ; Save track coords (Arguments passed in AX, BX, CX, DX) + mov .track_x1, ax + mov .track_y1, bx + mov .track_x2, cx + mov .track_y2, dx + + ; 1. Dessiner la piste (Track) + GFX RECTANGLE_FILL, .track_x1, .track_y1, .track_x2, .track_y2, PATTERN_WHITE_LIGHT + GFX RECTANGLE, .track_x1, .track_y1, .track_x2, .track_y2, 0 + + ; 2. Calculer la taille du curseur (Thumb) + 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 + + ; Thumb X1 = attr_val + mov cx, [gs:si + widget.attr_val] + + ; --- CLAMP X1 (Fix Overflow) --- + ; Max X1 = TrackX2 - ThumbW + mov dx, .track_x2 + sub dx, ax ; DX = Max X1 + cmp cx, dx + jle .chk_min_x + mov cx, dx + .chk_min_x: + cmp cx, .track_x1 + jge .ok_x1 + mov cx, .track_x1 + .ok_x1: + mov .thumb_x1, cx + + ; Thumb X2 = X1 + Width + add cx, ax + mov .thumb_x2, cx + + ; Thumb Y1 = Track Y1 + mov ax, .track_y1 + mov .thumb_y1, ax + + ; Thumb Y2 = Track Y2 + mov ax, .track_y2 + mov .thumb_y2, ax + 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 + + ; Thumb Y1 = attr_val + mov cx, [gs:si + widget.attr_val] + + ; --- CLAMP Y1 (Fix Overflow) --- + ; Max Y1 = TrackY2 - ThumbH + mov dx, .track_y2 + sub dx, ax ; DX = Max Y1 + cmp cx, dx + jle .chk_min_y + mov cx, dx + .chk_min_y: + cmp cx, .track_y1 + jge .ok_y1 + mov cx, .track_y1 + .ok_y1: + mov .thumb_y1, cx + + ; Thumb Y2 = Y1 + Height + add cx, ax + mov .thumb_y2, cx + + ; Thumb X1 = Track X1 + mov ax, .track_x1 + mov .thumb_x1, ax + + ; Thumb X2 = Track X2 + mov ax, .track_x2 + mov .thumb_x2, ax + + .draw_thumb: + ; 3. Dessiner le curseur (Thumb) + GFX RECTANGLE_FILL, .thumb_x1, .thumb_y1, .thumb_x2, .thumb_y2, PATTERN_WHITE + GFX RECTANGLE, .thumb_x1, .thumb_y1, .thumb_x2, .thumb_y2, 0 + + ; --- Grip Lines (3 lignes pour un look moderne) --- + cmp byte [gs:si + widget.attr_mode], 2 + je .grip_v + + ; --- Grip Horizontal (Lignes verticales) --- + ; Center X + mov ax, .thumb_x1 + add ax, .thumb_x2 + shr ax, 1 ; AX = Center X + + ; Y bounds (padding 3px) + mov bx, .thumb_y1 + add bx, 3 + mov dx, .thumb_y2 + sub dx, 3 + + ; Center Line + GFX LINE, ax, bx, ax, dx, 0 + + ; Left Line + mov cx, ax + sub cx, 2 + cmp cx, .thumb_x1 + jle .skip_left + GFX LINE, cx, bx, cx, dx, 0 + .skip_left: + + ; Right Line + mov cx, ax + add cx, 2 + cmp cx, .thumb_x2 + jge .grip_done + GFX LINE, cx, bx, cx, dx, 0 + jmp .grip_done + + .grip_v: + ; --- Grip Vertical (Lignes horizontales) --- + ; Center Y + mov bx, .thumb_y1 + add bx, .thumb_y2 + shr bx, 1 ; BX = Center Y + + ; X bounds (padding 3px) + mov ax, .thumb_x1 + add ax, 3 + mov cx, .thumb_x2 + sub cx, 3 + + ; Center Line + GFX LINE, ax, bx, cx, bx, 0 + + ; Top Line + mov dx, bx + sub dx, 2 + cmp dx, .thumb_y1 + jle .skip_top + GFX LINE, ax, dx, cx, dx, 0 + .skip_top: + + ; Bottom Line + mov dx, bx + add dx, 2 + cmp dx, .thumb_y2 + jge .grip_done + GFX LINE, ax, dx, cx, dx, 0 + + .grip_done: + leave + ret + +; Clean up defines +%undef .thumb_x1 +%undef .thumb_y1 +%undef .thumb_x2 +%undef .thumb_y2 +%undef .track_x1 +%undef .track_y1 +%undef .track_x2 +%undef .track_y2 + +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 + + .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, ax, bx, cx, dx, 0 + jmp .done + + .paint_hover: + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE_LIGHT + GFX RECTANGLE, ax, bx, cx, dx, 0 + jmp .done + + .paint_pressed: + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_BLACK + GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT + + .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] + + GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT + call draw_text + ret + +; +; 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 + + .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 + GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 + ; GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE_LIGHT + mov di, 0 ; Couleur Noire + 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 + add ax, 2 + add bx, 2 + sub cx, 2 + sub dx, 2 + GFX RECTANGLE_ROUND, ax, bx, cx, dx, di + ret + +draw_checkbox: + ; met un "fond" blanc + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE + + cmp byte [gs:si + widget.state], GUI_STATE_HOVER + jne .no_hover + GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 + .no_hover: + + ; Calcul coords box (centré verticalement) + mov ax, [gs:si + widget.x] + add ax, 4 + mov bx, [gs:si + widget.y] + + mov cx, [gs:si + widget.h] + mov dx, [gs:si + widget.w] + + sub cx, GUI_CHECKBOX_SIZE + shr cx, 1 + add bx, cx ; Y1 + + mov cx, ax + add cx, GUI_CHECKBOX_SIZE ; X2 + + mov dx, bx + add dx, GUI_CHECKBOX_SIZE ; Y2 + + ; Sauvegarde coords pour le X + push ax + push bx + push cx + push dx + + ; Fond blanc + Bordure noire + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE + GFX RECTANGLE, ax, bx, cx, dx, 0 + + ; Check if checked + cmp word [gs:si + widget.attr_val], 0 + je .draw_label + + ; Dessin du X (Diagonales) + add ax, 2 + add bx, 2 + sub cx, 2 + sub dx, 2 + + ; Optimisation : Utilisation de LINE au lieu de PUTPIXEL en boucle + ; Diagonale 1 : (ax, bx) -> (cx, dx) + GFX LINE, ax, bx, cx, dx, 0 + + ; Diagonale 2 : (ax, dx) -> (cx, bx) + GFX LINE, ax, dx, cx, bx, 0 + + .draw_label: + pop dx + pop cx + pop bx + pop ax + + ; Dessin du texte à droite (BoxX2 + 6) + add cx, 6 + + ; Y = Centré par rapport au widget + mov bx, [gs:si + widget.y] + mov ax, [gs:si + widget.h] + sub ax, 8 + shr ax, 1 + add bx, ax + inc bx + + GFX GOTOXY, cx, bx + mov dx, [gs:si + widget.text_seg] + mov ax, [gs:si + widget.text_ofs] + GFX WRITE, dx, ax + + 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] + + .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 + + .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 + + mov bx, [gs:si + widget.h] + sub bx, 8 + shr bx, 1 + add bx, [gs:si + widget.y] ; Y final + inc bx + + pop cx ; CX = X Final, BX = Y Final + + GFX GOTOXY, cx, bx + + 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 diff --git a/src/services/gui_lib.asm b/src/gui/lib.asm similarity index 60% rename from src/services/gui_lib.asm rename to src/gui/lib.asm index 4593f95..12f583c 100644 --- a/src/services/gui_lib.asm +++ b/src/gui/lib.asm @@ -20,10 +20,6 @@ ; ; ============================================================================= -; -; https://github.com/buserror/libmui -; - ; --- Configuration --- %define GUI_RAM_SEG 0x0A00 ; Segment de données UI %define GUI_MAX_WIDGETS 32 ; Nombre max de widgets simultanés @@ -578,470 +574,3 @@ gui_logic_checkbox: mov byte [gs:si + widget.state], GUI_STATE_HOVER xor ax, ax ret - - -; Dessine le widget pointé par SI -gui_draw_single_widget: - pusha - - GFX MOUSE_HIDE ; On cache la souris AVANT de commencer le dessin du widget - - mov al, [gs:si + widget.state] - cmp al, [gs:si + widget.oldstate] - je .done - - mov [gs:si + widget.oldstate], al ; marque comme à jour - - ; Chargement coords - mov ax, [gs:si + widget.x] - mov bx, [gs:si + widget.y] - - mov cx, [gs:si + widget.w] - mov dx, [gs:si + widget.h] - - ; Calcul X2, Y2 - add cx, ax - add dx, bx - - mov [gs:si + widget.x2], cx - mov [gs:si + widget.y2], dx - - ; 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: - cmp byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX - jne .case_5 - call draw_checkbox - jmp .done - - .case_5: - - - .done: - GFX MOUSE_SHOW ; On réaffiche la souris APRES, capturant le widget fini - popa - ret - -; --- Locals Mapping --- -%define .thumb_x1 word [bp-2] -%define .thumb_y1 word [bp-4] -%define .thumb_x2 word [bp-6] -%define .thumb_y2 word [bp-8] -%define .track_x1 word [bp-10] -%define .track_y1 word [bp-12] -%define .track_x2 word [bp-14] -%define .track_y2 word [bp-16] -draw_slider: - push bp - mov bp, sp - sub sp, 16 ; Reserve space for locals - - ; Save track coords (Arguments passed in AX, BX, CX, DX) - mov .track_x1, ax - mov .track_y1, bx - mov .track_x2, cx - mov .track_y2, dx - - ; 1. Dessiner la piste (Track) - GFX RECTANGLE_FILL, .track_x1, .track_y1, .track_x2, .track_y2, PATTERN_WHITE_LIGHT - GFX RECTANGLE, .track_x1, .track_y1, .track_x2, .track_y2, 0 - - ; 2. Calculer la taille du curseur (Thumb) - 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 - - ; Thumb X1 = attr_val - mov cx, [gs:si + widget.attr_val] - - ; --- CLAMP X1 (Fix Overflow) --- - ; Max X1 = TrackX2 - ThumbW - mov dx, .track_x2 - sub dx, ax ; DX = Max X1 - cmp cx, dx - jle .chk_min_x - mov cx, dx - .chk_min_x: - cmp cx, .track_x1 - jge .ok_x1 - mov cx, .track_x1 - .ok_x1: - mov .thumb_x1, cx - - ; Thumb X2 = X1 + Width - add cx, ax - mov .thumb_x2, cx - - ; Thumb Y1 = Track Y1 - mov ax, .track_y1 - mov .thumb_y1, ax - - ; Thumb Y2 = Track Y2 - mov ax, .track_y2 - mov .thumb_y2, ax - 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 - - ; Thumb Y1 = attr_val - mov cx, [gs:si + widget.attr_val] - - ; --- CLAMP Y1 (Fix Overflow) --- - ; Max Y1 = TrackY2 - ThumbH - mov dx, .track_y2 - sub dx, ax ; DX = Max Y1 - cmp cx, dx - jle .chk_min_y - mov cx, dx - .chk_min_y: - cmp cx, .track_y1 - jge .ok_y1 - mov cx, .track_y1 - .ok_y1: - mov .thumb_y1, cx - - ; Thumb Y2 = Y1 + Height - add cx, ax - mov .thumb_y2, cx - - ; Thumb X1 = Track X1 - mov ax, .track_x1 - mov .thumb_x1, ax - - ; Thumb X2 = Track X2 - mov ax, .track_x2 - mov .thumb_x2, ax - - .draw_thumb: - ; 3. Dessiner le curseur (Thumb) - GFX RECTANGLE_FILL, .thumb_x1, .thumb_y1, .thumb_x2, .thumb_y2, PATTERN_WHITE - GFX RECTANGLE, .thumb_x1, .thumb_y1, .thumb_x2, .thumb_y2, 0 - - ; --- Grip Lines (3 lignes pour un look moderne) --- - cmp byte [gs:si + widget.attr_mode], 2 - je .grip_v - - ; --- Grip Horizontal (Lignes verticales) --- - ; Center X - mov ax, .thumb_x1 - add ax, .thumb_x2 - shr ax, 1 ; AX = Center X - - ; Y bounds (padding 3px) - mov bx, .thumb_y1 - add bx, 3 - mov dx, .thumb_y2 - sub dx, 3 - - ; Center Line - GFX LINE, ax, bx, ax, dx, 0 - - ; Left Line - mov cx, ax - sub cx, 2 - cmp cx, .thumb_x1 - jle .skip_left - GFX LINE, cx, bx, cx, dx, 0 - .skip_left: - - ; Right Line - mov cx, ax - add cx, 2 - cmp cx, .thumb_x2 - jge .grip_done - GFX LINE, cx, bx, cx, dx, 0 - jmp .grip_done - - .grip_v: - ; --- Grip Vertical (Lignes horizontales) --- - ; Center Y - mov bx, .thumb_y1 - add bx, .thumb_y2 - shr bx, 1 ; BX = Center Y - - ; X bounds (padding 3px) - mov ax, .thumb_x1 - add ax, 3 - mov cx, .thumb_x2 - sub cx, 3 - - ; Center Line - GFX LINE, ax, bx, cx, bx, 0 - - ; Top Line - mov dx, bx - sub dx, 2 - cmp dx, .thumb_y1 - jle .skip_top - GFX LINE, ax, dx, cx, dx, 0 - .skip_top: - - ; Bottom Line - mov dx, bx - add dx, 2 - cmp dx, .thumb_y2 - jge .grip_done - GFX LINE, ax, dx, cx, dx, 0 - - .grip_done: - leave - ret - -; Clean up defines -%undef .thumb_x1 -%undef .thumb_y1 -%undef .thumb_x2 -%undef .thumb_y2 -%undef .track_x1 -%undef .track_y1 -%undef .track_x2 -%undef .track_y2 - -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 - - .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, ax, bx, cx, dx, 0 - jmp .done - - .paint_hover: - GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE_LIGHT - GFX RECTANGLE, ax, bx, cx, dx, 0 - jmp .done - - .paint_pressed: - GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_BLACK - GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT - - .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] - - GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT - call draw_text - ret - -; -; 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 - - .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 - GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 - ; GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE_LIGHT - mov di, 0 ; Couleur Noire - 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 - add ax, 2 - add bx, 2 - sub cx, 2 - sub dx, 2 - GFX RECTANGLE_ROUND, ax, bx, cx, dx, di - ret - -draw_checkbox: - ; met un "fond" blanc - GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE - - cmp byte [gs:si + widget.state], GUI_STATE_HOVER - jne .no_hover - GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 - .no_hover: - - ; Calcul coords box (centré verticalement) - mov ax, [gs:si + widget.x] - add ax, 4 - mov bx, [gs:si + widget.y] - - mov cx, [gs:si + widget.h] - mov dx, [gs:si + widget.w] - - sub cx, GUI_CHECKBOX_SIZE - shr cx, 1 - add bx, cx ; Y1 - - mov cx, ax - add cx, GUI_CHECKBOX_SIZE ; X2 - - mov dx, bx - add dx, GUI_CHECKBOX_SIZE ; Y2 - - ; Sauvegarde coords pour le X - push ax - push bx - push cx - push dx - - ; Fond blanc + Bordure noire - GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE - GFX RECTANGLE, ax, bx, cx, dx, 0 - - ; Check if checked - cmp word [gs:si + widget.attr_val], 0 - je .draw_label - - ; Dessin du X (Diagonales) - add ax, 2 - add bx, 2 - sub cx, 2 - sub dx, 2 - - ; Optimisation : Utilisation de LINE au lieu de PUTPIXEL en boucle - ; Diagonale 1 : (ax, bx) -> (cx, dx) - GFX LINE, ax, bx, cx, dx, 0 - - ; Diagonale 2 : (ax, dx) -> (cx, bx) - GFX LINE, ax, dx, cx, bx, 0 - - .draw_label: - pop dx - pop cx - pop bx - pop ax - - ; Dessin du texte à droite (BoxX2 + 6) - add cx, 6 - - ; Y = Centré par rapport au widget - mov bx, [gs:si + widget.y] - mov ax, [gs:si + widget.h] - sub ax, 8 - shr ax, 1 - add bx, ax - inc bx - - GFX GOTOXY, cx, bx - mov dx, [gs:si + widget.text_seg] - mov ax, [gs:si + widget.text_ofs] - GFX WRITE, dx, ax - - 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] - - .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 - - .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 - - mov bx, [gs:si + widget.h] - sub bx, 8 - shr bx, 1 - add bx, [gs:si + widget.y] ; Y final - inc bx - - pop cx ; CX = X Final, BX = Y Final - - GFX GOTOXY, cx, bx - - 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