/* framebuffer.c * Linux framebuffer code * (c) 2002 Petr 'Brain' Kulhavy * This file is a part of the Links program, released under GPL. */ #include "cfg.h" #ifdef GRDRV_FB #define USE_GPM_DX /* #define FB_DEBUG */ /* #define SC_DEBUG */ /* note: SIGUSR1 is used by libpthread and is disabled even if no thread functions are called --- do not use */ #define SIG_REL SIGUSR2 #define SIG_ACQ SIGVTALRM #if defined(FB_DEBUG) || defined(SC_DEBUG) #define MESSAGE(a) fprintf(stderr,"%s",a); #endif #include "links.h" #include "bits.h" #include #include #include #include #include #include #include "arrow.inc" #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1L) #endif #ifdef GPM_HAVE_SMOOTH #define gpm_smooth GPM_SMOOTH #else #define gpm_smooth 0 #endif static int TTY = 0; #ifndef USE_GPM_DX static int fb_txt_xsize, fb_txt_ysize; static struct winsize fb_old_ws; static struct winsize fb_new_ws; static int fb_old_ws_v; static int fb_msetsize; #endif static int fb_hgpm; static int fb_console; static struct itrm *fb_kbd; static struct graphics_device *fb_old_vd; static struct graphics_device *fb_block_dev; static int fb_handler; static unsigned char *fb_mem, *fb_vmem; static unsigned fb_mem_size; static unsigned fb_mapped_size; static int fb_linesize,fb_bits_pp,fb_pixelsize; static int fb_xsize,fb_ysize; static long border_left, border_right, border_top, border_bottom; static int fb_colors, fb_palette_colors; static struct fb_var_screeninfo vi; static struct fb_fix_screeninfo fi; static void fb_draw_bitmap(struct graphics_device *dev,struct bitmap* hndl, int x, int y); static unsigned char *fb_driver_param; struct graphics_driver fb_driver; static int have_cmap=0; static volatile int fb_active=1; struct palette { unsigned short *red; unsigned short *green; unsigned short *blue; }; static struct palette old_palette; static struct palette global_pal; static struct vt_mode vt_mode,vt_omode; static struct fb_var_screeninfo oldmode; static volatile int in_gr_operation; /* mouse */ static int mouse_x, mouse_y; /* mouse pointer coordinates */ static long mouse_black, mouse_white; static int background_x, background_y; /* Where was the mouse background taken from */ static unsigned char *mouse_buffer, *background_buffer, *new_background_buffer; static struct graphics_device *mouse_graphics_device; static int global_mouse_hidden; #define TEST_MOUSE(xl,xh,yl,yh) if (RECTANGLES_INTERSECT(\ (xl),(xh),\ background_x,background_x+arrow_width,\ (yl),(yh),\ background_y,background_y+arrow_height)\ && !global_mouse_hidden){\ mouse_hidden=1;\ hide_mouse();\ }else mouse_hidden=0; #define END_MOUSE if (mouse_hidden) show_mouse(); #define END_GR \ in_gr_operation--;\ if (!fb_active && !in_gr_operation)\ EINTRLOOP(rs, ioctl(TTY,VT_RELDISP,1)); #define START_GR \ in_gr_operation++; \ if (!fb_active) { END_GR; return; } #define START_GR_0 \ in_gr_operation++; \ if (!fb_active) { END_GR; return 0; } #define NUMBER_OF_DEVICES 10 #define TEST_INACTIVITY if (!fb_active||dev!=current_virtual_device) return; #define TEST_INACTIVITY_0 if (!fb_active||dev!=current_virtual_device) return 0; #define RECTANGLES_INTERSECT(xl0, xh0, xl1, xh1, yl0, yh0, yl1, yh1) (\ (xl0)<(xh1)\ && (xl1)<(xh0)\ && (yl0)<(yh1)\ && (yl1)<(yh0)) /* This assures that x, y, xs, ys, data will be sane according to clipping * rectangle. If nothing lies within this rectangle, the current function * returns. The data pointer is automatically advanced by this macro to reflect * the right position to start with inside the bitmap. */ #define CLIP_PREFACE \ int mouse_hidden;\ int xs=hndl->x,ys=hndl->y;\ unsigned char *data=hndl->data;\ \ TEST_INACTIVITY\ if (x>=dev->clip.x2||x+xs<=dev->clip.x1) return;\ if (y>=dev->clip.y2||y+ys<=dev->clip.y1) return;\ if (x+xs>dev->clip.x2) xs=dev->clip.x2-x;\ if (y+ys>dev->clip.y2) ys=dev->clip.y2-y;\ if (dev->clip.x1-x>0){\ xs-=(dev->clip.x1-x);\ data+=fb_pixelsize*(dev->clip.x1-x);\ x=dev->clip.x1;\ }\ if (dev->clip.y1-y>0){\ ys-=(dev->clip.y1-y);\ data+=hndl->skip*(dev->clip.y1-y);\ y=dev->clip.y1;\ }\ /* xs, ys: how much pixels to paint\ * data: where to start painting from\ */\ START_GR\ TEST_MOUSE (x,x+xs,y,y+ys) /* fill_area: 5,5,10,10 fills in 25 pixels! */ /* This assures that left, right, top, bottom will be sane according to the * clipping rectangle set up by svga_driver->set_clip_area. If empty region * results, return from current function occurs. */ #define FILL_CLIP_PREFACE \ int mouse_hidden;\ TEST_INACTIVITY\ if (left>=right||top>=bottom) return;\ if (left>=dev->clip.x2||right<=dev->clip.x1||top>=dev->clip.y2||bottom<=dev->clip.y1) return;\ if (leftclip.x1) left=dev->clip.x1;\ if (right>dev->clip.x2) right=dev->clip.x2;\ if (topclip.y1) top=dev->clip.y1;\ if (bottom>dev->clip.y2) bottom=dev->clip.y2;\ START_GR\ TEST_MOUSE(left,right,top,bottom) #define HLINE_CLIP_PREFACE \ int mouse_hidden;\ TEST_INACTIVITY\ if (yclip.y1||y>=dev->clip.y2||right<=dev->clip.x1||left>=dev->clip.x2) return;\ if (leftclip.x1) left=dev->clip.x1;\ if (right>dev->clip.x2) right=dev->clip.x2;\ if (left>=right) return;\ START_GR\ TEST_MOUSE (left,right,y,y+1) #define VLINE_CLIP_PREFACE \ int mouse_hidden;\ TEST_INACTIVITY\ if (xclip.x1||x>=dev->clip.x2||top>=dev->clip.y2||bottom<=dev->clip.y1) return;\ if (topclip.y1) top=dev->clip.y1;\ if (bottom>dev->clip.y2) bottom=dev->clip.y2;\ if (top>=bottom) return;\ START_GR\ TEST_MOUSE(x,x+1,top,bottom) #define HSCROLL_CLIP_PREFACE \ int mouse_hidden;\ TEST_INACTIVITY_0\ if (!sc) return 0;\ if (sc>(dev->clip.x2-dev->clip.x1)||-sc>(dev->clip.x2-dev->clip.x1))\ return 1;\ START_GR_0\ TEST_MOUSE (dev->clip.x1,dev->clip.x2,dev->clip.y1,dev->clip.y2) #define VSCROLL_CLIP_PREFACE \ int mouse_hidden;\ TEST_INACTIVITY_0\ if (!sc) return 0;\ if (sc>dev->clip.y2-dev->clip.y1||-sc>dev->clip.y2-dev->clip.y1) return 1;\ START_GR_0\ TEST_MOUSE (dev->clip.x1, dev->clip.x2, dev->clip.y1, dev->clip.y2)\ #include "fbcommon.inc" static void redraw_mouse(void); static void fb_mouse_move(int dx, int dy) { struct event ev; mouse_x += dx; mouse_y += dy; ev.ev = EV_MOUSE; if (mouse_x >= fb_xsize) mouse_x = fb_xsize - 1; if (mouse_y >= fb_ysize) mouse_y = fb_ysize - 1; if (mouse_x < 0) mouse_x = 0; if (mouse_y < 0) mouse_y = 0; ev.x = mouse_x; ev.y = mouse_y; ev.b = B_MOVE; if (!current_virtual_device) return; if (current_virtual_device->mouse_handler) current_virtual_device->mouse_handler(current_virtual_device, ev.x, ev.y, ev.b); redraw_mouse(); } static void fb_key_in(void *p, struct event *ev, int size) { if (size != sizeof(struct event)) return; if (ev->ev == EV_ABORT) terminate_loop = 1; if (ev->ev != EV_KBD) return; if ((ev->y & KBD_ALT) && ev->x >= '0' && ev->x <= '9') { switch_virtual_device((ev->x - '1' + 10) % 10); return; } if (!current_virtual_device) return; if (!ev->y && ev->x == KBD_F5) fb_mouse_move(-3, 0); else if (!ev->y && ev->x == KBD_F6) fb_mouse_move(0, 3); else if (!ev->y && ev->x == KBD_F7) fb_mouse_move(0, -3); else if (!ev->y && ev->x == KBD_F8) fb_mouse_move(3, 0); else { if (fb_driver.codepage!=utf8_table&&(ev->x)>=128&&(ev->x)<=255) if ((ev->x=cp2u(ev->x,fb_driver.codepage)) == -1) return; if (current_virtual_device->keyboard_handler) current_virtual_device->keyboard_handler(current_virtual_device, ev->x, ev->y); } } #define mouse_getscansegment(buf,x,y,w) memcpy(buf,fb_vmem+y*fb_linesize+x*fb_pixelsize,w) #define mouse_drawscansegment(ptr,x,y,w) memcpy(fb_vmem+y*fb_linesize+x*fb_pixelsize,ptr,w); /* Flushes the background_buffer onscreen where it was originally taken from. */ static void place_mouse_background(void) { struct bitmap bmp; bmp.x=arrow_width; bmp.y=arrow_height; bmp.skip=arrow_width*fb_pixelsize; bmp.data=background_buffer; { struct graphics_device * current_virtual_device_backup; current_virtual_device_backup=current_virtual_device; current_virtual_device=mouse_graphics_device; fb_draw_bitmap(mouse_graphics_device, &bmp, background_x, background_y); current_virtual_device=current_virtual_device_backup; } } /* Only when the old and new mouse don't interfere. Using it on interfering mouses would * cause a flicker. */ static void hide_mouse(void) { global_mouse_hidden=1; place_mouse_background(); } /* Gets background from the screen (clipping provided only right and bottom) to the * passed buffer. */ static void get_mouse_background(unsigned char *buffer_ptr) { int width,height,skip,x,y; skip=arrow_width*fb_pixelsize; x=mouse_x; y=mouse_y; width=fb_pixelsize*(arrow_width+x>fb_xsize?fb_xsize-x:arrow_width); height=arrow_height+y>fb_ysize?fb_ysize-y:arrow_height; for (;height;height--){ mouse_getscansegment(buffer_ptr,x,y,width); buffer_ptr+=skip; y++; } } /* Overlays the arrow's image over the mouse_buffer * Doesn't draw anything into the screen */ static void render_mouse_arrow(void) { int x,y, reg0, reg1; unsigned char *mouse_ptr=mouse_buffer; unsigned *arrow_ptr=arrow; for (y=arrow_height;y;y--){ reg0=*arrow_ptr; reg1=arrow_ptr[1]; arrow_ptr+=2; for (x=arrow_width;x;) { int mask=1<<(--x); if (reg0&mask) memcpy (mouse_ptr, &mouse_black, fb_pixelsize); else if (reg1&mask) memcpy (mouse_ptr, &mouse_white, fb_pixelsize); mouse_ptr+=fb_pixelsize; } } } static void place_mouse(void) { struct bitmap bmp; bmp.x=arrow_width; bmp.y=arrow_height; bmp.skip=arrow_width*fb_pixelsize; bmp.data=mouse_buffer; { struct graphics_device * current_graphics_device_backup; current_graphics_device_backup=current_virtual_device; current_virtual_device=mouse_graphics_device; fb_draw_bitmap(mouse_graphics_device, &bmp, mouse_x, mouse_y); current_virtual_device=current_graphics_device_backup; } global_mouse_hidden=0; } /* Only when the old and the new mouse positions do not interfere. Using this routine * on interfering positions would cause a flicker. */ static void show_mouse(void) { get_mouse_background(background_buffer); background_x=mouse_x; background_y=mouse_y; memcpy(mouse_buffer,background_buffer,fb_pixelsize*arrow_area); render_mouse_arrow(); place_mouse(); } /* Doesn't draw anything into the screen */ static void put_and_clip_background_buffer_over_mouse_buffer(void) { unsigned char *bbufptr=background_buffer, *mbufptr=mouse_buffer; int left=background_x-mouse_x; int top=background_y-mouse_y; int right,bottom; int bmpixelsizeL=fb_pixelsize; int number_of_bytes; int byte_skip; right=left+arrow_width; bottom=top+arrow_height; if (left<0){ bbufptr-=left*bmpixelsizeL; left=0; } if (right>arrow_width) right=arrow_width; if (top<0){ bbufptr-=top*bmpixelsizeL*arrow_width; top=0; } if (bottom>arrow_height) bottom=arrow_height; mbufptr+=bmpixelsizeL*(left+arrow_width*top); byte_skip=arrow_width*bmpixelsizeL; number_of_bytes=bmpixelsizeL*(right-left); for (;topfb_ysize) mouse_bottom=fb_ysize; if (background_bottom>fb_ysize) background_bottom=fb_ysize; /* Let's do the top part */ if (background_topfb_xsize?fb_xsize-background_left :arrow_width; for (;background_topmouse_top){ /* Draw the mouse */ mouse_length=mouse_right>fb_xsize ?fb_xsize-mouse_left:arrow_width; for (;mouse_topfb_xsize?fb_xsize-mouse_left:arrow_width; for (;mouse_topfb_xsize?fb_xsize-mouse_left:arrow_width; background_length=background_right-mouse_right; if (background_length+mouse_right>fb_xsize) background_length=fb_xsize-mouse_right; l1=mouse_length*fb_pixelsize; l2=(mouse_right-background_left)*fb_pixelsize; l3=background_length*fb_pixelsize; for (;mouse_top0) mouse_drawscansegment( background_ptr +l2, mouse_right,mouse_top ,l3); mouse_ptr+=skip; background_ptr+=skip; } } if (background_bottomfb_xsize?fb_xsize-mouse_left :arrow_width; for (;background_bottomfb_xsize?fb_xsize-background_left :arrow_width; for (;mouse_bottomred[a]=(a&8)?65535:0; palette->green[a]=((a>>1)&3)*(65535/3); palette->blue[a]=(a&1)?65535:0; } break; case 256: for (a=0;ared[a]=((a>>5)&7)*(65535/7); palette->green[a]=((a>>2)&7)*(65535/7); palette->blue[a]=(a&3)*(65535/3); } break; case 32768: for (a=0;ared[a]=((a>>10)&31)*(65535/31); palette->green[a]=((a>>5)&31)*(65535/31); palette->blue[a]=(a&31)*(65535/31); */ palette->red[a]= palette->green[a]= palette->blue[a]=(((a&31)*255)/31)*257; } break; case 65536: for (a=0;ared[a]=((a>>11)&31)*(65535/31); palette->green[a]=((a>>5)&63)*(65535/63); palette->blue[a]=(a&31)*(65535/31); */ palette->green[a]=(((a&63)*255)/64)*257; palette->red[a]= palette->blue[a]=(((a&31)*255)/32)*257; } break; default: for (a=0;ared[a]= palette->green[a]= palette->blue[a]=a*257; /* stuff it in both high and low byte */ } } } static void alloc_palette(struct palette *pal) { pal->red=mem_calloc(sizeof(unsigned short)*fb_palette_colors); pal->green=mem_calloc(sizeof(unsigned short)*fb_palette_colors); pal->blue=mem_calloc(sizeof(unsigned short)*fb_palette_colors); if (!pal->red||!pal->green||!pal->blue) { /*internal("Cannot create palette.\n")*/; } } static void free_palette(struct palette *pal) { mem_free(pal->red); mem_free(pal->green); mem_free(pal->blue); } static void set_palette(struct palette *pal) { struct fb_cmap cmap; int i; unsigned short *red=pal->red; unsigned short *green=pal->green; unsigned short *blue=pal->blue; __u16 *r, *g, *b, *t; int rs; r=mem_alloc(fb_palette_colors*sizeof(__u16)); g=mem_alloc(fb_palette_colors*sizeof(__u16)); b=mem_alloc(fb_palette_colors*sizeof(__u16)); t=mem_calloc(fb_palette_colors*sizeof(__u16)); if (!r||!g||!b||!t) { /*internal("Cannot allocate memory.\n")*/; } for (i = 0; i < fb_palette_colors; i++) { r[i] = red[i]; g[i] = green[i]; b[i] = blue[i]; /*fprintf(stderr, "%d %d %d\n", r[i], g[i], b[i]);*/ /*fprintf(stderr, "%5x: %5x\t%5x\t%5x\t%5x\n",i,r[i],g[i],b[i],t[i]);*/ } cmap.start = 0; cmap.len = fb_palette_colors; cmap.red = r; cmap.green = g; cmap.blue = b; cmap.transp = t; EINTRLOOP(rs, ioctl(fb_handler, FBIOPUTCMAP, &cmap)); if (rs==-1) { /*internal("Cannot set palette\n")*/; } mem_free(r);mem_free(g);mem_free(b);mem_free(t); } static void get_palette(struct palette *pal) { struct fb_cmap cmap; int i; __u16 *r, *g, *b, *t; int rs; r=mem_alloc(fb_palette_colors*sizeof(__u16)); g=mem_alloc(fb_palette_colors*sizeof(__u16)); b=mem_alloc(fb_palette_colors*sizeof(__u16)); t=mem_alloc(fb_palette_colors*sizeof(__u16)); if (!r||!g||!b||!t) { /*internal("Cannot allocate memory.\n")*/; } cmap.start = 0; cmap.len = fb_palette_colors; cmap.red = r; cmap.green = g; cmap.blue = b; cmap.transp = t; EINTRLOOP(rs, ioctl(fb_handler, FBIOGETCMAP, &cmap)); if (rs==-1) { /*internal("Cannot get palette\n")*/; } for (i = 0; i < fb_palette_colors; i++) { /*printf("%d %d %d\n",r[i],g[i],b[i]);*/ pal->red[i] = r[i]; pal->green[i] = g[i]; pal->blue[i] = b[i]; } mem_free(r);mem_free(g);mem_free(b);mem_free(t); } static void fb_clear_videoram(void) { memset(fb_mem, 0, (border_top + fb_ysize + border_bottom) * fb_linesize); } static void sleep_a_little_bit(void) { struct timeval tv = { 0, 20000 }; fd_set dummy; int rs; FD_ZERO(&dummy); EINTRLOOP(rs, select(0, &dummy, &dummy, &dummy, &tv)); } static void fb_switch_signal(void *data) { struct vt_stat st; struct rect r; int sign=(int)(unsigned long)data; int rs; switch(sign) { case SIG_REL: /* release */ fb_active=0; if (!in_gr_operation) EINTRLOOP(rs, ioctl(TTY,VT_RELDISP,1)); break; case SIG_ACQ: /* acq */ EINTRLOOP(rs, ioctl(TTY,VT_GETSTATE,&st)); if (rs) return; if (st.v_active != fb_console) return; fb_active=1; EINTRLOOP(rs, ioctl(TTY,VT_RELDISP,VT_ACKACQ)); /* * There is a race condition in Linux NVidia framebuffer driver * It still draws into a framebuffer here, so we have to sleep */ sleep_a_little_bit(); if (have_cmap && current_virtual_device) set_palette(&global_pal); r.x1=0; r.y1=0; r.x2=fb_xsize; r.y2=fb_ysize; if (border_left | border_top | border_right | border_bottom) fb_clear_videoram(); if (current_virtual_device) current_virtual_device->redraw_handler(current_virtual_device,&r); break; } } static unsigned char *fb_switch_init(void) { int rs; EINTRLOOP(rs, ioctl(TTY, VT_WAITACTIVE, fb_console)); install_signal_handler(SIG_REL, fb_switch_signal, (void*)SIG_REL, 1); install_signal_handler(SIG_ACQ, fb_switch_signal, (void*)SIG_ACQ, 0); EINTRLOOP(rs, ioctl(TTY,VT_GETMODE, &vt_omode)); if (rs == -1) { return stracpy("Could not get VT mode.\n"); } memcpy(&vt_mode, &vt_omode, sizeof(vt_mode)); vt_mode.mode = VT_PROCESS; vt_mode.waitv = 0; vt_mode.relsig = SIG_REL; vt_mode.acqsig = SIG_ACQ; EINTRLOOP(rs, ioctl(TTY,VT_SETMODE, &vt_mode)); if (rs == -1) { return stracpy("Could not set VT mode.\n"); } return NULL; } static void fb_switch_shutdown(void) { int rs; EINTRLOOP(rs, ioctl(TTY,VT_SETMODE, &vt_omode)); } static void fb_shutdown_palette(void) { if (have_cmap) { set_palette(&old_palette); free_palette(&old_palette); free_palette(&global_pal); } } static void fb_ctrl_c(struct itrm *i) { kbd_ctrl_c(); } #ifndef USE_GPM_DX static void fb_mouse_setsize(void) { struct vt_stat vs; int rs; EINTRLOOP(rs, ioctl(0, VT_GETSTATE, &vs)); if (!rs) { fd_set zero; struct timeval tv; FD_ZERO(&zero); memset(&tv, 0, sizeof tv); EINTRLOOP(rs, ioctl(0, VT_ACTIVATE, vs.v_active > 1 ? 1 : 2)); tv.tv_sec = 0; tv.tv_usec = 100000; EINTRLOOP(rs, select(0, &zero, &zero, &zero, &tv)); tv.tv_sec = 0; tv.tv_usec = 100000; EINTRLOOP(rs, select(0, &zero, &zero, &zero, &tv)); tv.tv_sec = 0; tv.tv_usec = 100000; EINTRLOOP(rs, select(0, &zero, &zero, &zero, &tv)); EINTRLOOP(rs, ioctl(0, VT_ACTIVATE, vs.v_active)); } } #endif static void unhandle_fb_mouse(void); static void fb_gpm_in(void *nic) { #ifndef USE_GPM_DX static int lx = -1, ly = -1; #endif struct event ev; Gpm_Event gev; again: if (Gpm_GetEvent(&gev) <= 0) { unhandle_fb_mouse(); return; } /*fprintf(stderr, "%d %d\n", gev.x, gev.y);*/ #ifndef USE_GPM_DX if (gev.x != lx || gev.y != ly) { mouse_x = (gev.x - 1) * fb_xsize / fb_txt_xsize + fb_xsize / fb_txt_xsize / 2 - 1; mouse_y = (gev.y - 1) * fb_ysize / fb_txt_ysize + fb_ysize / fb_txt_ysize / 2 - 1; lx = gev.x, ly = gev.y; } #else if (gev.dx || gev.dy) { if (!(gev.type & gpm_smooth)) { mouse_x += gev.dx * 8; mouse_y += gev.dy * 8; } else { mouse_x += gev.dx; mouse_y += gev.dy; } } #endif ev.ev = EV_MOUSE; if (mouse_x >= fb_xsize) mouse_x = fb_xsize - 1; if (mouse_y >= fb_ysize) mouse_y = fb_ysize - 1; if (mouse_x < 0) mouse_x = 0; if (mouse_y < 0) mouse_y = 0; if (!(gev.type & gpm_smooth) && (gev.dx || gev.dy)) { mouse_x = (mouse_x + 8) / 8 * 8 - 4; mouse_y = (mouse_y + 8) / 8 * 8 - 4; if (mouse_x >= fb_xsize) mouse_x = fb_xsize - 1; if (mouse_y >= fb_ysize) mouse_y = fb_ysize - 1; if (mouse_x < 0) mouse_x = 0; if (mouse_y < 0) mouse_y = 0; } ev.x = mouse_x; ev.y = mouse_y; if (gev.buttons & GPM_B_LEFT) ev.b = B_LEFT; else if (gev.buttons & GPM_B_MIDDLE) ev.b = B_MIDDLE; else if (gev.buttons & GPM_B_RIGHT) ev.b = B_RIGHT; else ev.b = 0; if (gev.type & GPM_DOWN) ev.b |= B_DOWN; else if (gev.type & GPM_UP) ev.b |= B_UP; else if (gev.type & GPM_DRAG) ev.b |= B_DRAG; else ev.b |= B_MOVE; #ifndef USE_GPM_DX if (fb_msetsize < 0) { } else if (fb_msetsize < 10) { fb_msetsize++; } else if ((ev.b & BM_ACT) == B_MOVE && !(ev.b & BM_BUTT)) { fb_mouse_setsize(); fb_msetsize = -1; } #endif if (((ev.b & BM_ACT) == B_MOVE && !(ev.b & BM_BUTT)) || (ev.b & BM_ACT) == B_DRAG) { if (can_read(fb_hgpm)) goto again; } if (!current_virtual_device) return; if (current_virtual_device->mouse_handler) current_virtual_device->mouse_handler(current_virtual_device, ev.x, ev.y, ev.b); redraw_mouse(); } static int handle_fb_mouse(void) { Gpm_Connect conn; #ifndef USE_GPM_DX int gpm_ver = 0; struct winsize ws; fb_old_ws_v = 0; #endif fb_hgpm = -1; #ifndef USE_GPM_DX Gpm_GetLibVersion(&gpm_ver); fb_msetsize = -1; if (gpm_ver >= 11900) { int rs; EINTRLOOP(rs,ioctl(1, TIOCGWINSZ, &ws)); if (rs != -1) { memcpy(&fb_old_ws, &ws, sizeof(struct winsize)); fb_old_ws_v = 1; ws.ws_row *= 2; EINTRLOOP(rs, ioctl(1, TIOCSWINSZ, &ws)); fb_msetsize = 0; memcpy(&fb_new_ws, &ws, sizeof ws); } } get_terminal_size(1, &fb_txt_xsize, &fb_txt_ysize); #endif conn.eventMask = ~0; conn.defaultMask = gpm_smooth; conn.minMod = 0; conn.maxMod = -1; if ((fb_hgpm = Gpm_Open(&conn, 0)) < 0) { unhandle_fb_mouse(); return -1; } set_handlers(fb_hgpm, fb_gpm_in, NULL, NULL, NULL); #ifdef SIGTSTP install_signal_handler(SIGTSTP, (void (*)(void *))sig_tstp, NULL, 0); #endif #ifdef SIGCONT install_signal_handler(SIGCONT, (void (*)(void *))sig_cont, NULL, 0); #endif #ifdef SIGTTIN install_signal_handler(SIGTTIN, (void (*)(void *))sig_tstp, NULL, 0); #endif return 0; } static void unhandle_fb_mouse(void) { if (fb_hgpm >= 0) set_handlers(fb_hgpm, NULL, NULL, NULL, NULL); #ifndef USE_GPM_DX fb_hgpm = -1; if (fb_old_ws_v) { int rs; EINTRLOOP(rs, ioctl(1, TIOCSWINSZ, &fb_old_ws)); fb_old_ws_v = 0; } #endif Gpm_Close(); #ifdef SIGTSTP install_signal_handler(SIGTSTP, (void (*)(void *))sig_tstp, NULL, 0); #endif #ifdef SIGCONT install_signal_handler(SIGCONT, (void (*)(void *))sig_cont, NULL, 0); #endif #ifdef SIGTTIN install_signal_handler(SIGTTIN, (void (*)(void *))sig_tstp, NULL, 0); #endif } #define seq_hide_cursor "\033[10000B\033[10000C\033[?25l" #define seq_show_cursor "\033[10000D\033[?25h" static void fb_print(unsigned char *str) { int wr; EINTRLOOP(wr, write(TTY, str, strlen(str))); if (wr <= 0) EINTRLOOP(wr, write(1, str, strlen(str))); } static void fb_hide_cursor(void) { fb_print(seq_hide_cursor); } static void fb_show_cursor(void) { fb_print(seq_show_cursor); } static unsigned char *fb_init_driver(unsigned char *param, unsigned char *ignore) { unsigned char *e; struct stat st; int rs; TTY = 0; EINTRLOOP(rs, ioctl(TTY,VT_GETMODE, &vt_omode)); if (rs == -1) { TTY = 1; EINTRLOOP(rs, ioctl(TTY,VT_GETMODE, &vt_omode)); if (rs == -1) { TTY = 0; } } kbd_set_raw = 1; fb_old_vd = NULL; fb_driver_param=NULL; if(param != NULL) fb_driver_param=stracpy(param); border_left = border_right = border_top = border_bottom = 0; if (!param) param=""; if (*param) { if (*param < '0' || *param > '9') { bad_p: if(fb_driver_param) { mem_free(fb_driver_param); fb_driver_param=NULL; } return stracpy("-mode syntax is left_border[,top_border[,right_border[,bottom_border]]]\n"); } border_left = strtoul(param, (char **)(void *)¶m, 10); if ((unsigned long)border_left > MAXINT / 10) goto bad_p; if (*param == ',') param++; } else { border_left = 0; } if (*param) { if (*param < '0' || *param > '9') goto bad_p; border_top = strtoul(param, (char **)(void *)¶m, 10); if ((unsigned long)border_top > MAXINT / 10) goto bad_p; if (*param == ',') param++; } else { border_top = border_left; } if (*param) { if (*param < '0' || *param > '9') goto bad_p; border_right = strtoul(param, (char **)(void *)¶m, 10); if ((unsigned long)border_right > MAXINT / 10) goto bad_p; if (*param == ',') param++; } else { border_right = border_left; } if (*param) { if (*param < '0' || *param > '9') goto bad_p; border_bottom = strtoul(param, (char **)(void *)¶m, 10); if ((unsigned long)border_bottom > MAXINT / 10) goto bad_p; if (*param == ',') param++; } else { border_bottom = border_top; } if (*param) goto bad_p; EINTRLOOP(rs, fstat(TTY, &st)); if (rs) { if(fb_driver_param) { mem_free(fb_driver_param); fb_driver_param=NULL; } return stracpy("Cannon stat stdin.\n"); } fb_console = st.st_rdev & 0xff; fb_hide_cursor(); if ((e = fb_switch_init())) { if(fb_driver_param) { mem_free(fb_driver_param); fb_driver_param=NULL; } fb_show_cursor(); return e; } EINTRLOOP(fb_handler, open("/dev/fb0", O_RDWR)); if (fb_handler==-1) { fb_switch_shutdown(); if(fb_driver_param) { mem_free(fb_driver_param); fb_driver_param=NULL; } fb_show_cursor(); return stracpy("Cannot open /dev/fb0.\n"); } EINTRLOOP(rs, ioctl (fb_handler, FBIOGET_VSCREENINFO, &vi)); if (rs==-1) { EINTRLOOP(rs, close(fb_handler)); fb_switch_shutdown(); if(fb_driver_param) { mem_free(fb_driver_param); fb_driver_param=NULL; } fb_show_cursor(); return stracpy("Cannot get FB VSCREENINFO.\n"); } oldmode=vi; EINTRLOOP(rs, ioctl (fb_handler, FBIOGET_FSCREENINFO, &fi)); if (rs==-1) { EINTRLOOP(rs, close(fb_handler)); fb_switch_shutdown(); if(fb_driver_param) { mem_free(fb_driver_param); fb_driver_param=NULL; } fb_show_cursor(); return stracpy("Cannot get FB FSCREENINFO.\n"); } fb_xsize=vi.xres; fb_ysize=vi.yres; fb_bits_pp=vi.bits_per_pixel; if (fb_bits_pp == 16 && vi.green.length == 5) fb_bits_pp = 15; if (fb_xsize <= border_left + border_right) border_left = border_right = 0; fb_xsize -= border_left + border_right; if (fb_ysize <= border_top + border_bottom) border_top = border_bottom = 0; fb_ysize -= border_top + border_bottom; fb_driver.x=fb_xsize; fb_driver.y=fb_ysize; switch(fb_bits_pp) { case 4: fb_pixelsize=1; fb_palette_colors=16; break; case 8: fb_pixelsize=1; fb_palette_colors=256; break; case 15: case 16: fb_pixelsize=2; fb_palette_colors=64; break; case 24: fb_palette_colors=256; fb_pixelsize=3; break; case 32: fb_palette_colors=256; fb_pixelsize=4; fb_bits_pp=24; break; default: EINTRLOOP(rs, close(fb_handler)); fb_switch_shutdown(); if(fb_driver_param) { mem_free(fb_driver_param); fb_driver_param=NULL; } fb_show_cursor(); return stracpy("Unknown bit depth"); } fb_colors=1<>1; background_y=mouse_y=fb_ysize>>1; mouse_black=fb_driver.get_color(0); mouse_white=fb_driver.get_color(0xffffff); mouse_graphics_device=fb_driver.init_device(); virtual_devices[0] = NULL; global_mouse_hidden=1; if (handle_fb_mouse()) { fb_driver.shutdown_device(mouse_graphics_device); mem_free(mouse_buffer); mem_free(background_buffer); mem_free(new_background_buffer); fb_shutdown_palette(); svgalib_free_trm(fb_kbd); shutdown_virtual_devices(); EINTRLOOP(rs, close(fb_handler)); fb_switch_shutdown(); if(fb_driver_param) { mem_free(fb_driver_param); fb_driver_param=NULL; } fb_show_cursor(); return stracpy("Cannot open GPM mouse.\n"); } /* hide cursor */ if (border_left | border_top | border_right | border_bottom) fb_clear_videoram(); show_mouse(); return NULL; } static void fb_shutdown_driver(void) { int rs; mem_free(mouse_buffer); mem_free(background_buffer); mem_free(new_background_buffer); fb_driver.shutdown_device(mouse_graphics_device); unhandle_fb_mouse(); in_gr_operation++; if (fb_active) { fb_clear_videoram(); EINTRLOOP(rs, ioctl (fb_handler, FBIOPUT_VSCREENINFO, &oldmode)); } END_GR; fb_shutdown_palette(); install_signal_handler(SIGINT, NULL, NULL, 0); EINTRLOOP(rs, close(fb_handler)); EINTRLOOP(rs, munmap(fb_mem,fb_mapped_size)); shutdown_virtual_devices(); fb_switch_shutdown(); svgalib_free_trm(fb_kbd); if(fb_driver_param) mem_free(fb_driver_param); /* show cursor */ fb_show_cursor(); } static unsigned char *fb_get_driver_param(void) { return fb_driver_param; } /* Return value: 0 alloced on heap * 1 alloced in vidram * 2 alloced in X server shm */ static int fb_get_empty_bitmap(struct bitmap *dest) { if (dest->x && (unsigned)dest->x * (unsigned)dest->y / (unsigned)dest->x != (unsigned)dest->y) overalloc(); if ((unsigned)dest->x * (unsigned)dest->y > (unsigned)MAXINT / fb_pixelsize) overalloc(); dest->data=mem_alloc(dest->x*dest->y*fb_pixelsize); dest->skip=dest->x*fb_pixelsize; dest->flags=0; return 0; } /* Return value: 0 alloced on heap * 1 alloced in vidram * 2 alloced in X server shm */ /* static int fb_get_filled_bitmap(struct bitmap *dest, long color) { int n; if (dest->x && (unsigned)dest->x * (unsigned)dest->y / (unsigned)dest->x != (unsigned)dest->y) overalloc(); if ((unsigned)dest->x * (unsigned)dest->y > MAXINT / fb_pixelsize) overalloc(); n=dest->x*dest->y*fb_pixelsize; dest->data=mem_alloc(n); pixel_set(dest->data,n,&color); dest->skip=dest->x*fb_pixelsize; dest->flags=0; return 0; } */ static void fb_register_bitmap(struct bitmap *bmp) { } static void fb_unregister_bitmap(struct bitmap *bmp) { mem_free(bmp->data); } static void *fb_prepare_strip(struct bitmap *bmp, int top, int lines) { return ((char *)bmp->data)+bmp->skip*top; } static void fb_commit_strip(struct bitmap *bmp, int top, int lines) { return; } static void fb_draw_bitmap(struct graphics_device *dev,struct bitmap* hndl, int x, int y) { unsigned char *scr_start; int rs; CLIP_PREFACE scr_start=fb_vmem+y*fb_linesize+x*fb_pixelsize; for(;ys;ys--){ memcpy(scr_start,data,xs*fb_pixelsize); data+=hndl->skip; scr_start+=fb_linesize; } END_MOUSE END_GR } #if 0 static void fb_draw_bitmaps(struct graphics_device *dev, struct bitmap **hndls, int n, int x, int y) { TEST_INACTIVITY if (x>=fb_xsize||y>fb_ysize) return; while(x+(*hndls)->x<=0&&n){ x+=(*hndls)->x; n--; hndls++; } while(n&&x<=fb_xsize){ fb_draw_bitmap(dev, *hndls, x, y); x+=(*hndls)->x; n--; hndls++; } } #endif static void fb_fill_area(struct graphics_device *dev, int left, int top, int right, int bottom, long color) { unsigned char *dest; int y; int rs; FILL_CLIP_PREFACE dest=fb_vmem+top*fb_linesize+left*fb_pixelsize; for (y=bottom-top;y;y--){ pixel_set(dest,(right-left)*fb_pixelsize,&color); dest+=fb_linesize; } END_MOUSE END_GR } static void fb_draw_hline(struct graphics_device *dev, int left, int y, int right, long color) { unsigned char *dest; int rs; HLINE_CLIP_PREFACE dest=fb_vmem+y*fb_linesize+left*fb_pixelsize; pixel_set(dest,(right-left)*fb_pixelsize,&color); END_MOUSE END_GR } static void fb_draw_vline(struct graphics_device *dev, int x, int top, int bottom, long color) { unsigned char *dest; int y; int rs; VLINE_CLIP_PREFACE dest=fb_vmem+top*fb_linesize+x*fb_pixelsize; for (y=(bottom-top);y;y--){ memcpy(dest,&color,fb_pixelsize); dest+=fb_linesize; } END_MOUSE END_GR } static int fb_hscroll(struct graphics_device *dev, struct rect_set **ignore, int sc) { unsigned char *dest, *src; int y; int len; int rs; HSCROLL_CLIP_PREFACE if (sc>0){ len=(dev->clip.x2-dev->clip.x1-sc)*fb_pixelsize; src=fb_vmem+fb_linesize*dev->clip.y1+dev->clip.x1*fb_pixelsize; dest=src+sc*fb_pixelsize; for (y=dev->clip.y2-dev->clip.y1;y;y--){ memmove(dest,src,len); dest+=fb_linesize; src+=fb_linesize; } }else{ len=(dev->clip.x2-dev->clip.x1+sc)*fb_pixelsize; dest=fb_vmem+fb_linesize*dev->clip.y1+dev->clip.x1*fb_pixelsize; src=dest-sc*fb_pixelsize; for (y=dev->clip.y2-dev->clip.y1;y;y--){ memmove(dest,src,len); dest+=fb_linesize; src+=fb_linesize; } } END_MOUSE END_GR return 1; } static int fb_vscroll(struct graphics_device *dev, struct rect_set **ignore, int sc) { unsigned char *dest, *src; int y; int len; int rs; VSCROLL_CLIP_PREFACE len=(dev->clip.x2-dev->clip.x1)*fb_pixelsize; if (sc>0){ /* Down */ dest=fb_vmem+(dev->clip.y2-1)*fb_linesize+dev->clip.x1*fb_pixelsize; src=dest-fb_linesize*sc; for (y=dev->clip.y2-dev->clip.y1-sc;y;y--){ memcpy(dest,src,len); dest-=fb_linesize; src-=fb_linesize; } }else{ /* Up */ dest=fb_vmem+dev->clip.y1*fb_linesize+dev->clip.x1*fb_pixelsize; src=dest-fb_linesize*sc; for (y=dev->clip.y2-dev->clip.y1+sc;y;y--){ memcpy(dest,src,len); dest+=fb_linesize; src+=fb_linesize; } } END_MOUSE END_GR return 1; } static void fb_set_clip_area(struct graphics_device *dev, struct rect *r) { memcpy(&dev->clip, r, sizeof(struct rect)); if (dev->clip.x1>=dev->clip.x2||dev->clip.y2<=dev->clip.y1||dev->clip.y2<=0||dev->clip.x2<=0||dev->clip.x1>=fb_xsize ||dev->clip.y1>=fb_ysize){ /* Empty region */ dev->clip.x1=dev->clip.x2=dev->clip.y1=dev->clip.y2=0; }else{ if (dev->clip.x1<0) dev->clip.x1=0; if (dev->clip.x2>fb_xsize) dev->clip.x2=fb_xsize; if (dev->clip.y1<0) dev->clip.y1=0; if (dev->clip.y2>fb_ysize) dev->clip.y2=fb_ysize; } } static int fb_block(struct graphics_device *dev) { if (fb_old_vd) return 1; fb_block_dev = dev; unhandle_fb_mouse(); fb_old_vd = current_virtual_device; current_virtual_device=NULL; svgalib_block_itrm(fb_kbd); if (have_cmap) set_palette(&old_palette); fb_switch_shutdown(); fb_show_cursor(); fb_print("\n"); return 0; } static int fb_unblock(struct graphics_device *dev) { int rs; if (current_virtual_device) { return 0; } if (dev != fb_block_dev) return -2; if (svgalib_unblock_itrm(fb_kbd)) return -1; fb_switch_init(); fb_hide_cursor(); vi.xoffset=0; vi.yoffset=0; EINTRLOOP(rs, ioctl(fb_handler, FBIOPAN_DISPLAY, &vi)); current_virtual_device = fb_old_vd; fb_old_vd = NULL; if (have_cmap) set_palette(&global_pal); handle_fb_mouse(); if (border_left | border_top | border_right | border_bottom) fb_clear_videoram(); if (current_virtual_device) current_virtual_device->redraw_handler(current_virtual_device ,¤t_virtual_device->size); return 0; } struct graphics_driver fb_driver={ "fb", fb_init_driver, init_virtual_device, shutdown_virtual_device, fb_shutdown_driver, fb_get_driver_param, fb_get_empty_bitmap, /*fb_get_filled_bitmap,*/ fb_register_bitmap, fb_prepare_strip, fb_commit_strip, fb_unregister_bitmap, fb_draw_bitmap, /*fb_draw_bitmaps,*/ NULL, /* fb_get_color */ fb_fill_area, fb_draw_hline, fb_draw_vline, fb_hscroll, fb_vscroll, fb_set_clip_area, fb_block, fb_unblock, NULL, /* set_title */ NULL, /* exec */ NULL, /* set_clipboard_text */ NULL, /* get_clipboard_text */ 0, /* depth (filled in fb_init_driver function) */ 0, 0, /* size (in X is empty) */ GD_DONT_USE_SCROLL|GD_NEED_CODEPAGE, /* flags */ 0, /* codepage */ NULL, /* shell */ }; #endif /* GRDRV_FB */