// TODO : TEXTMODE FALLBACK! // TODO : OPTIMIZE FOR SPEED! // TODO : /dev/mouse (how does it work in linux in vim etc?) // TODO : check what is active,happens ONLY on // refresh screen! (mouse handling)? #include "compositor.h" #include "scheduler.h" #include "syscalls.h" #include "kernel.h" #include "kmalloc.h" #include "log.h" #include "vmem.h" #include "timer.h" #include "asm_x86.h" #include "mount.h" #include "fd.h" #include "lib/string/string.h" #define MAX_WINDOWS 50 #define MAX_PID 200 #define MAX_INVALID 100 // max pending invalidation rectangles per process #define MAX_ICONS 50 void load_bmp(uint8_t *dst,char *filename); ringbuffer bg_invl; static uint32_t *vmem=VMEM_FRAMEBUFFER; static uint8_t backbuffer[VESA_MAX_WIDTH*VESA_MAX_HEIGHT*4]; static uint8_t wallpaper [VESA_MAX_WIDTH*VESA_MAX_HEIGHT*4]; static uint8_t icon_data [VESA_MAX_WIDTH*VESA_MAX_HEIGHT*4]; /** struct holding invalidated screen rect */ struct inval { uint16_t x; uint16_t y; uint16_t w; uint16_t h; }; /** struct holding window data */ struct window { uint8_t *fb; // position signed to check for overflows. int16_t posx; int16_t posy; uint16_t width; uint16_t height; uint32_t pid; // owning process id bool active; // mouseover bool draw_meat; // redraw content? bool draw_border; // show borders bool on_close; // close button mouseover // set on init bool borderless; // never show borders bool fixed; // can not be moved bool no_close; // can not be moved struct pdirectory *vmem; // retreive from task manager? }; static uint16_t next_window=0; struct window bbwindow; struct window windows[MAX_WINDOWS]; struct window *pid_to_win[MAX_PID]; struct inval invalid[MAX_PID][MAX_INVALID]; uint16_t invalid_cnt[MAX_PID]; // fraembuffer info static uint16_t vesa_width; static uint16_t vesa_height; static uint16_t vesa_depth; static uint16_t vesa_pitch; // mouse state static int mouse_x; static int mouse_y; static int last_mouse_x; static int last_mouse_y; static int mouse_k=0; // icons static int mouse_icon_width=25; static int mouse_icon_height=25; static int mouse_icon_lx=0; static int mouse_icon_ly=0; static int mouse_icon_active_lx=0; static int mouse_icon_active_ly=25; static int X_icon_width=25; static int X_icon_height=25; static int X_icon_lx=0; static int X_icon_ly=50; static int X_icon_active_lx=0; static int X_icon_active_ly=75; // costly but beautiful static bool option_blending=false; // so better disable! // ugly and cheap static bool option_win_borders=true; // so enable // framerate control static bool skip_render; /** print single pixel to dst without bound checking */ static void put_pixel(uint8_t *dst, int x,int y, uint32_t color,uint16_t pitch,uint8_t depth,bool alpha) { uint8_t *p=dst+y*pitch+x*depth; *p++=color; *p++=color>>8; *p++=color>>16; if(alpha)*p=color>>24; } /** retrieves single pixel from src without bound checking */ static uint32_t get_pixel(uint8_t *src, int x,int y,uint16_t pitch,uint8_t depth,bool alpha) { uint32_t col=0; uint8_t *p=src+y*pitch+x*depth; col=(*p++); col+=(*p++)<<8; col+=(*p++)<<16; if(alpha)col+=(*p)<<24; return col; } /** print single pixel to dst without bound checking using alpha value, encoded alpha in the highest octet. 1-opaque 0-transparent no premultiplied alpha. TODO: change maybe for speedup!? **/ static void put_pixel_alpha(uint8_t *dst, int x,int y, uint32_t color,uint16_t pitch, uint8_t depth,bool alpha) { uint32_t col=get_pixel(dst,x,y,vesa_pitch,vesa_depth,false); uint8_t dst_b=col&0xff; uint8_t dst_g=(col&0xff00)>>8; uint8_t dst_r=(col&0xff0000)>>16; uint8_t src_b=color&0xff; uint8_t src_g=(color&0xff00)>>8; uint8_t src_r=(color&0xff0000)>>16; uint8_t src_a=(color&0xff000000)>>24; uint8_t out_r=((src_r*(src_a))>>8)+(((0xff-src_a)*dst_r)>>8); uint8_t out_g=((src_g*(src_a))>>8)+(((0xff-src_a)*dst_g)>>8); uint8_t out_b=((src_b*(src_a))>>8)+(((0xff-src_a)*dst_b)>>8); put_pixel(dst, x,y,(out_r<<16)+(out_g<<8)+out_b,vesa_pitch,vesa_depth,false); } // TODO fuck transparancey key. we have alfa now static void cpy_rect(uint8_t *src, uint16_t src_pitch, uint16_t src_x, uint16_t src_y, uint8_t *dst, uint16_t dst_pitch, uint16_t dst_x, uint16_t dst_y, uint16_t width, uint16_t height, bool bnd_chk, uint8_t mult, uint8_t div, bool transp, uint32_t transp_key, bool alpha) { for(int x=0;xvmem) { mydir=x86_get_page_directory(); x86_set_page_directory(win->vmem); } for(int i=0;ifb,win->width*4, 0,0, // from dst,vesa_pitch, win->posx,win->posy, win->width,win->height, false, 0, 0, // scaling not implemented yet anyway, not sure how to use them :P true, // transparency color key 0xff00ff // color key of transp ,true ); } else { uint32_t pid=windows[i].pid; // if too many areas invalidated refresh whole window if(invalid_cnt[pid]>=MAX_INVALID) { invalid[pid][0].x=win->posx; invalid[pid][0].y=win->posy; invalid[pid][0].w=win->width; invalid[pid][0].h=win->height; invalid_cnt[pid]=1; } for(int j=0;jx-win->posx; int from_y=inv->y-win->posy; int to_x=inv->x; int width=inv->w; int to_y=inv->y; int height=inv->h; if(from_x<0) { to_x=inv->x-from_x; width=inv->w+from_x; from_x=0; } if(from_y<0) { to_y=inv->y-from_y; height=inv->h+from_y; from_y=0; } if(win->widthwidth-from_x; if(win->heightheight-from_y; //klog("rep from %d %d , to %d %d , size %d %d",from_x,from_y,to_x,to_y,width,height); if(width>0&&height>0) cpy_rect(win->fb,win->width*4, from_x,from_y, // from dst,vesa_pitch, to_x,to_y, width,height, false, 0, 0, // scaling not implemented yet anyway, not sure how to use them :P true, // transparency color key 0xff00ff // color key of transp ,alpha ); } } } if(win->vmem) { x86_set_page_directory(mydir); } // CLOSE ICON // if(!win->no_close) { int iconx=X_icon_lx; int icony=X_icon_ly; if(win->on_close) { iconx=X_icon_active_lx; icony=X_icon_active_ly; } cpy_rect(icon_data,vesa_pitch,iconx,icony, // from dst, vesa_pitch,win->posx+win->width-X_icon_width,win->posy, // to X_icon_width,X_icon_height, // icon dimensions false, 0, 0, // scaling not implemented yet anyway, not sure how to use them :P true, // transparency color key 0xff00ff // color key of transp ,true ); } // BORDERS // if(option_win_borders&&!win->borderless) { uint32_t border_col=0x002b36; if(win->active)border_col=0xfdfd6e3; for(uint16_t x=win->posx;xposx+win->width;x++) { put_pixel(dst,x,win->posy,border_col,vesa_pitch,vesa_depth,false); //TOP put_pixel(dst,x,win->posy+win->height-1,border_col,vesa_pitch,vesa_depth,false); //BOTTOM } for(uint16_t y=win->posy;yposy+win->height;y++) { put_pixel(dst,win->posx,y,border_col,vesa_pitch,vesa_depth,false); //LEFT put_pixel(dst,win->posx+win->width-1,y,border_col,vesa_pitch,vesa_depth,false); //RIGHT } } // } static void invalidate_all() { invalid[0][invalid_cnt[0]].x=0; invalid[0][invalid_cnt[0]].y=0; invalid[0][invalid_cnt[0]].w=vesa_width; invalid[0][invalid_cnt[0]].h=vesa_height; invalid_cnt[0]++; } static void put_mouse(uint8_t *dst,bool inval) { static int last_put_mouse_x=0; static int last_put_mouse_y=0; int iconx=mouse_icon_lx; int icony=mouse_icon_ly; if(mouse_k&1) { iconx=mouse_icon_active_lx; icony=mouse_icon_active_ly; } last_put_mouse_x=mouse_x; last_put_mouse_y=mouse_y; invalid[0][invalid_cnt[0]].x=mouse_x; invalid[0][invalid_cnt[0]].y=mouse_y; invalid[0][invalid_cnt[0]].w=mouse_icon_width; invalid[0][invalid_cnt[0]].h=mouse_icon_height; invalid_cnt[0]++; if(inval) { return; } cpy_rect(icon_data,vesa_pitch,iconx,icony, // from dst, vesa_pitch,mouse_x,mouse_y, // to mouse_icon_width,mouse_icon_height, // icon dimensions false, 0, 0, // scaling not implemented yet anyway, not sure how to use them :P true, // transparency color key 0xff00ff // color key of transp ,true ); } void compositor_del_window(uint32_t addr) { for(int i=0;i3000) { klog("fps: %d",frames*1000/(now-last)); last=now; frames=0; } for(int i=0;i=0;i--) { uint32_t pid=windows[i].pid; invalid_cnt[pid]=0; } put_mouse(backbuffer,true);//invalidate only } void check_win_pos(struct window *w) { if(w->posx<0)w->posx=0; if(w->posy<0)w->posy=0; if(w->posx+w->width>=vesa_width)w->posx=vesa_width-w->width-1; if(w->posy+w->height>=vesa_height)w->posy=vesa_height-w->height-1; } void compositor_kb_handle(char c) { for(int i=0;iactive) { // klog("writing [%c] to window with pid [%d]",c,w->pid); fd_write(get_tty(w->pid),c); } } } void compositor_mouse_handle(int16_t diff_x,int16_t diff_y, uint8_t key) { // klog("%d %d %d",diff_x,diff_y,key); static uint8_t lastkey=0; if(!(key&2)&&(lastkey&2)) // right mouse click { klog("tell init to spawn new foolshell"); fd_write(get_tty(1),'0'); // tell init to spawn new xterm } mouse_x+=diff_x; mouse_y+=diff_y; mouse_k=key; if(mouse_x<0)mouse_x=0; if(mouse_y<0)mouse_y=0; if(mouse_x>=vesa_width-mouse_icon_width)mouse_x=vesa_width-1-mouse_icon_width; if(mouse_y>=vesa_height-mouse_icon_height)mouse_y=vesa_height-1-mouse_icon_height; if(!(key&1)) { last_mouse_x=mouse_x; last_mouse_y=mouse_y; } int active=-1; bool active_win=false; // top most window first for(int i=next_window;i>=0;i--) { struct window *w=&windows[i]; w->on_close=false; if(w->fixed)continue; if(active==-1&&mouse_x>w->posx&&mouse_xposx+w->width&&mouse_y>w->posy&&mouse_yposy+w->height) { //// if(key&1) //// { //// //// w->draw_meat=false; //// uint32_t xy=(w->posx<<16)|(w->posy); //// uint32_t wh=(640<<16)|(400); //// //// /* //// ringbuffer_put(&bg_invl,xy&0x000000ff); //// ringbuffer_put(&bg_invl,(xy&0x0000ff00)>>8); //// ringbuffer_put(&bg_invl,(xy&0x00ff0000)>>16); //// ringbuffer_put(&bg_invl,(xy&0xff000000)>>24); //// //// ringbuffer_put(&bg_invl,wh&0x000000ff); //// ringbuffer_put(&bg_invl,(wh&0x0000ff00)>>8); //// ringbuffer_put(&bg_invl,(wh&0x00ff0000)>>16); //// ringbuffer_put(&bg_invl,(wh&0xff000000)>>24); //// */ //// //// w->posx-=last_mouse_x-mouse_x; //// last_mouse_x=mouse_x; //// //// w->posy-=last_mouse_y-mouse_y; //// last_mouse_y=mouse_y; //// //// check_win_pos(w); //// //// w->draw_border=true; //// //// } //// else //// { //// w->draw_meat=true; //// if(lastkey&1) //// { //// w->draw_border=true; //// uint32_t xy=(0<<16)|(0); //// uint32_t wh=(640<<16)|(400); //// /////// ringbuffer_put(w->invl,xy&0x000000ff); /////// ringbuffer_put(w->invl,(xy&0x0000ff00)>>8); /////// ringbuffer_put(w->invl,(xy&0x00ff0000)>>16); /////// ringbuffer_put(w->invl,(xy&0xff000000)>>24); /////// /////// ringbuffer_put(w->invl,wh&0x000000ff); /////// ringbuffer_put(w->invl,(wh&0x0000ff00)>>8); /////// ringbuffer_put(w->invl,(wh&0x00ff0000)>>16); /////// ringbuffer_put(w->invl,(wh&0xff000000)>>24); //// } //// //// } w->active=true; active_win=true; active=i; if(mouse_x>w->posx+w->width-X_icon_width&&mouse_yposy+X_icon_height)w->on_close=true; } else { w->active=false; } } /* if(active_win&&active!=-1&&active!=0&&(key&1)) { struct window temp; temp=windows[active]; windows[active]=windows[0]; pid_to_win[windows[0].pid]=&windows[active]; windows[0]=temp; pid_to_win[windows[0].pid]=&windows[0]; } */ lastkey=key; } /** THIS CAN LOAD WINDOWS ARGB BITMAPS WITH BITMAPINFOHEADER */ void load_bmp(uint8_t *dst,char *filename) { klog("loading bitmap %s",filename); fd dat=mount_file_open(filename); char B=fd_read(&dat); // 0x0 char M=fd_read(&dat); // 0x1 //klog("id = %c%c",M,B); if(M!='M'||B!='B')kpanic("not a (windows) bitmap!"); uint32_t size=(fd_read(&dat))+(fd_read(&dat)<<8)+(fd_read(&dat)<<16)+(fd_read(&dat)<<24); //0x02 klog("bytes = 0x%08x",size); // skip next 4 reserved bytes fd_read(&dat); fd_read(&dat); fd_read(&dat); fd_read(&dat); uint32_t off=(fd_read(&dat))+(fd_read(&dat)<<8)+(fd_read(&dat)<<16)+(fd_read(&dat)<<24); //0x0a klog("offset = 0x%08X",off); // skip next 4 bytes fd_read(&dat); fd_read(&dat); fd_read(&dat); fd_read(&dat); uint32_t w=(fd_read(&dat))+(fd_read(&dat)<<8)+(fd_read(&dat)<<16)+(fd_read(&dat)<<24); //0x12 uint32_t h=(fd_read(&dat))+(fd_read(&dat)<<8)+(fd_read(&dat)<<16)+(fd_read(&dat)<<24); //0x16 klog("width=%d,height=%d",w,h); for(int i=0;i=vesa_width)continue; if(h-y-1>=vesa_height)continue; put_pixel(dst,x,h-y-1,val,vesa_pitch,4,true); } } } void compositor_invalidate(uint32_t pid,uint16_t x, uint16_t y, uint16_t w, uint16_t h,uint32_t *fb) { // klog("pid %d invalidating %dx%d rect at %d %d with fb addr 0x%08x",pid,w,h,x,y,fb); if(fb!=0)pid_to_win[pid]->fb=fb; if(invalid_cnt[pid]>=MAX_INVALID){ // klog("compositor: invalidation buffer is full for pid: %d",pid); return;//invalidation buffer is full. } invalid[pid][invalid_cnt[pid]].x=x+pid_to_win[pid]->posx; invalid[pid][invalid_cnt[pid]].y=y+pid_to_win[pid]->posy; invalid[pid][invalid_cnt[pid]].w=w; invalid[pid][invalid_cnt[pid]].h=h; invalid_cnt[pid]++; } int compositor_create_window(uint32_t pid,uint16_t x, uint16_t y,uint16_t w,uint16_t h,uint16_t flags) { klog("pid %d creating %dx%d win at %d %d with flags 0x%02x",pid,w,h,x,y,flags); if (next_window>=MAX_WINDOWS)kpanic("max number of windows reached. increase MAX_WINDOWS"); int idx=next_window; // auto position if(x==0xffff&&y==0xffff) { windows[idx].posx=mouse_x-(w/2); windows[idx].posy=mouse_y-(h/2); } else { windows[idx].posx=x; windows[idx].posy=y; } windows[idx].width=w; windows[idx].height=h; check_win_pos(&windows[idx]); windows[idx].active=0; windows[idx].fb=0; windows[idx].vmem=scheduler_get_vmem(pid); windows[idx].pid=pid; windows[idx].draw_border=true; windows[idx].draw_meat=true; windows[idx].on_close=false; windows[idx].borderless=flags&1; windows[idx].fixed=flags&2; windows[idx].borderless=flags&1; windows[idx].no_close=false; pid_to_win[pid]=&windows[idx]; next_window++; return 0;// ok } void compositor_destroy_window(uint32_t pid) { klog("NOT IMPL: destroying win for pid %d",pid); }