/* framebuf.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_FB_ACCEL*/ /*#define USE_FB_ACCEL_FILLRECT*/ /* #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 #if defined(USE_FB_ACCEL) #if !defined(FBIO_ACCEL_SUPPORT) #define FBIO_ACCEL_SUPPORT 0x4630 #define FBIO_ACCEL_SYNC 0x4631 #define FBIO_ACCEL_FILLRECT 0x4632 #define FBIO_ACCEL_FILLRECT_SYNC 0x4633 #define FBIO_ACCEL_COPYAREA 0x4634 #define FBIO_ACCEL_COPYAREA_SYNC 0x4635 #define FB_ACCEL_FILLRECT_SUPPORTED 0x00000001 #define FB_ACCEL_FILLRECT_ACCELERATED 0x00000002 #define FB_ACCEL_COPYAREA_SUPPORTED 0x00000004 #define FB_ACCEL_COPYAREA_ACCELERATED 0x00000008 #define FB_ACCEL_SYNC_NEEDED 0x40000000 /* we don't know if fb_copyarea and fb_fillrect are or aren't defined */ #define fb_copyarea fb_redefined_copyarea #define fb_fillrect fb_redefined_fillrect struct fb_copyarea { __u32 dx; __u32 dy; __u32 width; __u32 height; __u32 sx; __u32 sy; }; struct fb_fillrect { __u32 dx; __u32 dy; __u32 width; __u32 height; __u32 color; __u32 rop; }; #endif #ifndef ROP_COPY #define ROP_COPY 0 #endif #ifndef ROP_XOR #define ROP_XOR 1 #endif #endif static int TTY = 0; static int fb_hgpm = -2; static int fb_hmice = -1; static unsigned char ps2_mouse_mode; #define fb_have_mouse (fb_hgpm >= 0 || fb_hmice >= 0) 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_handle = -1; 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 int 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 *bmp, int x, int y); static unsigned char *fb_driver_param; extern struct graphics_driver fb_driver; static int have_cmap; static volatile int fb_active; 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; #ifdef USE_FB_ACCEL static int accel_flags; static int need_accel_sync; static inline void accel_sync(void) { int rs; if (need_accel_sync) { EINTRLOOP(rs, ioctl(fb_handle, FBIO_ACCEL_SYNC)); need_accel_sync = 0; } } #else #define accel_sync() do { } while (0) #endif /* 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; static int last_mouse_buttons; #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 \ memory_barrier(&fb_vmem); \ in_gr_operation--;\ if (!fb_active && !in_gr_operation) {\ accel_sync();\ EINTRLOOP(rs, ioctl(TTY,VT_RELDISP,1));\ } #define INC_IN_GR \ in_gr_operation++; \ memory_barrier(&fb_vmem); #define START_GR \ INC_IN_GR \ if (!fb_active) { END_GR return; } #define START_GR_0 \ INC_IN_GR \ 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, ys;\ unsigned char *data=bmp->data;\ \ if (!data)\ return;\ \ TEST_INACTIVITY\ CLIP_DRAW_BITMAP\ xs = bmp->x;\ ys = bmp->y;\ 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+=bmp->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 x1, x2, y1, y2 will be sane according to the * clipping rectangle set up by set_clip_area. If empty region * results, return from current function occurs. */ #define FILL_CLIP_PREFACE \ int mouse_hidden;\ TEST_INACTIVITY\ CLIP_FILL_AREA\ START_GR\ TEST_MOUSE(x1,x2,y1,y2) #define HLINE_CLIP_PREFACE \ int mouse_hidden;\ TEST_INACTIVITY\ CLIP_DRAW_HLINE\ START_GR\ TEST_MOUSE(x1,x2,y,y+1) #define VLINE_CLIP_PREFACE \ int mouse_hidden;\ TEST_INACTIVITY\ CLIP_DRAW_VLINE\ START_GR\ TEST_MOUSE(x,x+1,y1,y2) #define SCROLL_CLIP_PREFACE \ int mouse_hidden;\ TEST_INACTIVITY_0\ 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 links_event ev; if (!fb_have_mouse) return; 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 (((last_mouse_buttons & BM_ACT) == B_DOWN || (last_mouse_buttons & BM_ACT) == B_DRAG) && !BM_IS_WHEEL(last_mouse_buttons)) { ev.b = (last_mouse_buttons & BM_BUTT) | B_DRAG; } if (!current_virtual_device) return; if (current_virtual_device->mouse_handler) current_virtual_device->mouse_handler(current_virtual_device, ev.x, ev.y, (int)ev.b); redraw_mouse(); } static void fb_key_in(struct itrm *p, unsigned char *ev_, int size) { int rs; struct links_event *ev = (struct links_event *)(void *)ev_; if (size != sizeof(struct links_event)) return; if (ev->ev == EV_ABORT) terminate_loop = 1; if (ev->ev != EV_KBD) return; if (ev->y & KBD_PASTING) goto skip; if ((ev->y & (KBD_CTRL | KBD_ALT)) == KBD_ALT && ev->x >= '0' && ev->x <= '9') { switch_virtual_device((ev->x - '1' + 10) % 10); goto flush_ret; } 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 { skip: if (g_kbd_codepage(&fb_driver) != utf8_table && ev->x >= 128 && ev->x <= 255) if ((ev->x = cp2u(ev->x, g_kbd_codepage(&fb_driver))) == -1) return; if (current_virtual_device && current_virtual_device->keyboard_handler) current_virtual_device->keyboard_handler(current_virtual_device, ev->x, ev->y); } flush_ret: EINTRLOOP(rs, fsync(fb_handle)); } #define mouse_getscansegment(buf,x,y,w) memcpy_to_fb(buf, fb_vmem + y * fb_linesize + x * fb_pixelsize, w, 1) #define mouse_drawscansegment(ptr,x,y,w) memcpy_to_fb(fb_vmem + y * fb_linesize + x * fb_pixelsize, ptr, w, 0) #define do_with_mouse_device(cmd) \ do { \ struct graphics_device *current_virtual_device_backup; \ \ current_virtual_device_backup = current_virtual_device; \ current_virtual_device = mouse_graphics_device; \ cmd; \ current_virtual_device = current_virtual_device_backup; \ } while (0) /* 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; do_with_mouse_device(fb_draw_bitmap(mouse_graphics_device, &bmp, background_x, background_y)); } /* 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; accel_sync(); 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; unsigned reg0, reg1; unsigned char *mouse_ptr=mouse_buffer; const 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;) { unsigned mask=1U<<(--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; if (!fb_have_mouse) return; bmp.x=arrow_width; bmp.y=arrow_height; bmp.skip=arrow_width*fb_pixelsize; bmp.data=mouse_buffer; do_with_mouse_device(fb_draw_bitmap(mouse_graphics_device, &bmp, mouse_x, mouse_y)); 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; accel_sync(); /* 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 = 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); } static void free_palette(struct palette *pal) { if (pal->red) mem_free(pal->red); if (pal->green) mem_free(pal->green); if (pal->blue) mem_free(pal->blue); } /* This is an empiric magic that ensures * Good white purity * Correct rounding and dithering prediction * And this is the cabbala: * 063 021 063 * 009 009 021 * 255 085 255 * 036 036 084 */ static void generate_palette(struct palette *pal) { int a; alloc_palette(pal); switch (fb_colors) { case 16: case 256: for (a = 0; a < fb_palette_colors; a++) { unsigned rgb[3]; int cols = fb_colors; if (cols == 256 && (fb_driver.depth & 0x300) == 0x300) cols = 216; q_palette(cols, a, 65535, rgb); pal->red[a] = rgb[0]; pal->green[a] = rgb[1]; pal->blue[a] = rgb[2]; } break; case 32768: for (a = 0; a < fb_palette_colors; a++) { /* pal->red[a] = ((a >> 10) & 31) * (65535 / 31); pal->green[a] = ((a >> 5) & 31) * (65535 / 31); pal->blue[a] = (a & 31) * (65535 / 31); */ pal->red[a] = pal->green[a] = pal->blue[a] = (((a & 31) * 255) / 31) * 257; } break; case 65536: for (a = 0; a < fb_palette_colors; a++){ /* pal->red[a] = ((a >> 11) & 31) * (65535 / 31); pal->green[a] = ((a >> 5) & 63) * (65535 / 63); pal->blue[a] = (a & 31) * (65535 / 31); */ pal->green[a] = (((a & 63) * 255) / 64) * 257; pal->red[a] = pal->blue[a] = (((a & 31) * 255) / 32) * 257; } break; default: for (a = 0; a < fb_palette_colors; a++) { pal->red[a] = pal->green[a] = pal->blue[a] = a * 257; /* stuff it in both high and low byte */ } } } 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; if (!red || !green || !blue) return; 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)); 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_handle, FBIOPUTCMAP, &cmap)); if (rs == -1) { /*error("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; pal->red = pal->green = pal->blue = NULL; 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)); cmap.start = 0; cmap.len = fb_palette_colors; cmap.red = r; cmap.green = g; cmap.blue = b; cmap.transp = t; EINTRLOOP(rs, ioctl(fb_handle, FBIOGETCMAP, &cmap)); if (rs == -1) { /*error("Cannot get palette\n")*/; goto skip; } alloc_palette(pal); 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]; } skip: mem_free(r); mem_free(g); mem_free(b); mem_free(t); } static void fb_clear_videoram(void) { int rs; START_GR #ifdef USE_FB_ACCEL_FILLRECT if (accel_flags & FB_ACCEL_FILLRECT_ACCELERATED) { struct fb_fillrect f; f.dx = 0; f.dy = 0; f.width = fb_xsize + border_left + border_right; f.height = fb_ysize + border_top + border_bottom; f.color = 0; f.rop = ROP_COPY; EINTRLOOP(rs, ioctl(fb_handle, FBIO_ACCEL_FILLRECT_SYNC, &f)); if (rs < 0) { error("fb_clear_videoram accel failed\n"); goto no_accel; } } else no_accel: #endif { accel_sync(); memset(fb_mem, 0, (border_top + fb_ysize + border_bottom) * fb_linesize); /*{ int size = (border_top + fb_ysize + border_bottom) * fb_linesize; int pos; for (pos = 0; pos < size; pos++) { fb_mem[pos] = (unsigned char)pos; } }*/ } END_GR } static void fb_switch_signal_rel(void *data) { int rs; fb_active = 0; if (!in_gr_operation) { accel_sync(); EINTRLOOP(rs, ioctl(TTY, VT_RELDISP, 1)); } } static void fb_switch_signal_acq(void *data) { struct vt_stat st; int rs; EINTRLOOP(rs, ioctl(TTY, VT_GETSTATE, &st)); if (rs) return; if (st.v_active != fb_console) return; INC_IN_GR 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 */ portable_sleep(10); if (have_cmap && current_virtual_device) set_palette(&global_pal); END_GR 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); EINTRLOOP(rs, fsync(fb_handle)); } static unsigned char *fb_switch_init(void) { int rs; INC_IN_GR /* If we use threads, the signal handler may execute on a different thread. framebuf.c doesn't handle different-thread signals, so we must switch to synchronous signal handling when using threads */ install_signal_handler(SIG_REL, fb_switch_signal_rel, NULL, #ifndef EXEC_IN_THREADS 1 #else 0 #endif ); install_signal_handler(SIG_ACQ, fb_switch_signal_acq, NULL, 0); EINTRLOOP(rs, ioctl(TTY, VT_GETMODE, &vt_omode)); if (rs == -1) { in_gr_operation--; return stracpy(cast_uchar "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) { in_gr_operation--; return stracpy(cast_uchar "Could not set VT mode.\n"); } EINTRLOOP(rs, ioctl(TTY, VT_WAITACTIVE, fb_console)); fb_active = 1; return NULL; } static void fb_switch_shutdown(void) { int rs; if (in_gr_operation <= 0) internal_error("fb_switch_shutdown: in_gr_operation %d", in_gr_operation); if (!fb_active && in_gr_operation == 1) { EINTRLOOP(rs, ioctl(TTY, VT_RELDISP, 1)); } EINTRLOOP(rs, ioctl(TTY, VT_SETMODE, &vt_omode)); install_signal_handler(SIG_REL, (void (*)(void *))NULL, (void*)SIG_REL, 1); install_signal_handler(SIG_ACQ, (void (*)(void *))NULL, (void*)SIG_ACQ, 0); in_gr_operation--; } static void fb_shutdown_palette(void) { if (have_cmap) { if (fb_active) set_palette(&old_palette); free_palette(&old_palette); free_palette(&global_pal); } } static void fb_ctrl_c(void *i_) { kbd_ctrl_c(); } static void unhandle_fb_mouse(void); static void fb_gpm_in(void *nic) { struct links_event ev; int g; Gpm_Event gev; int rs; again: set_handlers(fb_hgpm, (void (*)(void *))NULL, (void (*)(void *))NULL, NULL); save_gpm_signals(); g = Gpm_GetEvent(&gev); restore_gpm_signals(); if (g <= 0) { fb_hgpm = -1; hide_mouse(); unhandle_fb_mouse(); return; } set_handlers(fb_hgpm, fb_gpm_in, (void (*)(void *))NULL, NULL); if (fb_hmice != -1) return; /*fprintf(stderr, "%x %x %d %d %d %d\n", gev.type, gev.buttons, gev.dx, gev.dy, gev.wdx, gev.wdy);*/ if (gev.dx || gev.dy) { if (!((int)gev.type & gpm_smooth)) { mouse_x += gev.dx * 8; mouse_y += gev.dy * 8; } #ifdef GPM_HAVE_SMOOTH 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 (!((int)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; #ifdef GPM_B_FOURTH else if (gev.buttons & GPM_B_FOURTH) ev.b = B_FOURTH; #endif #ifdef GPM_B_UP else if (gev.buttons & GPM_B_UP) ev.b = B_FIFTH; #endif #ifdef GPM_B_DOWN else if (gev.buttons & GPM_B_DOWN) ev.b = B_SIXTH; #endif else ev.b = 0; if ((int)gev.type & GPM_DOWN) ev.b |= B_DOWN; else if ((int)gev.type & GPM_UP) ev.b |= B_UP; else if ((int)gev.type & GPM_DRAG) ev.b |= B_DRAG; else ev.b |= B_MOVE; #ifdef HAVE_WDX_WDY if ((ev.b & BM_ACT) == B_DRAG || (ev.b & BM_ACT) == B_MOVE) { if (gev.wdy < 0) { ev.b &= ~BM_BUTT; ev.b |= B_WHEELDOWN; } else if (gev.wdy > 0) { ev.b &= ~BM_BUTT; ev.b |= B_WHEELUP; } #if 0 /* it doesn't work anyway - the exps2 protocol doesn't support it and evdev support in gpm is buggy */ else if (gev.wdx < 0) { ev.b &= ~BM_BUTT; ev.b |= B_WHEELRIGHT; } else if (gev.wdx > 0) { ev.b &= ~BM_BUTT; ev.b |= B_WHEELLEFT; #endif } #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; } last_mouse_buttons = (int)ev.b; if (!current_virtual_device) return; if (current_virtual_device->mouse_handler) current_virtual_device->mouse_handler(current_virtual_device, ev.x, ev.y, (int)ev.b); redraw_mouse(); EINTRLOOP(rs, fsync(fb_handle)); } static void fb_ps2_in(void *nic) { unsigned char buffer[4]; int rs, dx, dy, dw, button; int size = ps2_mouse_mode ? 4 : 3; if (hard_read(fb_hmice, buffer, size) != size) { unhandle_fb_mouse(); return; } dx = buffer[1]; if (buffer[0] & 0x10) dx |= -0x100; dy = buffer[2]; if (buffer[0] & 0x20) dy |= -0x100; dy = -dy; button = -1; if (buffer[0] & 1) button = B_LEFT; else if (buffer[0] & 2) button = B_RIGHT; else if (buffer[0] & 4) button = B_MIDDLE; else if (ps2_mouse_mode == 4) { if (buffer[3] & 0x10) button = B_FOURTH; else if (buffer[3] & 0x20) button = B_FIFTH; } dw = 0; if (ps2_mouse_mode == 3) { dw = buffer[3]; if (dw & 0x80) dw |= -0x100; } if (ps2_mouse_mode == 4) { dw = buffer[3] & 0x0f; if (dw & 0x08) dw |= -0x10; } if (fb_active) { mouse_x += dx; mouse_y += dy; } 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 ((last_mouse_buttons & BM_ACT) == B_UP || (last_mouse_buttons & BM_ACT) == B_MOVE) { if (button == -1) button = B_MOVE; else button |= B_DOWN; } else { if (button == -1) button = (last_mouse_buttons & BM_BUTT) | B_UP; else button |= B_DRAG; } if ((button & BM_ACT) == B_DRAG || (button & BM_ACT) == B_MOVE) { if (dw > 0) { button &= ~BM_BUTT; button |= B_WHEELDOWN; } else if (dw < 0) { button &= ~BM_BUTT; button |= B_WHEELUP; } } /*fprintf(stderr, "%02x %d %d %d %d\n", buffer[0], mouse_x, mouse_y, button, last_mouse_buttons);*/ last_mouse_buttons = button; if (!current_virtual_device) return; if (!fb_active) button = B_MOVE; if (current_virtual_device->mouse_handler) current_virtual_device->mouse_handler(current_virtual_device, mouse_x, mouse_y, button); redraw_mouse(); EINTRLOOP(rs, fsync(fb_handle)); } static int handle_fb_mouse(void) { Gpm_Connect conn; fb_hmice = c_open(cast_uchar "/dev/input/mice", O_RDWR | O_NOCTTY); if (fb_hmice != -1) { unsigned char c; static const unsigned char imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80, 0xf2 }; static const unsigned char imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80, 0xf2 }; if (hard_write(fb_hmice, imps_seq, sizeof(imps_seq)) != sizeof(imps_seq)) goto gpm_fallback; do if (hard_read(fb_hmice, &c, 1) != 1) goto gpm_fallback; while (c != 0xfa); if (hard_read(fb_hmice, &ps2_mouse_mode, 1) != 1) goto gpm_fallback; if (!ps2_mouse_mode) goto open_gpm; if (ps2_mouse_mode != 3) goto gpm_fallback; if (hard_write(fb_hmice, imex_seq, sizeof(imex_seq)) != sizeof(imex_seq)) goto gpm_fallback; do if (hard_read(fb_hmice, &c, 1) != 1) goto gpm_fallback; while (c != 0xfa); if (hard_read(fb_hmice, &ps2_mouse_mode, 1) != 1) goto gpm_fallback; if (ps2_mouse_mode == 3) goto open_gpm; if (ps2_mouse_mode != 4) goto gpm_fallback; } open_gpm: if (0) { gpm_fallback: close_socket(&fb_hmice); } fb_hgpm = -2; conn.eventMask = (unsigned short)~0U; conn.defaultMask = gpm_smooth; conn.minMod = 0; conn.maxMod = (unsigned short)~0U; save_gpm_signals(); fb_hgpm = Gpm_Open(&conn, 0); restore_gpm_signals(); if (fb_hgpm < 0) { fb_hgpm = -2; if (fb_hmice >= 0) goto done; unhandle_fb_mouse(); return -1; } done: if (fb_hgpm >= 0) set_handlers(fb_hgpm, fb_gpm_in, (void (*)(void *))NULL, NULL); if (fb_hmice != -1) set_handlers(fb_hmice, fb_ps2_in, (void (*)(void *))NULL, NULL); #ifdef SIGTSTP install_signal_handler(SIGTSTP, sig_tstp, NULL, 0); #endif #ifdef SIGCONT install_signal_handler(SIGCONT, sig_cont, NULL, 0); #endif #ifdef SIGTTIN install_signal_handler(SIGTTIN, sig_tstp, NULL, 0); #endif return 0; } static void unhandle_fb_mouse(void) { if (fb_hgpm >= 0) set_handlers(fb_hgpm, (void (*)(void *))NULL, (void (*)(void *))NULL, NULL); if (fb_hgpm >= -1) { save_gpm_signals(); Gpm_Close(); restore_gpm_signals(); } fb_hgpm = -2; close_socket(&fb_hmice); #ifdef SIGTSTP install_signal_handler(SIGTSTP, sig_tstp, NULL, 0); #endif #ifdef SIGCONT install_signal_handler(SIGCONT, sig_cont, NULL, 0); #endif #ifdef SIGTTIN install_signal_handler(SIGTTIN, sig_tstp, NULL, 0); #endif } static const unsigned char seq_hide_cursor[] = "\033[10000B\033[10000C\033[?25l"; static const unsigned char seq_show_cursor[] = "\033[10000D\033[?25h"; static void fb_print(const unsigned char *str) { int wr; EINTRLOOP(wr, (int)write(TTY, str, strlen(cast_const_char str))); if (wr <= 0) EINTRLOOP(wr, (int)write(1, str, strlen(cast_const_char str))); } static void fb_hide_cursor(void) { fb_print(seq_hide_cursor); } static void fb_show_cursor(void) { fb_print(seq_show_cursor); } static void fb_pan_display(void) { int rs; vi.xoffset=0; vi.yoffset=0; EINTRLOOP(rs, ioctl(fb_handle, FBIOPAN_DISPLAY, &vi)); /* don't check error */ } static unsigned char *fb_init_driver(unsigned char *param, unsigned char *ignore) { char *se; unsigned char *e; struct stat st; int rs; unsigned long ul; #if 0 if (getenv("LINKS_MEMCPY_TEST")) { #define LEN 256 unsigned long iteration = 0; size_t ps = getpagesize(); unsigned char val = 0; unsigned char data[LEN]; unsigned char test[LEN]; unsigned char *map; unsigned char *from; unsigned start, end, i; memset(data, 0, LEN); memset(test, 0, LEN); map = mmap(NULL, ps * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (map == MAP_FAILED) perror("mmap"), exit(1); map += ps; if (mprotect(map, ps, PROT_READ | PROT_WRITE)) perror("mprotect"), exit(1); fprintf(stderr, "testing memcpy...\n"); while (1) { start = (unsigned)random() % LEN; end = (unsigned)random() % LEN; if (start > end) continue; /*if ((end - start) < 7) continue;*/ if (random() & 1) from = map + (unsigned)random() % 8; else from = map + ps - (end - start) - (unsigned)random() % 8; if (1) { for (i = start; i < end; i++) data[i] = val++; memcpy(from, data + start, end - start); /*fprintf(stderr, "copy %lu: %p, %p, %x\n", iteration, test + start, from, end - start);*/ memcpy_to_fb(test + start, from, end - start, 1); if (memcmp(test, data, LEN)) break; } iteration++; } internal_error("memcpy is broken (%lu): %u, %u (%u), %p", iteration, start, end, end - start, from); } #undef LEN #endif save_terminal(); 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=cast_uchar ""; if (*param) { if (*param < '0' || *param > '9') { bad_p: e = stracpy(cast_uchar "-mode syntax is left_border[,top_border[,right_border[,bottom_border]]]\n"); goto fail1; } ul = strtoul(cast_const_char param, &se, 10); param = cast_uchar se; if (ul > MAXINT / 10) goto bad_p; border_left = (int)ul; if (*param == ',') param++; } else { border_left = 0; } if (*param) { if (*param < '0' || *param > '9') goto bad_p; ul = strtoul(cast_const_char param, &se, 10); param = cast_uchar se; if (ul > MAXINT / 10) goto bad_p; border_top = (int)ul; if (*param == ',') param++; } else { border_top = border_left; } if (*param) { if (*param < '0' || *param > '9') goto bad_p; ul = strtoul(cast_const_char param, &se, 10); param = cast_uchar se; if (ul > MAXINT / 10) goto bad_p; border_right = (int)ul; if (*param == ',') param++; } else { border_right = border_left; } if (*param) { if (*param < '0' || *param > '9') goto bad_p; ul = strtoul(cast_const_char param, &se, 10); param = cast_uchar se; if (ul > MAXINT / 10) goto bad_p; border_bottom = (int)ul; if (*param == ',') param++; } else { border_bottom = border_top; } if (*param) goto bad_p; EINTRLOOP(rs, fstat(TTY, &st)); if (rs) { e = stracpy(cast_uchar "Cannon stat stdin.\n"); goto fail1; } fb_console = (int)(st.st_rdev & 0xff); fb_hide_cursor(); if ((e = fb_switch_init())) { goto fail2; } fb_handle = c_open(cast_uchar "/dev/fb0", O_RDWR | O_NOCTTY); if (fb_handle == -1) { e = stracpy(cast_uchar "Cannot open /dev/fb0.\n"); goto fail3; } EINTRLOOP(rs, ioctl(fb_handle, FBIOGET_VSCREENINFO, &vi)); if (rs == -1) { e = stracpy(cast_uchar "Cannot get FB VSCREENINFO.\n"); goto fail4; } /*oldmode=vi;*/ EINTRLOOP(rs, ioctl(fb_handle, FBIOGET_FSCREENINFO, &fi)); if (rs == -1) { e = stracpy(cast_uchar "Cannot get FB FSCREENINFO.\n"); goto fail4; } #if 0 { unsigned i, j, l; fprintf(stderr, "\n"); for (i = 0; i < sizeof(fi); i += 16) { l = sizeof(fi) - i; if (l > 16) l = 16; fprintf(stderr, "fi(%02x):", i); for (j = 0; j < l; j++) fprintf(stderr, "%s %02x", j && !(j & 3) ? " |" : "", ((unsigned char *)&fi)[i + j]); fprintf(stderr, "\n"); } fprintf(stderr, "\n"); for (i = 0; i < sizeof(vi); i += 16) { l = sizeof(vi) - i; if (l > 16) l = 16; fprintf(stderr, "vi(%02x):",i); for (j = 0; j < l; j++) fprintf(stderr, "%s %02x", j && !(j & 3) ? " |" : "", ((unsigned char *)&vi)[i + j]); fprintf(stderr, "\n"); } } #endif 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) { #if 0 case 4: fb_pixelsize = 1; fb_palette_colors = 16; break; #endif 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: e = stracpy(cast_uchar "Unknown bit depth.\n"); goto fail4; } fb_colors = 1 << fb_bits_pp; /* we must pan before setting palette */ fb_pan_display(); have_cmap = 0; if (fi.visual == FB_VISUAL_PSEUDOCOLOR && fb_colors <= 0x1000000) {/* set palette */ have_cmap = 1; fb_palette_colors = fb_colors; } if (fi.visual == FB_VISUAL_DIRECTCOLOR) { /* set pseudo palette */ have_cmap = 2; } fb_linesize = fi.line_length; fb_mem_size = fi.smem_len; init_virtual_devices(&fb_driver, NUMBER_OF_DEVICES); fb_kbd = handle_svgalib_keyboard(fb_key_in); /* Mikulas: nechodi to na sparcu */ if (fb_mem_size < (unsigned)((border_top + fb_ysize + border_bottom) * fb_linesize)) { e = stracpy(cast_uchar "Nonlinear mapping of graphics memory not supported.\n"); goto fail5; } if (vi.nonstd) { e = stracpy(cast_uchar "Non-standard pixel format.\n"); goto fail5; } fb_driver.flags |= GD_DONT_USE_SCROLL; #ifdef USE_FB_ACCEL need_accel_sync = 0; if (getenv("LINKS_NO_FB_ACCEL")) { rs = -1; } else { EINTRLOOP(rs, ioctl(fb_handle, FBIO_ACCEL_SUPPORT)); } if (rs < 0) { accel_flags = 0; /*debug("accel not supported");*/ } else { accel_flags = rs; /*debug("accel supported, flags %x", accel_flags);*/ } if (fb_bits_pp != 8) accel_flags &= ~(FB_ACCEL_FILLRECT_SUPPORTED | FB_ACCEL_FILLRECT_ACCELERATED); if (accel_flags & FB_ACCEL_COPYAREA_ACCELERATED) fb_driver.flags &= ~GD_DONT_USE_SCROLL; #endif /* * Some framebuffer implementations (for example Mach64) on Sparc64 hate * partial framebuffer mappings. * * For others, we can save virtual memory space by doing a partial mmap. */ fb_mapped_size = (border_top + fb_ysize + border_bottom) * fb_linesize; retry1: if ((fb_mem = mmap(NULL, fb_mapped_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_handle, 0)) == MAP_FAILED) { if (errno == EINTR) goto retry1; if (fb_mapped_size != fb_mem_size) { fb_mapped_size = fb_mem_size; goto retry1; } e = stracpy(cast_uchar "Cannot mmap graphics memory.\n"); goto fail5; } fb_vmem = fb_mem + border_left * fb_pixelsize + border_top * fb_linesize; fb_driver.depth = fb_pixelsize & 7; fb_driver.depth |= (fb_bits_pp & 31) << 3; #if 0 fprintf(stderr, "\nfb: bytes %u, bits %u\n", fb_pixelsize, fb_bits_pp); fprintf(stderr, "r: %x %x %x\n", vi.red.offset, vi.red.length, vi.red.msb_right); fprintf(stderr, "g: %x %x %x\n", vi.green.offset, vi.green.length, vi.green.msb_right); fprintf(stderr, "b: %x %x %x\n", vi.blue.offset, vi.blue.length, vi.blue.msb_right); fprintf(stderr, "t: %x %x %x\n", vi.transp.offset, vi.transp.length, vi.transp.msb_right); #endif if (big_endian) { /* Big endian */ if (fb_driver.depth == 130 || fb_driver.depth == 122) fb_driver.depth |= 1 << 8; else if (fb_driver.depth == 196) fb_driver.depth |= 1 << 9; } if (fb_driver.depth == 65 && have_cmap) { if (!fb_driver.param->palette_mode) fb_driver.depth |= 0x300; fb_driver.flags |= GD_SELECT_PALETTE; } else { fb_driver.flags &= ~GD_SELECT_PALETTE; } fb_driver.get_color = get_color_fn(fb_driver.depth); if (!fb_driver.get_color) { e = stracpy(cast_uchar "Unknown bit format.\n"); goto fail6; } if (have_cmap) { get_palette(&old_palette); generate_palette(&global_pal); set_palette(&global_pal); } install_signal_handler(SIGINT, fb_ctrl_c, fb_kbd, 0); /* mouse */ mouse_buffer = mem_alloc(fb_pixelsize * arrow_area); background_buffer = mem_alloc(fb_pixelsize * arrow_area); new_background_buffer = mem_alloc(fb_pixelsize * arrow_area); background_x = mouse_x = fb_xsize >> 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; last_mouse_buttons = B_MOVE; handle_fb_mouse(); /* hide cursor */ if (border_left | border_top | border_right | border_bottom) fb_clear_videoram(); show_mouse(); END_GR #if 0 if (getenv("LINKS_MEMCPY_BENCH")) { unsigned array2[20000]; unsigned i; for (i = 0; i < 327680; i++) { /*memcpy_to_fb((unsigned char *)fb_mem, (unsigned char *)array2 + 0, 2048);*/ /*memcpy_to_fb((unsigned char *)fb_mem + (i & 31), (unsigned char *)array2 + ((i >> 5) & 31), 11000);*/ /*memcpy_to_fb((unsigned char *)fb_mem + (i & 31), (unsigned char *)array2 + ((i >> 5) & 31), ((i >> 10) & 31) + 1, 1);*/ memcpy_to_fb((unsigned char *)array2 + ((i >> 5) & 31), (unsigned char *)fb_mem + (i & 31), ((i >> 10) & 8191) + 1, 1); } exit(1); } #endif return NULL; #if 0 fail7: fb_driver.shutdown_device(mouse_graphics_device); mem_free(mouse_buffer); mem_free(background_buffer); mem_free(new_background_buffer); fb_shutdown_palette(); #endif fail6: EINTRLOOP(rs, munmap(fb_mem, fb_mapped_size)); fail5: svgalib_free_trm(fb_kbd); shutdown_virtual_devices(); fail4: EINTRLOOP(rs, fsync(fb_handle)); close_socket(&fb_handle); restore_terminal(); fail3: fb_switch_shutdown(); fail2: fb_show_cursor(); fail1: if (fb_driver_param) { mem_free(fb_driver_param); fb_driver_param=NULL; } return e; } 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(); INC_IN_GR if (fb_active) { fb_clear_videoram(); /*EINTRLOOP(rs, ioctl(fb_handle, FBIOPUT_VSCREENINFO, &oldmode));*/ } fb_shutdown_palette(); EINTRLOOP(rs, fsync(fb_handle)); close_socket(&fb_handle); 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(); install_signal_handler(SIGINT, (void (*)(void *))NULL, NULL, 0); if (in_gr_operation) internal_error("fb_shutdown_driver: in_gr_operation %d", in_gr_operation); restore_terminal(); } static void fb_after_fork(void) { int rs; if (fb_handle != -1) { EINTRLOOP(rs, close(fb_handle)); fb_handle = -1; } } static unsigned char *fb_get_driver_param(void) { return fb_driver_param; } static void fb_get_margin(int *left, int *right, int *top, int *bottom) { *left = border_left; *right = border_right; *top = border_top; *bottom = border_bottom; } static int fb_set_margin(int left, int right, int top, int bottom) { struct rect_set *rs; int i, l; struct rect r; if (left + right >= fb_xsize + border_left + border_right) return -1; if (top + bottom >= fb_ysize + border_top + border_bottom) return -1; hide_mouse(); rs = init_rect_set(); add_to_rect_set(&rs, &mouse_graphics_device->size); r.x1 = left - border_left; r.y1 = top - border_top; r.x2 = fb_xsize - (right - border_right); r.y2 = fb_ysize - (bottom - border_bottom); exclude_rect_from_set(&rs, &r); do_with_mouse_device( for (i = 0; i < rs->m; i++) if (is_rect_valid(&rs->r[i])) drv->fill_area(current_virtual_device, rs->r[i].x1, rs->r[i].y1, rs->r[i].x2, rs->r[i].y2, 0); ); mem_free(rs); mouse_x += border_left - left; mouse_y += border_top - top; fb_xsize = fb_xsize + border_left + border_right - (left + right); fb_ysize = fb_ysize + border_top + border_bottom - (top + bottom); border_left = left; border_right = right; border_top = top; border_bottom = bottom; if (mouse_x >= fb_xsize) mouse_x = fb_xsize - 1; if (mouse_x < 0) mouse_x = 0; if (mouse_y >= fb_ysize) mouse_y = fb_ysize - 1; if (mouse_y < 0) mouse_y = 0; fb_vmem = fb_mem + border_left * fb_pixelsize + border_top * fb_linesize; mouse_graphics_device->size.x2 = mouse_graphics_device->clip.x2 = fb_xsize; mouse_graphics_device->size.y2 = mouse_graphics_device->clip.y2 = fb_ysize; show_mouse(); if (fb_driver_param) mem_free(fb_driver_param); fb_driver_param = init_str(); l = 0; add_num_to_str(&fb_driver_param, &l, left); add_chr_to_str(&fb_driver_param, &l, ','); add_num_to_str(&fb_driver_param, &l, top); add_chr_to_str(&fb_driver_param, &l, ','); add_num_to_str(&fb_driver_param, &l, right); add_chr_to_str(&fb_driver_param, &l, ','); add_num_to_str(&fb_driver_param, &l, bottom); resize_virtual_devices(fb_xsize, fb_ysize); return 0; } /* 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) { dest->data = NULL; if (dest->x && (size_t)dest->x * (size_t)dest->y / (size_t)dest->x != (size_t)dest->y) return -1; if ((size_t)dest->x * (size_t)dest->y > MAX_SIZE_T / 2 / fb_pixelsize) return -1; dest->data = mem_alloc_mayfail((size_t)dest->x * (size_t)dest->y * fb_pixelsize); if (!dest->data) return -1; dest->skip = (ssize_t)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) { if (bmp->data) mem_free(bmp->data); } static void *fb_prepare_strip(struct bitmap *bmp, int top, int lines) { if (!bmp->data) return NULL; return ((unsigned char *)bmp->data) + bmp->skip * top; } static void fb_commit_strip(struct bitmap *bmp, int top, int lines) { } static void fb_draw_bitmap(struct graphics_device *dev, struct bitmap *bmp, int x, int y) { unsigned char *scr_start; int rs; size_t copy_len; CLIP_PREFACE accel_sync(); scr_start = fb_vmem + y * fb_linesize + x * fb_pixelsize; copy_len = xs * fb_pixelsize; for (; ys; ys--) { memcpy_to_fb_inline(scr_start, data, copy_len, 0); data += bmp->skip; scr_start += fb_linesize; } END_MOUSE END_GR } #ifdef USE_FB_ACCEL_FILLRECT static inline unsigned fb_accel_color(void *ptr) { return *(unsigned char *)ptr; } #endif static void fb_fill_area(struct graphics_device *dev, int x1, int y1, int x2, int y2, long color) { unsigned char *dest; int y; int rs; FILL_CLIP_PREFACE #ifdef USE_FB_ACCEL_FILLRECT if (accel_flags & FB_ACCEL_FILLRECT_ACCELERATED) { struct fb_fillrect f; f.dx = x1 + border_left; f.dy = y1 + border_top; f.width = x2 - x1; f.height = y2 - y1; f.color = fb_accel_color(&color); f.rop = ROP_COPY; EINTRLOOP(rs, ioctl(fb_handle, FBIO_ACCEL_FILLRECT, &f)); if (rs < 0) { error("fill_area accel failed\n"); goto no_accel; } if (accel_flags & FB_ACCEL_SYNC_NEEDED) need_accel_sync = 1; } else no_accel: #endif { accel_sync(); dest=fb_vmem+y1*fb_linesize+x1*fb_pixelsize; for (y=y2-y1;y;y--){ pixel_set(dest,(x2-x1)*fb_pixelsize,&color); dest+=fb_linesize; } } END_MOUSE END_GR } static void fb_draw_hline(struct graphics_device *dev, int x1, int y, int x2, long color) { unsigned char *dest; int rs; HLINE_CLIP_PREFACE #ifdef USE_FB_ACCEL_FILLRECT if (accel_flags & FB_ACCEL_FILLRECT_ACCELERATED) { struct fb_fillrect f; f.dx = x1 + border_left; f.dy = y + border_top; f.width = x2 - x1; f.height = 1; f.color = fb_accel_color(&color); f.rop = ROP_COPY; EINTRLOOP(rs, ioctl(fb_handle, FBIO_ACCEL_FILLRECT, &f)); if (rs < 0) { error("draw_hline accel failed\n"); goto no_accel; } if (accel_flags & FB_ACCEL_SYNC_NEEDED) need_accel_sync = 1; } else no_accel: #endif { accel_sync(); dest=fb_vmem+y*fb_linesize+x1*fb_pixelsize; pixel_set(dest,(x2-x1)*fb_pixelsize,&color); } END_MOUSE END_GR } static void fb_draw_vline(struct graphics_device *dev, int x, int y1, int y2, long color) { unsigned char *dest; int y; int rs; VLINE_CLIP_PREFACE #ifdef USE_FB_ACCEL_FILLRECT if (accel_flags & FB_ACCEL_FILLRECT_ACCELERATED) { struct fb_fillrect f; f.dx = x + border_left; f.dy = y1 + border_top; f.width = 1; f.height = y2 - y1; f.color = fb_accel_color(&color); f.rop = ROP_COPY; EINTRLOOP(rs, ioctl(fb_handle, FBIO_ACCEL_FILLRECT, &f)); if (rs < 0) { error("draw_vline accel failed\n"); goto no_accel; } if (accel_flags & FB_ACCEL_SYNC_NEEDED) need_accel_sync = 1; } else no_accel: #endif { accel_sync(); dest = fb_vmem + y1 * fb_linesize + x * fb_pixelsize; y = y2 - y1; switch (fb_pixelsize) { case 1: { unsigned char val = *(unsigned char *)&color; do { *dest = val; dest += fb_linesize; } while (--y); break; } #ifdef t2c case 2: { t2c val = *(t2c *)memory_barrier(&color); do { *(t2c *)dest = val; dest += fb_linesize; } while (--y); break; } #endif #ifdef t4c case 4: { t4c val = *(t4c *)memory_barrier(&color); do { *(t4c *)dest = val; dest += fb_linesize; } while (--y); break; } #endif default: { do { memcpy_to_fb(dest, (unsigned char *)&color, fb_pixelsize, 0); dest += fb_linesize; } while (--y); break; } } } END_MOUSE END_GR } static int fb_scroll(struct graphics_device *dev, struct rect_set **ignore, int scx, int scy) { int rs; SCROLL_CLIP_PREFACE #ifdef USE_FB_ACCEL if (accel_flags & FB_ACCEL_COPYAREA_ACCELERATED) { struct fb_copyarea f; if (scx >= 0) { f.sx = dev->clip.x1 + border_left; f.width = dev->clip.x2 - dev->clip.x1 - scx; f.dx = dev->clip.x1 + scx + border_left; } else { f.sx = dev->clip.x1 - scx + border_left; f.width = dev->clip.x2 - dev->clip.x1 + scx; f.dx = dev->clip.x1 + border_left; } if (scy >= 0) { f.sy = dev->clip.y1 + border_top; f.height = dev->clip.y2 - dev->clip.y1 - scy; f.dy = dev->clip.y1 + scy + border_top; } else { f.sy = dev->clip.y1 - scy + border_top; f.height = dev->clip.y2 - dev->clip.y1 + scy; f.dy = dev->clip.y1 + border_top; } EINTRLOOP(rs, ioctl(fb_handle, FBIO_ACCEL_COPYAREA, &f)); if (rs < 0) { error("hscroll accel failed\n"); goto no_accel; } if (accel_flags & FB_ACCEL_SYNC_NEEDED) need_accel_sync = 1; } else no_accel: #endif { unsigned char *dest, *src; int y, len, dest_off, src_off; accel_sync(); if (scx >= 0) { len = (dev->clip.x2 - dev->clip.x1 - scx) * fb_pixelsize; dest_off = scx * fb_pixelsize; src_off = 0; } else { len = (dev->clip.x2 - dev->clip.x1 + scx) * fb_pixelsize; dest_off = 0; src_off = -scx * fb_pixelsize; } if (scy > 0) { /* Down */ dest = fb_vmem + (dev->clip.y2 - 1) * fb_linesize + dev->clip.x1 * fb_pixelsize; src = dest - fb_linesize * scy; dest += dest_off; src += src_off; for (y = dev->clip.y2 - dev->clip.y1 - scy; y; y--) { memcpy_to_fb(dest, src, len, 1); 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 * scy; dest += dest_off; src += src_off; for (y = dev->clip.y2 - dev->clip.y1 + scy; y; y--) { if (scy) memcpy_to_fb(dest, src, len, 1); else memmove_in_fb(dest, src, len); dest += fb_linesize; src += fb_linesize; } } } END_MOUSE END_GR return 1; } static void fb_update_palette(void) { int rs; if (!fb_driver.param->palette_mode) fb_driver.depth |= 0x300; else fb_driver.depth &= ~0x300; fb_driver.get_color = get_color_fn(fb_driver.depth); if (!fb_driver.get_color) internal_error("invalid driver depth %d", fb_driver.depth); mouse_black = fb_driver.get_color(0); mouse_white = fb_driver.get_color(0xffffff); free_palette(&global_pal); generate_palette(&global_pal); START_GR set_palette(&global_pal); END_GR } static int fb_block(struct graphics_device *dev) { int rs; if (fb_old_vd) return 1; INC_IN_GR if (fb_active) accel_sync(); 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 && fb_active) set_palette(&old_palette); EINTRLOOP(rs, fsync(fb_handle)); fb_switch_shutdown(); fb_clear_videoram(); fb_show_cursor(); fb_print(cast_uchar "\n"); restore_terminal(); return 0; } static int fb_unblock(struct graphics_device *dev) { int rs; unsigned char *e; if (current_virtual_device) return 0; if (dev != fb_block_dev) return -2; if (svgalib_unblock_itrm(fb_kbd)) return -1; e = fb_switch_init(); if (e) { fatal_exit("fb_switch_init failed: %s", e); } save_terminal(); fb_hide_cursor(); fb_pan_display(); current_virtual_device = fb_old_vd; fb_old_vd = NULL; if (have_cmap) set_palette(&global_pal); handle_fb_mouse(); END_GR 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); EINTRLOOP(rs, fsync(fb_handle)); return 0; } struct graphics_driver fb_driver = { cast_uchar "fb", fb_init_driver, init_virtual_device, shutdown_virtual_device, fb_shutdown_driver, fb_show_cursor, fb_after_fork, fb_get_driver_param, (unsigned char *(*)(void))NULL, fb_get_margin, fb_set_margin, fb_get_empty_bitmap, fb_register_bitmap, fb_prepare_strip, fb_commit_strip, fb_unregister_bitmap, fb_draw_bitmap, (long (*)(int))NULL, /* fb_get_color */ fb_fill_area, fb_draw_hline, fb_draw_vline, fb_scroll, (void (*)(struct graphics_device *dev))NULL, /* set_clip_area */ (void (*)(struct graphics_device *dev))NULL, /* flush */ fb_block, fb_unblock, fb_update_palette, /* set_palette */ NULL, /* get_real_colors */ (void (*)(struct graphics_device *, unsigned char *))NULL, /* set_title */ (int (*)(unsigned char *, int))NULL, /* exec */ (void (*)(unsigned char *))NULL, /* set_clipboard_text */ (unsigned char *(*)(void))NULL, /* get_clipboard_text */ 0, /* depth (filled in fb_init_driver function) */ 0, 0, /* size (in X is empty) */ GD_NEED_CODEPAGE, /* flags */ NULL, /* param */ }; #endif /* GRDRV_FB */