quinn-os/gui.c
2015-09-03 14:22:38 +10:00

671 lines
18 KiB
C

#include "multiboot.h"
#include "font.h"
#include "pointer.xpm"
#include "minimize.xpm"
#include "close.xpm"
#include "defaulticon.xpm"
#include "keyboard.h"
#include "gui.h"
#include "schedule.h"
#include "mouse.h"
#include "string.h"
#include "memory.h"
#include "console.h"
#include "wallpaper.xpm"
extern struct task_t *current_task;
extern const struct bitmap_font font;
#define BochsBreak() outportw(0x8A00,0x8A00); outportw(0x8A00,0x08AE0);
#define BochsConsolePrintChar(c) outportb(0xe9, c)
void render(unsigned char* dest, unsigned char* src, unsigned int src_width, unsigned int src_height, int dest_x, int dest_y, unsigned int dest_width, unsigned int dest_height);
void gui_pagemap();
struct ModeInfoBlock {
unsigned short attributes;
unsigned char winA,winB;
unsigned short granularity;
unsigned short winsize;
unsigned short segmentA, segmentB;
unsigned int winFuncPointer;
unsigned short pitch; // bytes per scanline
unsigned short Xres, Yres;
unsigned char Wchar, Ychar, planes, bpp, banks;
unsigned char memory_model, bank_size, image_pages;
unsigned char reserved0;
unsigned char red_mask, red_position;
unsigned char green_mask, green_position;
unsigned char blue_mask, blue_position;
unsigned char rsv_mask, rsv_position;
unsigned char directcolor_attributes;
unsigned int physbase; // your LFB (Linear Framebuffer) address ;)
unsigned int reserved1;
unsigned short reserved2;
} __attribute__((packed));
unsigned char *defaulticon;
unsigned char *mouseptr;
unsigned char *backbuffer;
unsigned char vidmode;
unsigned char *framebuffer;
unsigned short width, height, depth, bytesPerLine;
unsigned char *minimizebtn;
unsigned char *closebtn;
unsigned char *wallpaper;
unsigned int window_count = 0;
struct window_t **windows;
extern int mouse_pos_x;
extern int mouse_pos_y;
extern char middle_btn;
extern char left_btn;
extern char right_btn;
unsigned int serialnos;
struct ModeInfoBlock *modeinfo;
void gui_convert_xpm(char **xpm, unsigned char **buffer);
void init_gui(multiboot_info_t *mbinfo) {
if (mbinfo->flags & 1 << 11) {
serialnos = 0;
vidmode = 1;
modeinfo = (struct ModeInfoBlock *)mbinfo->vbe_mode_info;
framebuffer = (char *)modeinfo->physbase;
bytesPerLine = modeinfo->pitch;
width = modeinfo->Xres;
height = modeinfo->Yres;
depth = modeinfo->bpp / 8;
gui_pagemap();
backbuffer = (unsigned char *)malloc(bytesPerLine * height);
memset(backbuffer, 0, bytesPerLine * height);
windows = (void *)0;
gui_convert_xpm(close_xpm, &closebtn);
gui_convert_xpm(pointer_xpm, &mouseptr);
gui_convert_xpm(minimize_xpm, &minimizebtn);
gui_convert_xpm(defaulticon_xpm, &defaulticon);
gui_convert_xpm(wallpaper_xpm, &wallpaper);
//BochsBreak();
}
}
void fill_rect(unsigned char *buffer, unsigned int dest_width, unsigned int dest_height, int sx, int sy, int w, int h, unsigned int colour) {
int j;
int k;
unsigned int 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;
}
where += (dest_width) * depth;
}
}
void gui_putpixel(unsigned char *buffer, unsigned int dest_width, unsigned int dest_height, int x, int y, unsigned int color) {
if (x >= dest_width || y >= dest_height) {
return;
}
if (x < 0 || y < 0) {
return;
}
int where = (x * depth) + (y * dest_width * depth);
buffer[where] = color & 255; // BLUE
buffer[where + 1] = (color >> 8) & 255; // GREEN
buffer[where + 2] = (color >> 16) & 255; // RED
}
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, unsigned char **buffer) {
unsigned int bwidth;
unsigned int bheight;
unsigned int colours;
unsigned int 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
unsigned int *theColours = (unsigned int *)malloc(sizeof(unsigned int) * colours);
char **theKeys = (char**)malloc(colours * sizeof(char *));
int i,j,k,l;
for (i=0;i<colours;i++) {
theKeys[i] = (char *)malloc(charsperpixel);
for (j=0;j<charsperpixel;j++) {
theKeys[i][j] = xpm[i+1][j];
}
offset = 3 + charsperpixel;
if (xpm[i+1][offset] == '#') {
theColours[i] = 0;
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] = 0xff000000;
}
}
unsigned char *ptr = (unsigned char *)malloc(bwidth * bheight * depth);
*buffer = ptr;
memset(ptr, 0, bwidth * bheight * depth);
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) {
if (theColours[k] != 0xff000000) {
ptr[boffset] = theColours[k] & 255; // BLUE
ptr[boffset + 1] = (theColours[k] >> 8) & 255; // GREEN
ptr[boffset + 2] = (theColours[k] >> 16) & 255; // RED
}
boffset+=depth;
break;
}
}
}
}
free(theColours);
for (i=0;i<charsperpixel;i++) {
free(theKeys[i]);
}
free(theKeys);
}
void gui_display_pointer() {
int x;
int y;
render(backbuffer, mouseptr, 32, 32, mouse_pos_x, mouse_pos_y, width, height);
//flip();
}
void gui_draw_char(unsigned char *buffer, unsigned int dest_width, unsigned int dest_height, unsigned int x, unsigned int y, char c, unsigned int colour) {
int i;
int l;
for (i=0;i<font.Chars;i++) {
if (font.Index[i] == c) break;
}
const char *font_char = &font.Bitmap[i * font.Height];
int cx,cy;
//int mask[8]={1,2,4,8,16,32,64,128};
int mask[8]={128,64,32,16,8,4,2,1};
for(cy=0;cy<16;cy++){
for(cx=0;cx<8;cx++){
if(font_char[cy]&mask[cx]) gui_putpixel(buffer, dest_width, dest_height, x+cx,y+cy, colour);
}
}
}
void draw_text(unsigned char *buffer, unsigned int dest_width, unsigned int dest_height, int sx, int sy, unsigned int 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);
}
}
void gui_keyboard_in(char c) {
if (windows[window_count-1]->key_buffer_at == 255) {
return;
}
windows[window_count-1]->key_buffer[windows[window_count-1]->key_buffer_at++] = c;
}
int gui_add_window(unsigned char *contents, char *name, int x, int y, int w, int h, unsigned char *icon, void (*keyboard_d)(char c)) {
struct window_t *new_window;
int i;
if (window_count == 0) {
windows = (struct window_t **)malloc(sizeof(struct window_t *));
} else {
windows = (struct window_t **)realloc(windows, sizeof(struct window_t *) * (window_count + 1));
}
new_window = (struct window_t *)malloc(sizeof(struct window_t));
new_window->serialno = ++serialnos;
new_window->posx = x;
new_window->posy = y;
new_window->width = w;
new_window->height = h;
new_window->key_buffer_at = 0;
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;
if (contents == (void *)0) {
new_window->contents = (unsigned char *)malloc(w * h * depth);
memset(new_window->contents, 0, w * h * depth);
} else {
new_window->contents = contents;
}
if (keyboard_d == (void *)0) {
new_window->keyboard_dest = gui_keyboard_in;
} else {
new_window->keyboard_dest = keyboard_d;
}
if (icon == (void *)0) {
new_window->icon = (unsigned char *)malloc(64 * 64 * depth);
memcpy(new_window->icon, defaulticon, 64 * 64 * depth);
} else {
new_window->icon = (unsigned char *)malloc(64 * 64 * depth);
memcpy(new_window->icon, icon, 64 * 64 * depth);
}
strncpy(new_window->name, name, 128);
new_window->zorder = window_count;
windows[window_count] = new_window;
window_count++;
keyboard_set_handler(new_window->keyboard_dest);
if (current_task->window_count == 0) {
current_task->window_list = (struct window_t **)malloc(sizeof(struct window_t *));
} else {
current_task->window_list = (struct window_t **)realloc(current_task->window_list, sizeof(struct window_t *) * (current_task->window_count + 1));
}
current_task->window_list[current_task->window_count] = new_window;
current_task->window_count++;
return new_window->serialno;
}
void gui_destroy_window(int serialno) {
struct window_t *window;
int i, j;
for (i=0;i<window_count;i++) {
if (windows[i]->serialno == serialno) {
window = windows[i];
for (j=i;j<window_count-1;j++) {
windows[j] = windows[j+1];
}
windows = (struct window_t **)realloc(windows, sizeof(struct window_t *) * (window_count-1));
window_count--;
keyboard_set_handler(windows[window_count-1]->keyboard_dest);
free(window->icon);
free(window->contents);
free(window);
return;
}
}
kprintf("Window not found %d\n", serialno);
}
void gui_drawtitlebar(unsigned char *buffer, unsigned int dest_width, unsigned int dest_height, struct window_t *window) {
fill_rect(buffer, dest_width, dest_height, window->posx, window->posy - 25, window->width, 25, 0xf182f8);
draw_text(buffer, dest_width, dest_height, window->posx + 25 + 4, window->posy - 25 + 4, 0xffffff, window->name);
render(buffer, minimizebtn, 16, 16, window->posx + window->width - 20, window->posy - 20, dest_width, dest_height);
render(buffer, closebtn, 16, 16, window->posx + 5, window->posy - 20, dest_width, dest_height);
}
void gui_pagemap() {
if (vidmode == 1) {
framebuffer = mem_map_framebuffer((unsigned int)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<window_count;j++) {
if (!(windows[j]->iconx == 0 && windows[j]->icony == 0)) {
if (!(x > windows[j]->iconx && x < windows[j]->iconx + 96 && y > windows[j]->icony && y < windows[j]->icony + 96) && !(windows[j]->icony == y && windows[j]->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, unsigned char *bmp) {
struct window_t *win = (void *)0;
int i;
for (i=0;i<window_count;i++) {
if (windows[i]->serialno == wh) {
win = windows[i];
break;
}
}
if (win == (void *)0) {
return 0;
}
render(win->contents, bmp, w, h, x, y, win->width, win->height);
return 1;
}
int gui_req_input(int wh, struct window_update_req *req) {
struct window_t *win = (void *)0;
int i;
for (i=0;i<window_count;i++) {
if (windows[i]->serialno == wh) {
win = windows[i];
break;
}
}
if (win == (void *)0) {
return 0;
}
req->close_req = win->close_req;
memcpy(req->key_buffer, win->key_buffer, 256);
req->key_buffer_at = win->key_buffer_at;
req->lclick = win->lclick;
req->rclick = win->rclick;
req->mclick = win->mclick;
req->mousex = mouse_pos_x - win->posx;
req->mousey = mouse_pos_y - win->posy;
win->close_req = 0;
win->key_buffer_at = 0;
return 1;
}
void gui_flip() {
int i, c, d;
struct window_t *swap;
// do click
if (mouse_get_click(0) == 1) {
for (i=window_count-1;i>=0;i--) {
if (!(windows[i]->minimized)) {
if (mouse_pos_x > windows[i]->posx && mouse_pos_x < windows[i]->posx + windows[i]->width && mouse_pos_y > windows[i]->posy - 25 && mouse_pos_y < windows[i]->posy + windows[i]->height) {
// send mouse click to client
if (mouse_pos_x > windows[i]->posx + windows[i]->width - 25 && mouse_pos_x < windows[i]->posx + windows[i]->width && mouse_pos_y > windows[i]->posy - 25 && mouse_pos_y < windows[i]->posy) {
// minimize
windows[i]->minimized = 1;
if (windows[i]->iconx == 0 && windows[i]->icony == 0) {
gui_find_icon_space(windows[i]);
}
} else if (mouse_pos_x > windows[i]->posx && mouse_pos_x < windows[i]->posx + 25 && mouse_pos_y > windows[i]->posy - 25 && mouse_pos_y < windows[i]->posy) {
windows[i]->close_req = 1;
} else if (windows[i]->zorder != window_count -1) {
windows[window_count-1]->zorder = windows[i]->zorder;
windows[i]->zorder = window_count - 1;
keyboard_set_handler(windows[i]->keyboard_dest);
}
break;
}
}
}
for (i=window_count-1;i>=0;i--) {
if (windows[i]->minimized) {
if (mouse_pos_x > windows[i]->iconx -16 && mouse_pos_x < windows[i]->iconx + 72 && mouse_pos_y > windows[i]->icony -16 && mouse_pos_y < windows[i]->icony + 72) {
windows[i]->minimized = 0;
break;
}
}
}
}
// process mouse down / up events
for (i=window_count-1;i>=0;i--) {
if (!windows[i]->minimized) {
if (mouse_pos_x > windows[i]->posx && mouse_pos_x < windows[i]->posx + windows[i]->width && mouse_pos_y > windows[i]->posy && mouse_pos_y < windows[i]->posy + windows[i]->height) {
windows[i]->lclick = left_btn;
windows[i]->rclick = right_btn;
windows[i]->mclick = middle_btn;
}
}
}
int handled_drag = 0;
if (mouse_is_dragging(0)) {
for (i=window_count-1;i>=0;i--) {
if (!(windows[i]->minimized)) {
if (windows[i]->drag_start_x > -1) {
int xoffset = mouse_pos_x - windows[i]->drag_start_x;
int yoffset = mouse_pos_y - windows[i]->drag_start_y;
windows[i]->posx += xoffset;
windows[i]->posy += yoffset;
}
if (mouse_pos_x > windows[i]->posx && mouse_pos_x < windows[i]->posx + windows[i]->width && mouse_pos_y > windows[i]->posy - 25 && mouse_pos_y < windows[i]->posy) {
windows[i]->drag_start_x = mouse_pos_x;
windows[i]->drag_start_y = mouse_pos_y;
handled_drag = 1;
break;
}
}
}
if (!handled_drag) {
for (i=window_count-1;i>=0;i--) {
if (windows[i]->minimized) {
if (windows[i]->drag_start_x > -1) {
int xoffset = mouse_pos_x - windows[i]->drag_start_x;
int yoffset = mouse_pos_y - windows[i]->drag_start_y;
windows[i]->iconx += xoffset;
windows[i]->icony += yoffset;
}
if (mouse_pos_x > windows[i]->iconx -16 && mouse_pos_x < windows[i]->iconx + 72 && mouse_pos_y > windows[i]->icony -16 && mouse_pos_y < windows[i]->icony + 72) {
windows[i]->drag_start_x = mouse_pos_x;
windows[i]->drag_start_y = mouse_pos_y;
}
}
}
}
} else {
for (i=window_count-1;i>=0;i--) {
windows[i]->drag_start_x = -1;
windows[i]->drag_start_y = -1;
}
}
// sort windows
for (c = 0 ; c < ( window_count - 1 ); c++) {
for (d = 0 ; d < window_count - c - 1; d++) {
if (windows[d]->zorder > windows[d+1]->zorder) {
swap = windows[d];
windows[d] = windows[d+1];
windows[d+1] = swap;
}
}
}
// draw desktop
//fill_rect(backbuffer, width, height, 0, 0, width, height, 0xc0c0c0);
render(backbuffer, wallpaper, 1024, 768, 0, 0, width, height);
// draw icons
for (i=0;i<window_count;i++) {
if (windows[i]->minimized == 1) {
render(backbuffer, windows[i]->icon, 64, 64, windows[i]->iconx, windows[i]->icony, width, height);
fill_rect(backbuffer, width, height, (48 - (strlen(windows[i]->name) * 8 / 2)) + (windows[i]->iconx - 16), windows[i]->icony + 74, strlen(windows[i]->name) * 8, 16, 0xf9c5ff);
draw_text(backbuffer, width, height, (48 - (strlen(windows[i]->name) * 8 / 2)) + (windows[i]->iconx - 16), windows[i]->icony + 74, 0, windows[i]->name);
}
}
// draw windows
for (i=0;i<window_count;i++) {
if (windows[i]->minimized == 0) {
gui_drawtitlebar(backbuffer, width, height, windows[i]);
render(backbuffer, windows[i]->contents, windows[i]->width, windows[i]->height, windows[i]->posx, windows[i]->posy, width, height);
}
}
// draw mouse
gui_display_pointer();
// flip
memcpy(framebuffer, backbuffer, bytesPerLine * height);
}
void render(unsigned char* dest, unsigned char* src, unsigned int src_width, unsigned int src_height, int dest_x, int dest_y, unsigned int dest_width, unsigned int dest_height) {
int i;
int pitch = dest_width * depth;
int src_off_x = 0;
int src_off_y = 0;
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++) {
unsigned int offset = (dest_x * depth) + (pitch * (dest_y + i));
unsigned char *pdest = (unsigned char *)(dest + offset);
unsigned char *psrc = (unsigned char *)(src + (src_width * depth * i));
int size = (src_width - src_off_x) * depth;
if (dest_y + i >= dest_height) {
break;
}
for (int index = 0; index < size; index += depth) {
if (dest_x + (index / depth) > dest_width) {
break;
}
if (psrc[index] == 0xFF && psrc[index + 1] == 0x00 && psrc[index + 2] == 0xFF) continue;
pdest[index] = psrc[index + (src_off_x * depth)];
pdest[index+1] = psrc[index + (src_off_x * depth) + 1];
pdest[index+2] = psrc[index + (src_off_x * depth) + 2];
}
}
}