quinn-os/gui.c
2025-04-14 18:43:09 +10:00

1174 lines
36 KiB
C

#include "inttypes.h"
#include "multiboot.h"
#include "pvec.h"
#include "font.h"
#include "pointer.xpm"
#include "minimize.xpm"
#include "close.xpm"
#include "defaulticon.xpm"
#include "poweroff.xpm"
#include "keyboard.h"
#include "gui.h"
#include "schedule.h"
#include "mouse.h"
#include "string.h"
#include "memory.h"
#include "console.h"
extern struct task_t *current_task;
struct gui_colour_scheme_t sys_color;
// extern const struct bitmap_font font;
#define WIN_REQ_FLAG_NO_TITLE 0x01
#define WIN_REQ_FLAG_NO_MOUSE 0x02
#define BochsBreak() \
outportw(0x8A00, 0x8A00); \
outportw(0x8A00, 0x08AE0);
#define BochsConsolePrintChar(c) outportb(0xe9, c)
void render(uint8_t *dest, uint8_t *src, uint32_t src_width, uint32_t src_height, int dest_x, int dest_y, uint32_t dest_width, uint32_t dest_height,
uint32_t src_depth);
void gui_pagemap();
struct ModeInfoBlock {
uint16_t attributes;
uint8_t winA, winB;
uint16_t granularity;
uint16_t winsize;
uint16_t segmentA, segmentB;
uint32_t winFuncPointer;
uint16_t pitch; // bytes per scanline
uint16_t Xres, Yres;
uint8_t Wchar, Ychar, planes, bpp, banks;
uint8_t memory_model, bank_size, image_pages;
uint8_t reserved0;
uint8_t red_mask, red_position;
uint8_t green_mask, green_position;
uint8_t blue_mask, blue_position;
uint8_t rsv_mask, rsv_position;
uint8_t directcolor_attributes;
uint32_t physbase; // your LFB (Linear Framebuffer) address ;)
uint32_t reserved1;
uint16_t reserved2;
} __attribute__((packed));
uint8_t *sysfont = (uint8_t *)fontdata;
uint8_t *defaulticon = NULL;
uint8_t *mouseptr = NULL;
uint8_t *backbuffer = NULL;
// uint8_t *last_backbuffer = NULL;
uint8_t vidmode = 0;
uint8_t *framebuffer = NULL;
uint16_t width = 0, height = 0, depth = 0, bytesPerLine = 0;
uint8_t *minimizebtn = NULL;
uint8_t *closebtn = NULL;
uint8_t *wallpaper = NULL;
uint8_t all_dirty = 1;
struct ptr_vector vwindows;
int last_mouse_pos_x = 0;
int last_mouse_pos_y = 0;
int last_mouse_vis = 1;
extern int mouse_pos_x;
extern int mouse_pos_y;
static struct window_t *mouse_last_over = NULL;
static int mouse_last_dragging = 0;
extern char middle_btn;
extern char left_btn;
extern char right_btn;
uint32_t serialnos = 0;
int wallpaper_lock = 0;
struct ModeInfoBlock *modeinfo = NULL;
static void gui_window_set_focus(struct window_t *win) {
if (win->focused)
return;
for (int i = 0; i < ptr_vector_len(&vwindows); i++) {
struct window_t *fwin = (struct window_t *)ptr_vector_get(&vwindows, i);
if (fwin->focused) {
fwin->dirty = 1;
fwin->focused = 0;
fwin->mytask->priority -= 10;
if (fwin->mytask->priority < 1) {
fwin->mytask->priority = 1;
}
}
}
win->mytask->priority += 10;
win->focused = 1;
win->dirty = 1;
}
struct rect_t {
int x, y, width, height;
};
#define MAX_DIRTY_RECTS 100
struct rect_t dirty_rects[MAX_DIRTY_RECTS];
int dirty_rect_count = 0;
void mark_dirty(int x, int y, int w, int h) {
static float percentage_dirty = 0;
if (all_dirty == 1) {
return; // No need to mark dirty if all is dirty
}
for (int i = 0; i < dirty_rect_count; i++) {
struct rect_t *rect = &dirty_rects[i];
if (x >= rect->x && x + w <= rect->x + rect->width && y >= rect->y && y + h <= rect->y + rect->height) {
return; // Already marked as dirty
}
}
if (dirty_rect_count == 0) {
percentage_dirty = 0;
}
percentage_dirty += (w * h) / (width * height);
if (percentage_dirty > 0.75) {
// if more than 75% of the screen is dirty, just mark it all dirty
dirty_rect_count = 0;
all_dirty = 1;
return;
}
if (dirty_rect_count < MAX_DIRTY_RECTS) {
dirty_rects[dirty_rect_count++] = (struct rect_t){x, y, w, h};
}
}
void init_gui(multiboot_info_t *mbinfo) {
if (mbinfo->flags & 1 << 11) {
serialnos = 0;
vidmode = 1;
modeinfo = (struct ModeInfoBlock *)mbinfo->vbe_mode_info;
framebuffer = (uint8_t *)modeinfo->physbase;
bytesPerLine = modeinfo->pitch;
width = modeinfo->Xres;
height = modeinfo->Yres;
depth = modeinfo->bpp / 8;
gui_pagemap();
// last_backbuffer = (uint8_t *)malloc(bytesPerLine * height);
backbuffer = (uint8_t *)dbmalloc(bytesPerLine * height, "init_gui 1");
memset(backbuffer, 0, bytesPerLine * height);
// memset(last_backbuffer, 0, bytesPerLine * height);
// windows = NULL;
gui_convert_xpm(close_xpm, &closebtn);
gui_convert_xpm(pointer_xpm, &mouseptr);
gui_convert_xpm(minimize_xpm, &minimizebtn);
gui_convert_xpm(defaulticon_xpm, &defaulticon);
wallpaper = NULL;
wallpaper_lock = 0;
sys_color.titlebar_focus = 0xf182f8;
sys_color.titlebar_focus_text = 0xffffff;
sys_color.titlebar_unfocus = 0xb4b4b4;
sys_color.titlebar_unfocus_text = 0x888888;
sys_color.icon_title = 0xf9c5ff;
sys_color.icon_title_text = 0x000000;
init_ptr_vector(&vwindows);
} else {
vidmode = 0;
}
}
void fill_rect(uint8_t *buffer, uint32_t dest_width, uint32_t dest_height, int sx, int sy, int w, int h, uint32_t colour) {
int j;
int k;
uint32_t where;
if (sy < 0) {
h = h + sy;
sy = 0;
}
if (sx < 0) {
w = w + sx;
sx = 0;
}
if (sx + w >= dest_width) {
w = dest_width - sx;
}
if (sy + h >= dest_height) {
h = dest_height - sy;
}
where = (sy * (dest_width * depth)) + (sx * depth);
for (j = 0; j < h; j++) {
for (k = 0; k < w; k++) {
buffer[where + k * depth] = colour & 255;
buffer[where + k * depth + 1] = (colour >> 8) & 255;
buffer[where + k * depth + 2] = (colour >> 16) & 255;
buffer[where + k * depth + 3] = 0xff;
}
where += (dest_width)*depth;
}
}
void gui_putpixel(uint8_t *buffer, uint32_t dest_width, uint32_t dest_height, int x, int y, uint32_t color) {
if (x >= dest_width || y >= dest_height) {
return;
}
if (x < 0 || y < 0) {
return;
}
int where = (x * depth) + (y * dest_width * depth);
// alpha
buffer[where] = color & 255; // BLUE
buffer[where + 1] = (color >> 8) & 255; // GREEN
buffer[where + 2] = (color >> 16) & 255; // RED
buffer[where + 3] = 0xFF;
}
int char_to_hex(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
} else {
return (c - 'A') + 10;
}
}
void gui_convert_xpm(char **xpm, uint8_t **buffer) {
uint32_t bwidth;
uint32_t bheight;
uint32_t colours;
uint32_t charsperpixel;
int offset = 0;
bwidth = 0;
while (xpm[0][offset] != ' ') {
bwidth = bwidth * 10 + (xpm[0][offset] - '0');
offset++;
}
offset++;
bheight = 0;
while (xpm[0][offset] != ' ') {
bheight = bheight * 10 + (xpm[0][offset] - '0');
offset++;
}
offset++;
colours = 0;
while (xpm[0][offset] != ' ') {
colours = colours * 10 + (xpm[0][offset] - '0');
offset++;
}
offset++;
charsperpixel = 0;
while (xpm[0][offset] != ' ' && xpm[0][offset] != '\0') {
charsperpixel = charsperpixel * 10 + (xpm[0][offset] - '0');
offset++;
}
// do colours
uint32_t *theColours = (uint32_t *)dbmalloc(sizeof(uint32_t) * colours, "gui convert xpm 1");
char **theKeys = (char **)dbmalloc(colours * sizeof(char *), "gui convert xpm 2");
int i, j, k, l;
for (i = 0; i < colours; i++) {
theKeys[i] = (char *)dbmalloc(charsperpixel, "gui convert xpm 3");
for (j = 0; j < charsperpixel; j++) {
theKeys[i][j] = xpm[i + 1][j];
}
offset = 3 + charsperpixel;
if (xpm[i + 1][offset] == '#') {
theColours[i] = 0xFF000000;
offset++;
theColours[i] = theColours[i] | (char_to_hex(xpm[i + 1][offset]) << 20);
offset++;
theColours[i] = theColours[i] | (char_to_hex(xpm[i + 1][offset]) << 16);
offset++;
theColours[i] = theColours[i] | (char_to_hex(xpm[i + 1][offset]) << 12);
offset++;
theColours[i] = theColours[i] | (char_to_hex(xpm[i + 1][offset]) << 8);
offset++;
theColours[i] = theColours[i] | (char_to_hex(xpm[i + 1][offset]) << 4);
offset++;
theColours[i] = theColours[i] | char_to_hex(xpm[i + 1][offset]);
} else {
theColours[i] = 0;
}
}
uint8_t *ptr = (uint8_t *)dbmalloc(bwidth * bheight * 4, "gui convert xpm 4");
*buffer = ptr;
memset(ptr, 0, bwidth * bheight * 4);
int boffset = 0;
int match;
for (i = 0; i < bheight; i++) {
for (j = 0; j < bwidth; j++) {
for (k = 0; k < colours; k++) {
match = 1;
for (l = 0; l < charsperpixel; l++) {
if (theKeys[k][l] != xpm[i + colours + 1][j * charsperpixel + l]) {
match = 0;
}
}
if (match == 1) {
ptr[boffset + 3] = (theColours[k] >> 24) & 255; // ALPHA
ptr[boffset + 0] = theColours[k] & 255; // BLUE
ptr[boffset + 1] = (theColours[k] >> 8) & 255; // GREEN
ptr[boffset + 2] = (theColours[k] >> 16) & 255; // RED
boffset += 4;
break;
}
}
}
}
dbfree(theColours, "gui convert xpm 5");
for (i = 0; i < colours; i++) {
dbfree(theKeys[i], "gui convert xpm 6");
}
dbfree(theKeys, "gui convert xpm 7");
}
void gui_draw_char(uint8_t *buffer, uint32_t dest_width, uint32_t dest_height, uint32_t x, uint32_t y, char c, uint32_t colour) {
int cx, cy;
// int mask[8]={1,2,4,8,16,32,64,128};
for (cy = 0; cy < 16; cy++) {
for (cx = 0; cx < 8; cx++) {
if (sysfont[(uint8_t)c * 16 + cy] >> cx & 0x1) {
gui_putpixel(buffer, dest_width, dest_height, x + 8 - (cx + 1), y + cy, colour);
}
}
}
}
void draw_text(uint8_t *buffer, uint32_t dest_width, uint32_t dest_height, int sx, int sy, uint32_t colour, char *text) {
int i;
for (i = 0; i < strlen(text); i++) {
gui_draw_char(buffer, dest_width, dest_height, sx + (i * 8), sy, text[i], colour);
}
}
static int got_escape = 0;
void gui_keyboard_in(uint8_t scancode, uint8_t state) {
// add a keyboard message
if (scancode == 0xe0) {
got_escape = 1;
return;
}
struct window_t *topwin = (struct window_t *)ptr_vector_get(&vwindows, ptr_vector_len(&vwindows) - 1);
if (topwin->event_msg_count < 99) {
struct event_msg_t *new_msg = (struct event_msg_t *)dbmalloc(sizeof(struct event_msg_t), "gui_keyboard_in 1");
if (!new_msg) {
got_escape = 0;
return;
}
new_msg->type = 1;
if (got_escape) {
new_msg->code = (0xe0 << 8) | scancode;
} else {
new_msg->code = scancode;
}
new_msg->state = state;
topwin->event_msgs[topwin->event_msg_count++] = new_msg;
if (topwin->mytask->state == TASK_SLEEPING && topwin->mytask->sleep_reason == SLEEP_GUIUPDATE) {
topwin->mytask->state = TASK_RUNNING;
}
}
got_escape = 0;
}
int gui_change_window_caption(int serialno, char *cap) {
int i;
for (i = 0; i < ptr_vector_len(&vwindows); i++) {
if (((struct window_t *)ptr_vector_get(&vwindows, i))->serialno == serialno) {
strncpy(((struct window_t *)ptr_vector_get(&vwindows, i))->name, cap, 128);
((struct window_t *)ptr_vector_get(&vwindows, i))->dirty = 1;
return 0;
}
}
return -1;
}
int gui_add_window(uint8_t *contents, char *name, int x, int y, int w, int h, uint8_t *icon, uint32_t flags, void (*keyboard_d)(uint8_t c, uint8_t state)) {
struct window_t *new_window;
new_window = (struct window_t *)dbmalloc(sizeof(struct window_t), "gui: add window (malloc 1)");
new_window->serialno = ++serialnos;
new_window->posx = x;
new_window->posy = y;
new_window->width = w;
new_window->height = h;
new_window->minimized = 0;
new_window->iconx = 0;
new_window->icony = 0;
new_window->close_req = 0;
new_window->lclick = 0;
new_window->rclick = 0;
new_window->mclick = 0;
new_window->flags = flags;
new_window->event_msg_count = 0;
new_window->focused = 0;
new_window->mytask = current_task;
new_window->dirty = 1;
if (contents == NULL) {
new_window->contents = (uint8_t *)dbmalloc(w * h * depth, "gui add window malloc 2");
fill_rect(new_window->contents, w, h, 0, 0, w, h, 0xfffafafa);
} else {
new_window->contents = contents;
}
if (keyboard_d == NULL) {
new_window->keyboard_dest = gui_keyboard_in;
} else {
new_window->keyboard_dest = keyboard_d;
}
if (icon == NULL) {
new_window->icon = (uint8_t *)dbmalloc(64 * 64 * 4, "gui add window malloc 3");
memcpy(new_window->icon, defaulticon, 64 * 64 * 4);
} else {
new_window->icon = (uint8_t *)dbmalloc(64 * 64 * 4, "gui add window malloc 4");
memcpy(new_window->icon, icon, 64 * 64 * 4);
}
strncpy(new_window->name, name, 128);
new_window->zorder = ptr_vector_len(&vwindows);
ptr_vector_append(&vwindows, new_window);
keyboard_set_handler(new_window->keyboard_dest);
if (current_task != NULL) {
ptr_vector_append(&current_task->window_list, new_window);
if (current_task->state == TASK_SLEEPING && current_task->sleep_reason == SLEEP_GUIUPDATE) {
current_task->state = TASK_RUNNING;
}
}
gui_window_set_focus(new_window);
return new_window->serialno;
}
void gui_mark_window_dirty(int serialno) {
for (int j = 0; j < ptr_vector_len(&vwindows); j++) {
if (((struct window_t *)ptr_vector_get(&vwindows, j))->serialno == serialno) {
((struct window_t *)ptr_vector_get(&vwindows, j))->dirty = 1;
return;
}
}
}
void gui_destroy_window_syscall(int serialno) {
for (int j = 0; j < ptr_vector_len(&current_task->window_list); j++) {
if (((struct window_t *)ptr_vector_get(&current_task->window_list, j))->serialno == serialno) {
ptr_vector_del(&current_task->window_list, j);
gui_destroy_window(serialno);
return;
}
}
kprintf("Window not found %d\n", serialno);
}
void gui_destroy_window(int serialno) {
for (int i = 0; i < ptr_vector_len(&vwindows); i++) {
if (((struct window_t *)ptr_vector_get(&vwindows, i))->serialno == serialno) {
struct window_t *win = (struct window_t *)ptr_vector_del(&vwindows, i);
if (win == mouse_last_over) {
mouse_last_over = NULL;
}
for (int j = i; j < ptr_vector_len(&vwindows); j++) {
((struct window_t *)ptr_vector_get(&vwindows, j))->zorder--;
}
if (win->focused && ptr_vector_len(&vwindows) > 0) {
gui_window_set_focus((struct window_t *)ptr_vector_get(&vwindows, ptr_vector_len(&vwindows) - 1));
}
if (ptr_vector_len(&vwindows) > 0) {
keyboard_set_handler(((struct window_t *)ptr_vector_get(&vwindows, ptr_vector_len(&vwindows) - 1))->keyboard_dest);
} else {
keyboard_set_handler(NULL);
}
all_dirty = 1;
dbfree(win->icon, "gui destroy window 1");
dbfree(win->contents, "gui destroy window 2");
dbfree(win, "gui destroy window 3");
return;
}
}
kprintf("Window not found %d\n", serialno);
}
void gui_drawtitlebar(uint8_t *buffer, uint32_t dest_width, uint32_t dest_height, struct window_t *window, int focused) {
if (focused) {
fill_rect(buffer, dest_width, dest_height, window->posx, window->posy - 25, window->width, 25, sys_color.titlebar_focus);
draw_text(buffer, dest_width, dest_height, window->posx + 25 + 4, window->posy - 25 + 4, sys_color.titlebar_focus_text, window->name);
} else {
fill_rect(buffer, dest_width, dest_height, window->posx, window->posy - 25, window->width, 25, sys_color.titlebar_unfocus);
draw_text(buffer, dest_width, dest_height, window->posx + 25 + 4, window->posy - 25 + 4, sys_color.titlebar_unfocus_text, window->name);
}
render(buffer, minimizebtn, 16, 16, window->posx + window->width - 20, window->posy - 20, dest_width, dest_height, 4);
render(buffer, closebtn, 16, 16, window->posx + 5, window->posy - 20, dest_width, dest_height, 4);
}
void gui_pagemap() { framebuffer = mem_map_framebuffer((uint32_t)framebuffer, bytesPerLine * height); }
void gui_find_icon_space(struct window_t *window) {
int i, j;
int x, y;
int foundspot;
i = 0;
while (1) {
y = height - (((i / (width / 96)) + 1) * 96);
x = (i % (width / 96)) * 96 + 16;
foundspot = 1;
for (j = 0; j < ptr_vector_len(&vwindows); j++) {
struct window_t *win = (struct window_t *)ptr_vector_get(&vwindows, j);
if (!(win->iconx == 0 && win->icony == 0)) {
if (!(x > win->iconx && x < win->iconx + 96 && y > win->icony && y < win->icony + 96) && !(win->icony == y && win->iconx == x)) {
foundspot = 1;
} else {
foundspot = 0;
break;
}
}
}
if (foundspot || i == 80) {
window->iconx = x;
window->icony = y;
return;
}
i++;
}
}
int gui_blit(int wh, int x, int y, int w, int h, uint8_t *bmp) {
struct window_t *win = NULL;
int i;
for (i = 0; i < ptr_vector_len(&vwindows); i++) {
if (((struct window_t *)ptr_vector_get(&vwindows, i))->serialno == wh) {
win = (struct window_t *)ptr_vector_get(&vwindows, i);
break;
}
}
if (win == NULL) {
return 0;
}
render(win->contents, bmp, w, h, x, y, win->width, win->height, 4);
win->dirty = 1;
return 1;
}
int gui_req_input(int wh, struct window_update_req *req) {
struct window_t *win = NULL;
int i;
for (i = 0; i < ptr_vector_len(&vwindows); i++) {
if (((struct window_t *)ptr_vector_get(&vwindows, i))->serialno == wh) {
win = (struct window_t *)ptr_vector_get(&vwindows, i);
break;
}
}
if (win == NULL || (win->event_msg_count == 0 && !win->close_req)) {
if (req->blocking) {
uint8_t sleep = 1;
for (int j = 0; j < ptr_vector_len(&win->mytask->window_list); j++) {
struct window_t *twin = (struct window_t *)ptr_vector_get(&win->mytask->window_list, j);
if (twin->event_msg_count > 0 || twin->close_req) {
sleep = 0;
break;
}
}
if (sleep) {
win->mytask->state = TASK_SLEEPING;
win->mytask->sleep_reason = SLEEP_GUIUPDATE;
return 0;
}
} else {
return 0;
}
}
req->close_req = win->close_req;
req->event_msgs_count = win->event_msg_count;
for (i = 0; i < req->event_msgs_count; i++) {
memcpy(&req->event_msgs[i], win->event_msgs[i], sizeof(struct event_msg_t));
dbfree(win->event_msgs[i], "gui_req_input 1");
}
win->event_msg_count = 0;
req->mousex = mouse_pos_x - win->posx;
req->mousey = mouse_pos_y - win->posy;
req->x = win->posx;
req->y = win->posy;
win->close_req = 0;
return 1;
}
void gui_sink_to_bottom(int wh) {
struct window_t *win;
win = (struct window_t *)ptr_vector_del(&vwindows, wh);
win->zorder = 0;
ptr_vector_ins(&vwindows, win, 0);
for (int i = 1; i < wh; i++) {
((struct window_t *)ptr_vector_get(&vwindows, i))->zorder++;
}
gui_window_set_focus((struct window_t *)ptr_vector_get(&vwindows, ptr_vector_len(&vwindows) - 1));
}
int gui_raise_to_icon_top(int wh) {
struct window_t *win;
int top_icon = 0;
win = (struct window_t *)ptr_vector_del(&vwindows, wh);
for (int i = 0; i < ptr_vector_len(&vwindows); i++) {
if (((struct window_t *)ptr_vector_get(&vwindows, i))->minimized) {
top_icon = i;
} else {
break;
}
}
win->zorder = top_icon;
ptr_vector_ins(&vwindows, win, top_icon);
for (int i = wh; i < top_icon; i++) {
((struct window_t *)ptr_vector_get(&vwindows, i))->zorder--;
}
return top_icon;
}
void gui_raise_to_top(int wh) {
struct window_t *win;
win = (struct window_t *)ptr_vector_del(&vwindows, wh);
for (int i = wh; i < ptr_vector_len(&vwindows); i++) {
((struct window_t *)ptr_vector_get(&vwindows, i))->zorder--;
}
win->zorder = ptr_vector_len(&vwindows);
ptr_vector_append(&vwindows, win);
gui_window_set_focus(win);
keyboard_set_handler(win->keyboard_dest);
}
void gui_proc_input() {
int i;
struct window_t *mouse_over = NULL;
int mouse_over_idx = -1;
if (mouse_is_dragging(0) == 1) {
if (mouse_last_dragging) {
if (mouse_last_over) {
// if already dragging... update position
int xoffset = mouse_pos_x - mouse_last_over->drag_start_x;
int yoffset = mouse_pos_y - mouse_last_over->drag_start_y;
if (mouse_last_over->minimized) {
mouse_last_over->iconx += xoffset;
mouse_last_over->icony += yoffset;
} else {
mouse_last_over->posx += xoffset;
mouse_last_over->posy += yoffset;
}
mouse_last_over->drag_start_x = mouse_pos_x;
mouse_last_over->drag_start_y = mouse_pos_y;
}
all_dirty = 1;
return;
}
} else {
if (mouse_last_dragging) {
mouse_get_click(0); // consume the click
mouse_last_dragging = 0;
if (mouse_last_over) {
if (mouse_last_over->drag_start_x != -1) {
mouse_last_over->drag_start_x = -1;
mouse_last_over->drag_start_y = -1;
}
}
return;
}
}
if (ptr_vector_len(&vwindows) > 0) {
for (i = ptr_vector_len(&vwindows) - 1; i >= 0; i--) {
struct window_t *win = (struct window_t *)ptr_vector_get(&vwindows, i);
if (!(win->minimized)) {
if (mouse_pos_x > win->posx && mouse_pos_x < win->posx + win->width &&
mouse_pos_y > win->posy - (win->flags & WIN_REQ_FLAG_NO_TITLE ? 0 : 25) && mouse_pos_y < win->posy + win->height) {
if (win != mouse_last_over) {
if (mouse_last_over) {
// send mouse exit event
if (mouse_last_over->event_msg_count < 99) {
struct event_msg_t *new_event = (struct event_msg_t *)dbmalloc(sizeof(struct event_msg_t), "gui flip 1");
new_event->type = 3;
new_event->code = 2;
new_event->state = 0;
new_event->x = mouse_pos_x - mouse_last_over->posx;
new_event->y = mouse_pos_y - mouse_last_over->posy;
mouse_last_over->event_msgs[mouse_last_over->event_msg_count++] = new_event;
if (mouse_last_over->mytask->state == TASK_SLEEPING && mouse_last_over->mytask->sleep_reason == SLEEP_GUIUPDATE) {
mouse_last_over->mytask->state = TASK_RUNNING;
}
}
}
// send mouse entry event
if (win->event_msg_count < 99) {
struct event_msg_t *new_event = (struct event_msg_t *)dbmalloc(sizeof(struct event_msg_t), "gui flip 1");
new_event->type = 3;
new_event->code = 1;
new_event->state = 0;
new_event->x = mouse_pos_x - win->posx;
new_event->y = mouse_pos_y - win->posy;
win->event_msgs[win->event_msg_count++] = new_event;
if (win->mytask->state == TASK_SLEEPING && win->mytask->sleep_reason == SLEEP_GUIUPDATE) {
win->mytask->state = TASK_RUNNING;
}
}
}
mouse_over = win;
mouse_over_idx = i;
break;
}
}
}
}
// do drag
if (mouse_is_dragging(0) == 1) {
if (mouse_over != NULL) {
if (mouse_pos_y < mouse_over->posy) {
gui_raise_to_top(mouse_over_idx);
mouse_over->drag_start_x = mouse_pos_x;
mouse_over->drag_start_y = mouse_pos_y;
mouse_last_over = mouse_over;
mouse_last_dragging = 1;
return;
}
} else {
for (i = ptr_vector_len(&vwindows) - 1; i >= 0; i--) {
struct window_t *win = (struct window_t *)ptr_vector_get(&vwindows, i);
if (win->minimized) {
if (mouse_pos_x > win->iconx - 16 && mouse_pos_x < win->iconx + 72 && mouse_pos_y > win->icony - 16 && mouse_pos_y < win->icony + 72) {
mouse_last_over = win;
mouse_last_over->drag_start_x = mouse_pos_x;
mouse_last_over->drag_start_y = mouse_pos_y;
mouse_last_dragging = 1;
return;
}
}
}
}
} else {
// do click
if (mouse_over != NULL) {
if (mouse_pos_y < mouse_over->posy && !(mouse_over->flags & WIN_REQ_FLAG_NO_TITLE)) {
// it's to the title bar;
if (mouse_get_click(0) == 1) {
if (mouse_pos_x < mouse_over->posx + 25) {
mouse_over->close_req = 1;
if (mouse_over->mytask->state == TASK_SLEEPING && mouse_over->mytask->sleep_reason == SLEEP_GUIUPDATE) {
mouse_over->mytask->state = TASK_RUNNING;
}
// done processing
return;
} else if (mouse_pos_x > mouse_over->posx + mouse_over->width - 25) {
mouse_over->minimized = 1;
all_dirty = 1;
if (mouse_over->iconx == 0 && mouse_over->icony == 0) {
gui_find_icon_space(mouse_over);
}
gui_sink_to_bottom(mouse_over_idx);
// done processing
return;
} else {
if (mouse_over->focused == 0) {
gui_raise_to_top(mouse_over_idx);
}
// done processing
return;
}
}
} else {
// it's to the window content
if (mouse_over->lclick != left_btn) {
gui_raise_to_top(mouse_over_idx);
if (mouse_over->event_msg_count < 99) {
struct event_msg_t *new_event = (struct event_msg_t *)dbmalloc(sizeof(struct event_msg_t), "gui flip 1");
new_event->type = 2;
new_event->code = 1;
new_event->state = left_btn;
new_event->x = mouse_pos_x - mouse_over->posx;
new_event->y = mouse_pos_y - mouse_over->posy;
mouse_over->event_msgs[mouse_over->event_msg_count++] = new_event;
if (mouse_over->mytask->state == TASK_SLEEPING && mouse_over->mytask->sleep_reason == SLEEP_GUIUPDATE) {
mouse_over->mytask->state = TASK_RUNNING;
}
}
mouse_over->lclick = left_btn;
mouse_get_click(0);
}
if (mouse_over->mclick != middle_btn) {
gui_raise_to_top(mouse_over_idx);
if (mouse_over->event_msg_count < 99) {
struct event_msg_t *new_event = (struct event_msg_t *)dbmalloc(sizeof(struct event_msg_t), "gui flip 2");
new_event->type = 2;
new_event->code = 2;
new_event->state = middle_btn;
new_event->x = mouse_pos_x - mouse_over->posx;
new_event->y = mouse_pos_y - mouse_over->posy;
mouse_over->event_msgs[mouse_over->event_msg_count++] = new_event;
if (mouse_over->mytask->state == TASK_SLEEPING && mouse_over->mytask->sleep_reason == SLEEP_GUIUPDATE) {
mouse_over->mytask->state = TASK_RUNNING;
}
}
mouse_over->mclick = middle_btn;
mouse_get_click(2);
}
if (mouse_over->rclick != right_btn) {
gui_raise_to_top(mouse_over_idx);
if (mouse_over->event_msg_count < 99) {
struct event_msg_t *new_event = (struct event_msg_t *)dbmalloc(sizeof(struct event_msg_t), "gui flip 3");
new_event->type = 2;
new_event->code = 3;
new_event->state = right_btn;
new_event->x = mouse_pos_x - mouse_over->posx;
new_event->y = mouse_pos_y - mouse_over->posy;
mouse_over->event_msgs[mouse_over->event_msg_count++] = new_event;
if (mouse_over->mytask->state == TASK_SLEEPING && mouse_over->mytask->sleep_reason == SLEEP_GUIUPDATE) {
mouse_over->mytask->state = TASK_RUNNING;
}
}
mouse_over->rclick = right_btn;
mouse_get_click(1);
}
return;
}
} else {
// clicking an icon or the desktop
if (mouse_get_click(0) == 1) {
for (i = ptr_vector_len(&vwindows) - 1; i >= 0; i--) {
struct window_t *win = (struct window_t *)ptr_vector_get(&vwindows, i);
if (win->minimized) {
if (mouse_pos_x > win->iconx - 16 && mouse_pos_x < win->iconx + 72 && mouse_pos_y > win->icony - 16 && mouse_pos_y < win->icony + 72) {
win->minimized = 0;
all_dirty = 1;
gui_raise_to_top(i);
return;
}
}
}
}
}
}
}
void gui_draw() {
int i;
// draw desktop
if (wallpaper == NULL || wallpaper_lock == 1) {
fill_rect(backbuffer, width, height, 0, 0, width, height, 0xfffeccff);
} else {
render(backbuffer, wallpaper, 1024, 768, 0, 0, width, height, 4);
}
// draw icons
for (i = 0; i < ptr_vector_len(&vwindows); i++) {
struct window_t *win = (struct window_t *)ptr_vector_get(&vwindows, i);
if (win->minimized == 1) {
render(backbuffer, win->icon, 64, 64, win->iconx, win->icony, width, height, 4);
fill_rect(backbuffer, width, height, (48 - (strlen(win->name) * 8 / 2)) + (win->iconx - 16), win->icony + 74, strlen(win->name) * 8, 16,
sys_color.icon_title);
draw_text(backbuffer, width, height, (48 - (strlen(win->name) * 8 / 2)) + (win->iconx - 16), win->icony + 74, sys_color.icon_title_text, win->name);
if (win->dirty) {
mark_dirty((48 - (strlen(win->name) * 8 / 2)) + (win->iconx - 16) < win->iconx ? (48 - (strlen(win->name) * 8 / 2)) + (win->iconx - 16) : win->iconx, win->icony, strlen(win->name) * 8, 90);
win->dirty = 0;
}
}
}
// draw windows
for (i = 0; i < ptr_vector_len(&vwindows); i++) {
struct window_t *win = (struct window_t *)ptr_vector_get(&vwindows, i);
if (win->minimized == 0) {
if (!(win->flags & WIN_REQ_FLAG_NO_TITLE)) {
fill_rect(backbuffer, width, height, win->posx - 1, win->posy - 26, win->width + 2, win->height + 27, 0);
gui_drawtitlebar(backbuffer, width, height, win, win->focused);
if (win->dirty) {
mark_dirty(win->posx - 1, win->posy - 26, win->width + 2, win->height + 27);
win->dirty = 0;
}
} else {
fill_rect(backbuffer, width, height, win->posx - 1, win->posy - 1, win->width + 2, win->height + 2, 0);
if (win->dirty) {
mark_dirty(win->posx - 1, win->posy - 1, win->width + 2, win->height + 2);
win->dirty = 0;
}
}
render(backbuffer, win->contents, win->width, win->height, win->posx, win->posy, width, height, depth);
}
}
}
void gui_draw_mouse() {
int i;
// draw mouse
int mouse_visible = 1;
for (i = ptr_vector_len(&vwindows) - 1; i >= 0; i--) {
struct window_t *win = (struct window_t *)ptr_vector_get(&vwindows, i);
if (!win->minimized) {
if (mouse_pos_x >= win->posx && mouse_pos_x <= win->posx + win->width && mouse_pos_y >= win->posy && mouse_pos_y <= win->posy + win->height) {
if (win->flags & WIN_REQ_FLAG_NO_MOUSE) {
mouse_visible = 0;
}
break;
}
}
}
int current_x = mouse_pos_x;
int current_y = mouse_pos_y;
if (last_mouse_vis) {
mark_dirty(last_mouse_pos_x, last_mouse_pos_y, 32, 32);
}
if (mouse_visible) {
mark_dirty(current_x, current_y, 32, 32);
render(backbuffer, mouseptr, 32, 32, current_x, current_y, width, height, 4);
last_mouse_vis = 1;
} else {
last_mouse_vis = 0;
}
last_mouse_pos_x = current_x;
last_mouse_pos_y = current_y;
}
void gui_flip() {
gui_proc_input();
gui_draw();
gui_draw_mouse();
if (all_dirty) {
all_dirty = 0;
dirty_rect_count = 0;
memcpy(framebuffer, backbuffer, bytesPerLine * height);
return;
}
// Update only the dirty regions of the screen
for (int i = 0; i < dirty_rect_count; i++) {
struct rect_t *rect = &dirty_rects[i];
if (rect->x < 0) {
rect->x = 0;
}
if (rect->y < 0) {
rect->y = 0;
}
if (rect->x + rect->width > width) {
rect->width = width - rect->x;
}
if (rect->y + rect->height > height) {
rect->height = height - rect->y;
}
if (rect->width <= 0 || rect->height <= 0) {
continue;
}
for (int y = rect->y; y < rect->y + rect->height; y++) {
memcpy(framebuffer + y * bytesPerLine + rect->x * depth,
backbuffer + y * bytesPerLine + rect->x * depth,
rect->width * depth);
}
}
// Clear dirty rectangles after updating
dirty_rect_count = 0;
}
static uint32_t blendPreMulAlpha(uint32_t p1, uint32_t p2) {
static const int AMASK = 0xFF000000;
static const int RBMASK = 0x00FF00FF;
static const int GMASK = 0x0000FF00;
static const int AGMASK = AMASK | GMASK;
static const int ONEALPHA = 0x01000000;
uint32_t a = (p2 & AMASK) >> 24;
uint32_t na = 255 - a;
uint32_t rb = ((na * (p1 & RBMASK)) + (a * (p2 & RBMASK))) >> 8;
uint32_t ag = (na * ((p1 & AGMASK) >> 8)) + (a * (ONEALPHA | ((p2 & GMASK) >> 8)));
return ((rb & RBMASK) | (ag & AGMASK));
}
void render(uint8_t *dest, uint8_t *src, uint32_t src_width, uint32_t src_height, int dest_x, int dest_y, uint32_t dest_width, uint32_t dest_height,
uint32_t src_depth) {
int i;
int pitch = dest_width * depth;
int src_off_x = 0;
int src_off_y = 0;
int index;
int dindex;
if (dest_x < 0) {
src_off_x = -dest_x;
dest_x = 0;
if (src_off_x >= src_width) {
return;
}
}
if (dest_y < 0) {
src_off_y = -dest_y;
dest_y = 0;
if (src_off_y >= src_height) {
return;
}
}
for (i = src_off_y; i < src_height; i++) {
uint32_t offset = (dest_x * depth) + (pitch * (dest_y + i));
uint8_t *pdest = (uint8_t *)(dest + offset);
uint8_t *psrc = (uint8_t *)(src + (src_width * src_depth * i));
int size = (src_width - src_off_x) * src_depth;
if (dest_y + i >= dest_height) {
break;
}
for (index = 0, dindex = 0; index < size; index += src_depth, dindex += depth) {
if (dest_x + (dindex / depth) >= dest_width) {
break;
}
if (psrc[index + (src_off_x * src_depth) + 3] < 0xFF) {
uint32_t pixel1 = (pdest[dindex + 3] << 24) | (pdest[dindex + 2] << 16) | (pdest[dindex + 1] << 8) | pdest[dindex];
uint32_t pixel2 = (psrc[index + (src_off_x * src_depth) + 3] << 24) | (psrc[index + (src_off_x * src_depth) + 2] << 16) |
(psrc[index + (src_off_x * src_depth) + 1] << 8) | psrc[index + (src_off_x * src_depth)];
uint32_t pixel3 = blendPreMulAlpha(pixel1, pixel2);
pdest[dindex] = pixel3 & 0xff;
pdest[dindex + 1] = (pixel3 >> 8) & 0xff;
pdest[dindex + 2] = (pixel3 >> 16) & 0xff;
pdest[dindex + 3] = 0xff;
} else {
pdest[dindex] = psrc[index + (src_off_x * src_depth)];
pdest[dindex + 1] = psrc[index + (src_off_x * src_depth) + 1];
pdest[dindex + 2] = psrc[index + (src_off_x * src_depth) + 2];
pdest[dindex + 3] = psrc[index + (src_off_x * src_depth) + 3];
}
}
}
}
int gui_set_colour(struct gui_colour_scheme_t *colourscheme) {
sys_color.titlebar_focus = colourscheme->titlebar_focus;
sys_color.titlebar_focus_text = colourscheme->titlebar_focus_text;
sys_color.titlebar_unfocus = colourscheme->titlebar_unfocus;
sys_color.titlebar_unfocus_text = colourscheme->titlebar_unfocus_text;
sys_color.icon_title = colourscheme->icon_title;
sys_color.icon_title_text = colourscheme->icon_title_text;
return 1;
}
int gui_set_font(char *data) {
if (sysfont != (uint8_t *)fontdata)
dbfree(sysfont, "gui_set_font");
sysfont = (uint8_t *)dbmalloc(4096, "gui_set_font");
memcpy(sysfont, data, 4096);
return 1;
}
int gui_set_wallpaper(char *data, int len) {
wallpaper_lock = 1;
if (wallpaper != NULL) {
dbfree(wallpaper, "gui_set_wallpaper free");
}
wallpaper = (uint8_t *)dbmalloc(len, "gui_set_wallpaper");
if (!wallpaper) {
wallpaper_lock = 0;
return 0;
}
memcpy(wallpaper, data, len);
wallpaper_lock = 0;
all_dirty = 1;
return 1;
}
void gui_display_halt() {
uint8_t *poweroff;
fill_rect(backbuffer, width, height, 0, 0, width, height, sys_color.icon_title);
gui_convert_xpm(poweroff_xpm, &poweroff);
render(backbuffer, poweroff, 703, 450, 155, 24, width, height, 4);
memcpy(framebuffer, backbuffer, bytesPerLine * height);
}