luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

splash.c (10313B)


      1 #include "splash.h"
      2 #include "decoder_PNG.h"
      3 #include "decoder_BMP.h"
      4 #include "decoder.h"
      5 #include "vesa.h"
      6 #include "ramdisk.h"
      7 #include "terminal.h"
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <stdio.h>
     11 
     12 /* External VESA state */
     13 extern vesa_state_t vesa_state;
     14 extern ramdisk_node_t* c_ramdisk_root;
     15 
     16 /* RTC port definitions */
     17 #define RTC_INDEX 0x70
     18 #define RTC_DATA  0x71
     19 #define RTC_SECONDS 0x00
     20 
     21 /* Read a byte from I/O port */
     22 static inline uint8_t inb(uint16_t port) {
     23     uint8_t ret;
     24     __asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
     25     return ret;
     26 }
     27 
     28 /* Write a byte to I/O port */
     29 static inline void outb(uint16_t port, uint8_t val) {
     30     __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
     31 }
     32 
     33 /* Read RTC register */
     34 static uint8_t read_rtc(uint8_t reg) {
     35     outb(RTC_INDEX, reg);
     36     return inb(RTC_DATA);
     37 }
     38 
     39 /* Convert BCD to binary */
     40 static uint8_t bcd_to_bin(uint8_t bcd) {
     41     return ((bcd >> 4) * 10) + (bcd & 0x0F);
     42 }
     43 
     44 /* Get current seconds from RTC */
     45 static uint8_t get_rtc_seconds(void) {
     46     uint8_t seconds = read_rtc(RTC_SECONDS);
     47     /* RTC might be in BCD or binary mode, convert if needed */
     48     return bcd_to_bin(seconds);
     49 }
     50 
     51 /* RTC-based delay function */
     52 void splash_delay_ms(uint32_t ms) {
     53     extern void terminal_writestring(const char*);
     54     extern int snprintf(char*, size_t, const char*, ...);
     55     char buf[128];
     56 
     57     /* For delays >= 1 second, use RTC seconds counter */
     58     if (ms >= 1000) {
     59         uint32_t seconds = ms / 1000;
     60         uint8_t start_sec = get_rtc_seconds();
     61         uint8_t target_sec = (start_sec + seconds) % 60;
     62 
     63         snprintf(buf, sizeof(buf), "RTC delay: %u ms (%u sec), start=%u, target=%u\n",
     64                  ms, seconds, start_sec, target_sec);
     65         terminal_writestring(buf);
     66 
     67         /* Wait until target second is reached */
     68         while (1) {
     69             uint8_t current_sec = get_rtc_seconds();
     70 
     71             /* Handle wraparound at 60 seconds */
     72             if (start_sec < target_sec) {
     73                 /* No wraparound */
     74                 if (current_sec >= target_sec) {
     75                     snprintf(buf, sizeof(buf), "RTC delay complete: current=%u\n", current_sec);
     76                     terminal_writestring(buf);
     77                     break;
     78                 }
     79             } else {
     80                 /* Wraparound case */
     81                 if (current_sec >= target_sec && current_sec < start_sec) {
     82                     snprintf(buf, sizeof(buf), "RTC delay complete (wrap): current=%u\n", current_sec);
     83                     terminal_writestring(buf);
     84                     break;
     85                 }
     86             }
     87 
     88             /* Small pause to avoid hammering the RTC */
     89             for (volatile int i = 0; i < 1000; i++) {
     90                 __asm__ volatile ("nop");
     91             }
     92         }
     93     } else {
     94         /* For sub-second delays, use busy loop */
     95         volatile uint32_t iterations = ms * 1000;
     96         for (volatile uint32_t i = 0; i < iterations; i++) {
     97             __asm__ volatile ("nop");
     98         }
     99     }
    100 }
    101 
    102 /* Draw an image centered on the screen with VESA */
    103 static void draw_image_centered(image_t* img) {
    104     if (!img || !vesa_state.active || !vesa_state.framebuffer) {
    105         return;
    106     }
    107 
    108     uint32_t screen_width = vesa_state.current_mode.width;
    109     uint32_t screen_height = vesa_state.current_mode.height;
    110 
    111     /* Calculate centered position */
    112     int x_offset = (screen_width - img->width) / 2;
    113     int y_offset = (screen_height - img->height) / 2;
    114 
    115     /* Ensure we don't draw off-screen */
    116     if (x_offset < 0) x_offset = 0;
    117     if (y_offset < 0) y_offset = 0;
    118 
    119     /* Get framebuffer properties */
    120     uint8_t* fb = vesa_state.framebuffer;
    121     uint16_t pitch = vesa_state.current_mode.pitch;
    122     uint8_t bytes_per_pixel = vesa_state.current_mode.bpp / 8;
    123 
    124     /* Draw the image */
    125     for (uint32_t y = 0; y < img->height; y++) {
    126         uint32_t screen_y = y_offset + y;
    127         if (screen_y >= screen_height) break;
    128 
    129         for (uint32_t x = 0; x < img->width; x++) {
    130             uint32_t screen_x = x_offset + x;
    131             if (screen_x >= screen_width) continue;
    132 
    133             uint8_t r, g, b, a;
    134             image_get_pixel(img, x, y, &r, &g, &b, &a);
    135 
    136             /* Calculate framebuffer position */
    137             uint8_t* pixel = fb + screen_y * pitch + screen_x * bytes_per_pixel;
    138 
    139             /* Write pixel based on framebuffer format */
    140             if (bytes_per_pixel == 3) {
    141                 /* 24-bit RGB */
    142                 pixel[0] = b;
    143                 pixel[1] = g;
    144                 pixel[2] = r;
    145             } else if (bytes_per_pixel == 4) {
    146                 /* 32-bit RGBA or RGBX */
    147                 pixel[0] = b;
    148                 pixel[1] = g;
    149                 pixel[2] = r;
    150                 pixel[3] = 0;  /* Reserved/Alpha */
    151             }
    152         }
    153     }
    154 }
    155 
    156 /* Clear screen to black */
    157 static void clear_screen_black(void) {
    158     if (!vesa_state.active || !vesa_state.framebuffer) {
    159         return;
    160     }
    161 
    162     uint32_t screen_height = vesa_state.current_mode.height;
    163     uint16_t pitch = vesa_state.current_mode.pitch;
    164 
    165     /* Clear to black */
    166     for (uint32_t y = 0; y < screen_height; y++) {
    167         memset(vesa_state.framebuffer + y * pitch, 0, pitch);
    168     }
    169 }
    170 
    171 /* Display a splash screen with a PNG image */
    172 int splash_show_png(const uint8_t* png_data, uint32_t png_size, uint32_t duration_ms) {
    173     (void)duration_ms;  /* Unused - delay is skipped */
    174     if (!png_data || png_size == 0) {
    175         terminal_writestring("ERROR: Invalid PNG data\n");
    176         return -1;
    177     }
    178 
    179     /* Decode the PNG */
    180     terminal_writestring("Decoding PNG splash screen...\n");
    181     image_t* img = png_decode(png_data, png_size);
    182     if (!img) {
    183         terminal_writestring("ERROR: Failed to decode PNG\n");
    184         return -1;
    185     }
    186 
    187     char size_str[32];
    188     terminal_writestring("PNG decoded: ");
    189     snprintf(size_str, sizeof(size_str), "%ux%u", img->width, img->height);
    190     terminal_writestring(size_str);
    191     terminal_writestring(" pixels\n");
    192 
    193     /* Initialize VESA if not already initialized */
    194     if (!vesa_state.initialized) {
    195         terminal_writestring("Initializing VESA...\n");
    196         if (vesa_init() != 0) {
    197             terminal_writestring("ERROR: Failed to initialize VESA\n");
    198             image_destroy(img);
    199             return -1;
    200         }
    201     }
    202 
    203     /* Set a suitable video mode if not already set */
    204     if (!vesa_state.active) {
    205         terminal_writestring("Setting VESA mode 1024x768x32...\n");
    206         if (vesa_set_mode(1024, 768, 32) != 0) {
    207             terminal_writestring("WARNING: Failed to set 1024x768x32, trying 800x600x32...\n");
    208             if (vesa_set_mode(800, 600, 32) != 0) {
    209                 terminal_writestring("ERROR: Failed to set video mode\n");
    210                 image_destroy(img);
    211                 return -1;
    212             }
    213         }
    214     }
    215 
    216     /* Clear screen to black */
    217     clear_screen_black();
    218 
    219     /* Draw the image centered */
    220     terminal_writestring("Displaying splash screen...\n");
    221     draw_image_centered(img);
    222 
    223     /* Skip delay for now - timing issues */
    224     // splash_delay_ms(duration_ms);
    225 
    226     /* Clean up */
    227     image_destroy(img);
    228     terminal_writestring("Splash screen complete\n");
    229 
    230     return 0;
    231 }
    232 
    233 /* Display a splash screen from ramdisk file path */
    234 int splash_show_from_file(const char* filepath, uint32_t duration_ms) {
    235     (void)duration_ms;  /* Unused - we don't block anymore */
    236 
    237     if (!filepath) {
    238         terminal_writestring("ERROR: Invalid file path\n");
    239         return -1;
    240     }
    241 
    242     terminal_writestring("Loading splash screen from: ");
    243     terminal_writestring(filepath);
    244     terminal_writestring("\n");
    245 
    246     /* Traverse ramdisk to find the file */
    247     ramdisk_node_t* node = ramdisk_find(c_ramdisk_root, filepath);
    248     if (!node) {
    249         terminal_writestring("ERROR: File not found in ramdisk\n");
    250         return -1;
    251     }
    252 
    253     if (node->type != RAMDISK_FILE) {
    254         terminal_writestring("ERROR: Path is not a file\n");
    255         return -1;
    256     }
    257 
    258     char size_str[32];
    259     snprintf(size_str, sizeof(size_str), "File size: %u bytes\n", node->file.size);
    260     terminal_writestring(size_str);
    261 
    262     /* Detect file type by extension */
    263     const char* ext = filepath;
    264     const char* dot = NULL;
    265     while (*ext) {
    266         if (*ext == '.') dot = ext;
    267         ext++;
    268     }
    269 
    270     image_t* img = NULL;
    271     if (dot && strcmp(dot, ".bmp") == 0) {
    272         terminal_writestring("Decoding BMP splash screen...\n");
    273         img = bmp_decode(node->file.data, node->file.size);
    274     } else if (dot && strcmp(dot, ".png") == 0) {
    275         terminal_writestring("Decoding PNG splash screen...\n");
    276         img = png_decode(node->file.data, node->file.size);
    277     } else {
    278         terminal_writestring("ERROR: Unknown image format (use .bmp or .png)\n");
    279         return -1;
    280     }
    281 
    282     if (!img) {
    283         terminal_writestring("ERROR: Failed to decode image\n");
    284         return -1;
    285     }
    286 
    287     snprintf(size_str, sizeof(size_str), "Image decoded: %ux%u pixels\n", img->width, img->height);
    288     terminal_writestring(size_str);
    289 
    290     /* Initialize VESA if not already initialized */
    291     if (!vesa_state.initialized) {
    292         terminal_writestring("Initializing VESA...\n");
    293         if (vesa_init() != 0) {
    294             terminal_writestring("ERROR: Failed to initialize VESA\n");
    295             image_destroy(img);
    296             return -1;
    297         }
    298     }
    299 
    300     /* Set a suitable video mode if not already set */
    301     if (!vesa_state.active) {
    302         terminal_writestring("Setting VESA mode 1024x768x32...\n");
    303         if (vesa_set_mode(1024, 768, 32) != 0) {
    304             terminal_writestring("WARNING: Failed to set 1024x768x32, trying 800x600x32...\n");
    305             if (vesa_set_mode(800, 600, 32) != 0) {
    306                 terminal_writestring("ERROR: Failed to set video mode\n");
    307                 image_destroy(img);
    308                 return -1;
    309             }
    310         }
    311     }
    312 
    313     /* Clear screen to black */
    314     clear_screen_black();
    315 
    316     /* Draw the image centered */
    317     terminal_writestring("Displaying splash screen...\n");
    318     draw_image_centered(img);
    319 
    320     /* Don't wait - let it stay on screen until drawn over by the desktop */
    321     /* Clean up memory but image stays in VRAM */
    322     image_destroy(img);
    323     terminal_writestring("Splash screen displayed (will remain until overwritten)\n");
    324 
    325     return 0;
    326 }