diff options
author | Keith Packard <keithp@neko.keithp.com> | 2006-11-25 10:05:58 -0800 |
---|---|---|
committer | Keith Packard <keithp@neko.keithp.com> | 2006-11-25 10:05:58 -0800 |
commit | edfcd07f8c11d1eea81781d782f6840dfeec8013 (patch) | |
tree | 37ad7aeabdd67cf68fe25273af67c6c08d28e287 | |
parent | 71a507fe21e135b98f6b43325c33e95220399024 (diff) | |
parent | ad435d8881c1a09d95d1895ecc11efe601413c2f (diff) |
Merge branch 'master' of ssh://git.freedesktop.org/git/twin
-rw-r--r-- | Makefile.am | 30 | ||||
-rw-r--r-- | ftwin.c | 105 | ||||
-rw-r--r-- | ftwin.cursor | bin | 0 -> 10004 bytes | |||
-rw-r--r-- | twin.h | 22 | ||||
-rw-r--r-- | twin_cursor.c | 238 | ||||
-rw-r--r-- | twin_fbdev.c | 502 | ||||
-rw-r--r-- | twin_fbdev.h | 96 | ||||
-rw-r--r-- | twin_linux_mouse.c | 193 | ||||
-rw-r--r-- | twin_linux_mouse.h | 67 | ||||
-rw-r--r-- | twin_screen.c | 126 |
10 files changed, 1347 insertions, 32 deletions
diff --git a/Makefile.am b/Makefile.am index ed2aa9c..99b2fc0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,13 +11,14 @@ INCLUDES= @X_CFLAGS@ @WARN_CFLAGS@ # #lib_LTLIBRARIES = libtwin.la -bin_PROGRAMS = xtwin +bin_PROGRAMS = xtwin ftwin -xtwin_SOURCES = \ +base_SOURCES = \ twin.h \ twin_box.c \ twin_button.c \ twin_convolve.c \ + twin_cursor.c \ twin_dispatch.c \ twin_draw.c \ twin_glyphs.c \ @@ -43,15 +44,17 @@ xtwin_SOURCES = \ twin_widget.c \ twin_window.c \ twin_work.c \ - twin_x11.c \ - twin_x11.h \ twinint.h \ twin_clock.c \ twin_clock.h \ twin_calc.c \ twin_calc.h \ twin_text.c \ - twin_text.h \ + twin_text.h + +xtwin_SOURCES = $(base_SOURCES) \ + twin_x11.c \ + twin_x11.h \ twin_demo.c \ twin_demo.h \ twin_hello.c \ @@ -62,4 +65,21 @@ xtwin_SOURCES = \ twin_demospline.h \ xtwin.c +ftwin_SOURCES = $(base_SOURCES) \ + twin_fbdev.c \ + twin_fbdev.h \ + twin_linux_mouse.c \ + twin_linux_mouse.h \ + twin_demo.c \ + twin_demo.h \ + twin_hello.c \ + twin_hello.h \ + twin_demoline.c \ + twin_demoline.h \ + twin_demospline.c \ + twin_demospline.h \ + ftwin.c + xtwin_LDADD = @X_LIBS@ -lm + +ftwin_LDADD = -lm @@ -0,0 +1,105 @@ +/* + * Test shell for twin fbdev & linux mouse driver + * + * Copyright 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> + * + * This Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Twin Library; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/time.h> +#include <time.h> +#include <stdlib.h> +#include <assert.h> +#include <signal.h> +#include <unistd.h> +#include <syscall.h> +#include <twin_clock.h> +#include <twin_text.h> +#include <twin_demo.h> +#include <twin_hello.h> +#include <twin_calc.h> +#include <twin_demoline.h> +#include <twin_demospline.h> + +#include "twin_fbdev.h" +#include "twin_linux_mouse.h" + +twin_fbdev_t *tf; + +static void exitfunc(void) +{ + if (tf) + twin_fbdev_destroy(tf); + tf = NULL; +} + +static void sigint(int sig) +{ + exitfunc(); + syscall(__NR_exit); +} + +int main (int argc, char **argv) +{ + int hx, hy; + twin_pixmap_t *cur; + + tf = twin_fbdev_create(-1, SIGUSR1); + if (tf == NULL) + return 1; + + atexit(exitfunc); + signal(SIGINT, sigint); + + twin_linux_mouse_create(NULL, tf->screen); + + cur = twin_load_X_cursor("ftwin.cursor", 0, &hx, &hy); + if (cur == NULL) + cur = twin_get_default_cursor(&hx, &hy); + if (cur != NULL) + twin_screen_set_cursor(tf->screen, cur, hx, hy); + twin_screen_set_background (tf->screen, twin_make_pattern ()); +#if 0 + twin_demo_start (tf->screen, "Demo", 100, 100, 400, 400); +#endif +#if 0 + twin_text_start (tf->screen, "Gettysburg Address", 0, 0, 300, 300); +#endif +#if 0 + twin_hello_start (tf->screen, "Hello, World", 0, 0, 200, 200); +#endif +#if 1 + twin_clock_start (tf->screen, "Clock", 10, 10, 200, 200); +#endif +#if 1 + twin_calc_start (tf->screen, "Calculator", 100, 100, 200, 200); +#endif +#if 1 + twin_demoline_start (tf->screen, "Demo Line", 0, 0, 400, 400); +#endif +#if 1 + twin_demospline_start (tf->screen, "Demo Spline", 20, 20, 400, 400); +#endif + + twin_fbdev_activate(tf); + + twin_dispatch (); + + return 0; +} diff --git a/ftwin.cursor b/ftwin.cursor Binary files differnew file mode 100644 index 0000000..785a7e4 --- /dev/null +++ b/ftwin.cursor @@ -153,6 +153,15 @@ typedef struct _twin_screen { */ twin_pixmap_t *pointer; /* + * mouse image (optional) + */ + twin_pixmap_t *cursor; + twin_coord_t curs_hx; + twin_coord_t curs_hy; + twin_coord_t curs_x; + twin_coord_t curs_y; + + /* * Output size */ twin_coord_t width, height; @@ -546,6 +555,15 @@ twin_fill (twin_pixmap_t *dst, twin_coord_t bottom); /* + * twin_cursor.c + */ +twin_pixmap_t *twin_get_default_cursor(int *hx, int *hy); + +twin_pixmap_t *twin_load_X_cursor(const char *file, int index, + int *hx, int *hy); + + +/* * twin_event.c */ @@ -970,6 +988,10 @@ twin_screen_set_background (twin_screen_t *screen, twin_pixmap_t *pixmap); twin_pixmap_t * twin_screen_get_background (twin_screen_t *screen); +void +twin_screen_set_cursor (twin_screen_t *screen, twin_pixmap_t *pixmap, + twin_fixed_t hotspot_x, twin_fixed_t hotspot_y); + twin_bool_t twin_screen_dispatch (twin_screen_t *screen, twin_event_t *event); diff --git a/twin_cursor.c b/twin_cursor.c new file mode 100644 index 0000000..f0e5fc3 --- /dev/null +++ b/twin_cursor.c @@ -0,0 +1,238 @@ +/* + * Manipulating twin cursor images + * + * Copyright 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> + * + * This Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Twin Library; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <byteswap.h> +#include <endian.h> + +#include "twin.h" + +/* Make something better here ! */ + +static unsigned int twin_def_cursor_image[] = { + 0x00000000, 0x88ffffff, 0x88ffffff, 0x00000000, + 0x88ffffff, 0xff000000, 0xff000000, 0x88ffffff, + 0x88ffffff, 0xff000000, 0xff000000, 0x88ffffff, + 0x00000000, 0x88ffffff, 0x88ffffff, 0x00000000, +}; + +twin_pixmap_t *twin_get_default_cursor(int *hx, int *hy) +{ + twin_pixmap_t *cur; + twin_pointer_t data; + + data.argb32 = twin_def_cursor_image; + cur = twin_pixmap_create_const(TWIN_ARGB32, 4, 4, 16, data); + if (cur == NULL) + return NULL; + *hx = *hy = 2; + return cur; +} + +/* + * Bits extracted from Xcursor + */ + +#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */ + +#define XCURSOR_FILE_MAJOR 1 +#define XCURSOR_FILE_MINOR 0 +#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR)) +#define XCURSOR_FILE_HEADER_LEN (4 * 4) +#define XCURSOR_FILE_TOC_LEN (3 * 4) + +/* + * Cursor files start with a header. The header + * contains a magic number, a version number and a + * table of contents which has type and offset information + * for the remaining tables in the file. + * + * File minor versions increment for compatible changes + * File major versions increment for incompatible changes (never, we hope) + * + * Chunks of the same type are always upward compatible. Incompatible + * changes are made with new chunk types; the old data can remain under + * the old type. Upward compatible changes can add header data as the + * header lengths are specified in the file. + * + * File: + * FileHeader + * LISTofChunk + * + * FileHeader: + * 0 CARD32 magic magic number + * 1 CARD32 header bytes in file header + * 2 CARD32 version file version + * 3 CARD32 ntoc number of toc entries + * LISTofFileToc toc table of contents + * + * FileToc: + * 0 CARD32 type entry type + * 1 CARD32 subtype entry subtype (size for images) + * 2 CARD32 position absolute file position + */ + + +/* + * The rest of the file is a list of chunks, each tagged by type + * and version. + * + * Chunk: + * ChunkHeader + * <extra type-specific header fields> + * <type-specific data> + * + * ChunkHeader: + * 0 CARD32 header bytes in chunk header + type header + * 1 CARD32 type chunk type + * 2 CARD32 subtype chunk subtype + * 3 CARD32 version chunk type version + */ + +#define XCURSOR_CHUNK_HEADER_LEN (4 * 4) + +/* + * Each cursor image occupies a separate image chunk. + * The length of the image header follows the chunk header + * so that future versions can extend the header without + * breaking older applications + * + * Image: + * 0 ChunkHeader header chunk header + * 4 CARD32 width actual width + * 5 CARD32 height actual height + * 6 CARD32 xhot hot spot x + * 7 CARD32 yhot hot spot y + * 8 CARD32 delay animation delay + * LISTofCARD32 pixels ARGB pixels + */ + +#define XCURSOR_IMAGE_TYPE 0xfffd0002 +#define XCURSOR_IMAGE_VERSION 1 +#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4)) +#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */ + +static inline int twin_read_header(int fd, uint32_t *buf, int size) +{ + int i, len; + + len = read(fd, buf, size); + if (len != size) + return 0; + +#if __BYTE_ORDER == __BIG_ENDIAN + for (i = 0; i < (len / 4); i++) + buf[i] = bswap_32(buf[i]); +#endif + return 1; +} + +twin_pixmap_t *twin_load_X_cursor(const char *file, int index, + int *hx, int *hy) +{ + int fd, img, i, toccnt; + uint32_t buffer[32], filepos, size; + twin_pixmap_t *cur = NULL; + + fd = open(file, O_RDONLY); + if (fd < 0) + return NULL; + if (!twin_read_header(fd, buffer, XCURSOR_FILE_HEADER_LEN)) + goto bail; + + /* check magic */ + if (buffer[0] != XCURSOR_MAGIC) + goto bail; + + /* check version. assume we support all minor versions */ + if ((buffer[2] >> 16) != XCURSOR_FILE_MAJOR) + goto bail; + + /* get number of TOC entries */ + toccnt = buffer[3]; + + /* seek to first toc entry (header len) */ + lseek(fd, buffer[1] , SEEK_SET); + + /* look for the index'th image in TOC */ + img = 0; + filepos = 0; + for (i = 0; filepos == 0 && i < toccnt; i++) { + if (!twin_read_header(fd, buffer, XCURSOR_FILE_TOC_LEN)) + goto bail; + if (buffer[0] == XCURSOR_IMAGE_TYPE) { + if (img == index) + filepos = buffer[2]; + img++; + } + } + /* check if found */ + if (filepos == 0) + goto bail; + + /* seek to image header and read it */ + lseek(fd, filepos, SEEK_SET); + if (!twin_read_header(fd, buffer, XCURSOR_IMAGE_HEADER_LEN)) + goto bail; + + /* check chunk type */ + if (buffer[1] != XCURSOR_IMAGE_TYPE) + goto bail; + + /* check image version */ + if (buffer[3] != XCURSOR_IMAGE_VERSION) + goto bail; + + /* get hotspot */ + *hx = buffer[6]; + *hy = buffer[7]; + + /* create pixmap */ + cur = twin_pixmap_create(TWIN_ARGB32, buffer[4], buffer[5]); + if (cur == NULL) + goto bail; + + /* load pixels */ + size = buffer[4] * buffer[5] * 4; + lseek(fd, filepos + buffer[0], SEEK_SET); + if (read(fd, cur->p.v, size) != size) { + twin_pixmap_destroy(cur); + goto bail; + } + +#if __BYTE_ORDER == __BIG_ENDIAN + for (i = 0; i < (size / 4); i++) + cur->p.argb32[i] = bswap_32(cur->p.argb32[i]); +#endif + + bail: + close(fd); + return cur; +} + diff --git a/twin_fbdev.c b/twin_fbdev.c new file mode 100644 index 0000000..a1d9472 --- /dev/null +++ b/twin_fbdev.c @@ -0,0 +1,502 @@ +/* + * Linux fbdev driver for Twin + * + * Copyright 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> + * + * This Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Twin Library; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <termios.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <stdarg.h> +#include <byteswap.h> + +#include <linux/vt.h> +#include <linux/kd.h> +#include <linux/input.h> + +#include "twin_fbdev.h" + +/* We might want to have more error logging options */ +#define SERROR(fmt...) do { fprintf(stderr, fmt); \ + fprintf(stderr, " : %s\n", strerror(errno)); \ + } while(0) +#define IERROR(fmt...) fprintf(stderr, fmt) + +#define DEBUG(fmt...) printf(fmt) +//#define DEBUG(fmt...) + +/* Only one instance can exist */ +static twin_fbdev_t *twin_fb; +static int vt_switch_pending; + +static void _twin_fbdev_put_span (twin_coord_t left, + twin_coord_t top, + twin_coord_t right, + twin_argb32_t *pixels, + void *closure) +{ + twin_fbdev_t *tf = closure; + twin_coord_t width = right - left; + unsigned int *dest; + + if (!tf->active || tf->fb_base == MAP_FAILED) + return; + + dest = (unsigned int *)(tf->fb_ptr + top * tf->fb_fix.line_length); + dest += left; + while(width--) + *(dest++) = *(pixels++); +} + +static twin_bool_t twin_fbdev_apply_config(twin_fbdev_t *tf) +{ + off_t off, pgsize = getpagesize(); + struct fb_cmap cmap; + size_t len; + + /* Tweak fields to default to 32 bpp argb and virtual == phys */ + tf->fb_var.xres_virtual = tf->fb_var.xres; + tf->fb_var.yres_virtual = tf->fb_var.yres; + tf->fb_var.bits_per_pixel = 32; + tf->fb_var.red.length = 8; + tf->fb_var.green.length = 8; + tf->fb_var.blue.length = 8; + tf->fb_var.transp.length = 8; + tf->fb_var.red.offset = 0; + tf->fb_var.green.offset = 0; + tf->fb_var.blue.offset = 0; + tf->fb_var.transp.offset = 0; + + /* Apply fbdev settings */ + if (ioctl(tf->fb_fd, FBIOPUT_VSCREENINFO, &tf->fb_var) < 0) { + SERROR("can't set fb mode"); + return 0; + } + + /* Get new fbdev configuration */ + if (ioctl(tf->fb_fd, FBIOGET_VSCREENINFO, tf->fb_var) < 0) { + SERROR("can't get framebuffer config"); + return 0; + } + + DEBUG("fbdev set config set to:\n"); + DEBUG(" xres = %d\n", tf->fb_var.xres); + DEBUG(" yres = %d\n", tf->fb_var.yres); + DEBUG(" xres_virtual = %d\n", tf->fb_var.xres_virtual); + DEBUG(" yres_virtual = %d\n", tf->fb_var.yres_virtual); + DEBUG(" bits_per_pix = %d\n", tf->fb_var.bits_per_pixel); + DEBUG(" red.len/off = %d/%d\n", + tf->fb_var.red.length, tf->fb_var.red.offset); + DEBUG(" green.len/off = %d/%d\n", + tf->fb_var.green.length, tf->fb_var.green.offset); + DEBUG(" blue.len/off = %d/%d\n", + tf->fb_var.blue.length, tf->fb_var.blue.offset); + DEBUG(" trans.len/off = %d/%d\n", + tf->fb_var.transp.length, tf->fb_var.transp.offset); + + /* Check bits per pixel */ + if (tf->fb_var.bits_per_pixel != 32) { + SERROR("can't set fb bpp to 32"); + return 0; + } + + /* Set colormap */ + cmap.start = 0; + cmap.len = 256; + cmap.red = tf->cmap[0]; + cmap.green = tf->cmap[1]; + cmap.blue = tf->cmap[2]; + cmap.transp = NULL; + ioctl(tf->fb_fd, FBIOPUTCMAP, &cmap); + + /* Get remaining settings */ + ioctl(tf->fb_fd, FBIOGET_FSCREENINFO, &tf->fb_fix); + + DEBUG(" line_lenght = %d\n", tf->fb_fix.line_length); + + /* Map the fb */ + off = (off_t)tf->fb_fix.smem_start & (pgsize - 1); + len = (size_t)tf->fb_fix.smem_len + off + (pgsize - 1); + len &= ~(pgsize - 1); + tf->fb_len = len; + + tf->fb_base = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, + tf->fb_fd, 0); + if (tf->fb_base == MAP_FAILED) { + SERROR("can't mmap framebuffer"); + return 0; + } + tf->fb_ptr = tf->fb_base + off; + + return 1; +} + +static void twin_fbdev_switch(twin_fbdev_t *tf, int activate) +{ + tf->vt_active = activate; + + DEBUG("console switch: %sactivating\n", activate ? "" : "de"); + + /* Upon activation */ + if (activate) { + /* Switch complete */ + ioctl(tf->vt_fd, VT_RELDISP, VT_ACKACQ); + + /* Restore fbdev settings */ + if (twin_fbdev_apply_config(tf)) { + /* Mark entire screen for refresh */ + if (tf->screen) + twin_screen_damage (tf->screen, 0, 0, + tf->screen->width, + tf->screen->height); + tf->active = 1; + } + } else { + /* Allow switch. Maybe we want to expose some option + * to disallow them ? + */ + ioctl(tf->vt_fd, VT_RELDISP, 1); + + tf->active = 0; + + if (tf->fb_base != MAP_FAILED) + munmap(tf->fb_base, tf->fb_len); + tf->fb_base = MAP_FAILED; + } +} + +static twin_bool_t twin_fbdev_work(void *closure) +{ + twin_fbdev_t *tf = closure; + + if (vt_switch_pending) { + twin_fbdev_switch(tf, !tf->vt_active); + vt_switch_pending = 0; + } + + if (tf->screen && tf->active && + twin_screen_damaged (tf->screen)) + twin_screen_update(tf->screen); + return TWIN_TRUE; +} + +static void twin_fbdev_vtswitch(int sig) +{ + signal(sig, twin_fbdev_vtswitch); + vt_switch_pending = 1; +} + +static twin_bool_t twin_fbdev_read_events(int file, twin_file_op_t ops, + void *closure) +{ + twin_fbdev_t *tf = closure; + unsigned char events[16]; + int i, count; + + count = read(file, events, 16); + + for (i = 0; i < count; i++) { + unsigned char e = events[i]; + + /* Handle special keys */ + switch(e) { + case KEY_F1...KEY_F10: + ioctl(tf->vt_fd, VT_ACTIVATE, e - KEY_F1 + 1); + break; + case KEY_ESC: + kill(0, SIGINT); + break; + } + } + + return 1; +} + +static twin_bool_t twin_fbdev_get_vt(twin_fbdev_t *tf, int wanted_vt) +{ + struct vt_stat vts; + int ttyfd; + char vtname[16]; + + /* Open tty0 and use it to look for a free vt */ + ttyfd = open("/dev/tty0", O_WRONLY); + if (ttyfd < 0) { + SERROR("can't open /dev/tty0"); + ttyfd = open("/dev/vc/0", O_WRONLY); + if (ttyfd < 0) { + SERROR("can't open /dev/vc/0"); + return 0; + } + } + /* Get previous VT */ + if (ioctl(ttyfd, VT_GETSTATE, &vts) < 0) { + SERROR("can't get current VT"); + return 0; + } + tf->vt_prev = vts.v_active; + + DEBUG("previous vt: %d\n", tf->vt_prev); + + /* Sanity check wanted_vt and try to obtain a free VT. This is + * all somewhat racy but that's how everybody does it. + */ + if (wanted_vt > 31) + wanted_vt = -1; + if (wanted_vt > 0 && (vts.v_state & (1 << wanted_vt))) { + IERROR("vt%d is busy\n", wanted_vt); + wanted_vt = -1; + } + if (wanted_vt < 0) + if (ioctl(ttyfd, VT_OPENQRY, &wanted_vt) < 0) + wanted_vt = -1; + if (wanted_vt < 0) { + IERROR("can't find a free VT"); + return 0; + } + + tf->vt_no = wanted_vt; + + DEBUG("new vt: %d\n", tf->vt_no); + + + /* we don't need tty0 anymore, close it and open the target VT. */ + close(ttyfd); + + /* lose tty */ + setpgrp(); + if ((ttyfd = open("/dev/tty", O_RDWR)) >= 0) { + ioctl(ttyfd, TIOCNOTTY, 0); + close(ttyfd); + } + + sprintf(vtname, "/dev/tty%d", tf->vt_no); + tf->vt_fd = open(vtname, O_RDWR | O_NONBLOCK); + if (tf->vt_fd < 0) { + sprintf(vtname, "/dev/vc/%d", tf->vt_no); + tf->vt_fd = open(vtname, O_RDWR | O_NONBLOCK); + } + if (tf->vt_fd < 0) { + SERROR("can't open tty %d", tf->vt_no); + return 0; + } + + /* set new controlling terminal */ + ioctl(tf->vt_fd, TIOCSCTTY, 1); + + /* set keyboard mode */ + ioctl(tf->vt_fd, KDSKBMODE, K_XLATE); + + tf->vt_active = tf->active = 0; + + return 1; +} + +static twin_bool_t twin_fbdev_setup_vt(twin_fbdev_t *tf, int switch_sig) +{ + struct vt_mode vtm; + struct termios tio; + + if (ioctl(tf->vt_fd, VT_GETMODE, &vtm) < 0) { + SERROR("can't get VT mode"); + return 0; + } + vtm.mode = VT_PROCESS; + vtm.relsig = switch_sig; + vtm.acqsig = switch_sig; + + signal(switch_sig, twin_fbdev_vtswitch); + tf->vt_swsig = switch_sig; + + if (ioctl(tf->vt_fd, VT_SETMODE, &vtm) < 0) { + SERROR("can't set VT mode"); + signal(switch_sig, SIG_IGN); + return 0; + } + + tcgetattr(tf->vt_fd, &tf->old_tio); + + ioctl(tf->vt_fd, KDGKBMODE, &tf->old_kbmode); + ioctl(tf->vt_fd, KDSKBMODE, K_MEDIUMRAW); + + tio = tf->old_tio; + tio.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP); + tio.c_oflag = 0; + tio.c_cflag = CREAD | CS8; + tio.c_lflag = 0; + tio.c_cc[VTIME]=0; + tio.c_cc[VMIN]=1; + cfsetispeed(&tio, 9600); + cfsetospeed(&tio, 9600); + tcsetattr(tf->vt_fd, TCSANOW, &tio); + + ioctl(tf->vt_fd, KDSETMODE, KD_GRAPHICS); + + return 1; +} + +static twin_bool_t twin_fbdev_init_fb(twin_fbdev_t *tf) +{ + int i; + + /* We always open /dev/fb0 for now. Might want fixing */ + tf->fb_fd = open("/dev/fb0", O_RDWR); + if (tf->fb_fd < 0) { + SERROR("can't open /dev/fb0"); + return 0; + } + + /* Get initial fbdev configuration */ + if (ioctl(tf->fb_fd, FBIOGET_VSCREENINFO, &tf->fb_var) < 0) { + SERROR("can't get framebuffer config"); + return 0; + } + + DEBUG("initial screen size: %dx%d\n", + tf->fb_var.xres, tf->fb_var.yres); + + tf->fb_base = MAP_FAILED; + + for (i = 0; i < 256; i++) { + unsigned short c = (i << 8) | i; + tf->cmap[0][i] = tf->cmap[1][i] = tf->cmap[2][i] = c; + } + + return 1; +} + +static twin_bool_t twin_fbdev_init_screen(twin_fbdev_t *tf) +{ + tf->screen = twin_screen_create(tf->fb_var.xres, + tf->fb_var.yres, + NULL, + _twin_fbdev_put_span, tf); + if (tf->screen == NULL) { + IERROR("can't create twin screen"); + return 0; + } + + return 1; +} + +static void twin_fbdev_cleanup_vt(twin_fbdev_t *tf) +{ + struct vt_mode vtm; + + ioctl(tf->vt_fd, VT_GETMODE, &vtm); + vtm.mode = VT_AUTO; + vtm.relsig = 0; + vtm.acqsig = 0; + ioctl(tf->vt_fd, VT_SETMODE, &vtm); + + signal(tf->vt_swsig, SIG_DFL); + + tcsetattr(tf->vt_fd, TCSANOW, &tf->old_tio); + ioctl(tf->vt_fd, KDSKBMODE, tf->old_kbmode); + + ioctl(tf->vt_fd, KDSETMODE, KD_TEXT); + ioctl(tf->vt_fd, VT_ACTIVATE, tf->vt_prev); + ioctl(tf->vt_fd, VT_WAITACTIVE, tf->vt_prev); +} + +twin_fbdev_t *twin_fbdev_create(int wanted_vt, int switch_sig) +{ + twin_fbdev_t *tf; + + if (twin_fb != NULL) { + IERROR("twin_fbdev supports only one instance"); + return NULL; + } + + tf = calloc(1, sizeof(twin_fbdev_t)); + if (tf == NULL) + return NULL; + + if (!twin_fbdev_get_vt(tf, wanted_vt)) + goto err_free; + + if (!twin_fbdev_setup_vt(tf, switch_sig)) + goto err_release; + + if (!twin_fbdev_init_fb(tf)) + goto err_reset_vt; + + if (!twin_fbdev_init_screen(tf)) + goto err_close_fb; + + twin_set_work(twin_fbdev_work, TWIN_WORK_REDISPLAY, tf); + + twin_set_file(twin_fbdev_read_events, tf->vt_fd, TWIN_READ, tf); + + twin_fb = tf; + return tf; + + err_close_fb: + close(tf->fb_fd); + err_reset_vt: + twin_fbdev_cleanup_vt(tf); + signal(switch_sig, SIG_DFL); + err_release: + close(tf->vt_fd); + err_free: + free(tf); + return NULL; +} + +void twin_fbdev_destroy(twin_fbdev_t *tf) +{ + tf->active = 0; + twin_fbdev_cleanup_vt(tf); + close(tf->fb_fd); + close(tf->vt_fd); + free(tf); + twin_fb = NULL; +} + +twin_bool_t twin_fbdev_activate(twin_fbdev_t *tf) +{ + /* If VT is not active, try to activate it. We don't deadlock + * here thanks to linux not waiting for VT_RELDISP on the target + * but we might want to be more careful + */ + if (!tf->vt_active) { + if (ioctl(tf->vt_fd, VT_ACTIVATE, tf->vt_no) < 0) + return 0; + if (ioctl(tf->vt_fd, VT_WAITACTIVE, tf->vt_no) < 0) + return 0; + } + + /* Run work to process the VT switch */ + twin_fbdev_work(tf); + + /* If the screen is not active, then we failed + * the fbdev configuration + */ + return tf->active; +} + diff --git a/twin_fbdev.h b/twin_fbdev.h new file mode 100644 index 0000000..0b3bcb3 --- /dev/null +++ b/twin_fbdev.h @@ -0,0 +1,96 @@ +/* + * Linux fbdev driver for Twin + * + * Copyright 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> + * + * This Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Twin Library; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _TWIN_FBDEV_H_ +#define _TWIN_FBDEV_H_ + +#include "twin.h" + +#include <termios.h> +#include <linux/fb.h> + +typedef struct _twin_fbdev { + twin_screen_t *screen; /* twin screen */ + + int vt_no; /* my VT */ + int vt_prev; /* previous VT */ + int vt_fd; /* my tty fd */ + int vt_active; /* vt is currently active */ + int vt_swsig; + + struct termios old_tio; /* old termios */ + int old_kbmode; + + int active; /* screen is active and useable */ + int fb_fd; /* my fbdev fd */ + int last_btn; /* last button state */ + + /* fbdev setup */ + struct fb_var_screeninfo fb_var; + struct fb_fix_screeninfo fb_fix; + unsigned short cmap[3][256]; + char *fb_base; + size_t fb_len; + char *fb_ptr; + +} twin_fbdev_t; + +/** + * twin_fbdev_create - create the fbdev backend + * @wanted_vt: which VT do you want ? pass -1 for auto-choose + * @switch_sig: signal for use internally for vt switch + * + * The new VT is not activated automatically. You can call + * twin_fbdev_activate() to do that. That way, you can setup your + * environment completely before you do the VT switch for smoother + * transitions. + * + * The fbdev is left to it's default settings. You can call functions + * to change them though they will only be applied if the fbdev is + * frontmost or when it is activated. + * + * Note that this implementation only supports 32bpp argb though it + * shouldn't be too hard to change that. + * + * Regarding the signal passed in switch_sig, it's the responsibility + * of the caller to make sure it's not blocked. + */ + +twin_fbdev_t *twin_fbdev_create(int wanted_vt, int switch_sig); + +/** + * twin_fbdev_destroy - distroy the fbdev backend + * @tf: backend pointed returned by twin_fbdev_create + */ +void twin_fbdev_destroy(twin_fbdev_t *tf); + +/** + * twin_fbdev_activate - activate the fbdev + * @tf: backemd pointer + * + * This triggers a console switch to the VT allocated to the + * twin screen. It returns false if the activation failed + * due to unsupported framebuffer settings or the VT switch + * was refused. + */ +twin_bool_t twin_fbdev_activate(twin_fbdev_t *tf); + + +#endif /* _TWIN_FBDEV_H_ */ diff --git a/twin_linux_mouse.c b/twin_linux_mouse.c new file mode 100644 index 0000000..c755495 --- /dev/null +++ b/twin_linux_mouse.c @@ -0,0 +1,193 @@ +/* + * Linux mouse driver for Twin + * + * Copyright 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> + * + * This Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Twin Library; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <math.h> + +#include "twin_linux_mouse.h" + +#define QUADRATIC_ACCELERATION 1 +#define DEFAULT_ACC_NUMERATOR 2 +#define DEFAULT_ACC_DENOMINATOR 1 +#define DEFAULT_ACC_THRESHOLD 4 + +static void twin_linux_mouse_check_bounds(twin_linux_mouse_t *tm) +{ + if (tm->x < 0) + tm->x = 0; + if (tm->x > tm->screen->width) + tm->x = tm->screen->width; + if (tm->y < 0) + tm->y = 0; + if (tm->y > tm->screen->height) + tm->y = tm->screen->height; +} + +/* This is directly copied from kdrive */ +static void twin_linux_mouse_accel(twin_linux_mouse_t *tm, int *dx, int *dy) +{ + double speed = sqrt (*dx * *dx + *dy * *dy); + double accel; +#ifdef QUADRATIC_ACCELERATION + double m; + + /* + * Ok, so we want it moving num/den times faster at threshold*2 + * + * accel = m *threshold + b + * 1 = m * 0 + b -> b = 1 + * + * num/den = m * (threshold * 2) + 1 + * + * num / den - 1 = m * threshold * 2 + * (num / den - 1) / threshold * 2 = m + */ + m = (((double) tm->acc_num / (double) tm->acc_den - 1.0) / + ((double) tm->acc_threshold * 2.0)); + accel = m * speed + 1; +#else + accel = 1.0; + if (speed > tm->acc_threshold) + accel = (double) tm->acc_num / tm->acc_den; +#endif + *dx = accel * *dx; + *dy = accel * *dy; +} + +static twin_bool_t twin_linux_mouse_events(int file, twin_file_op_t ops, + void *closure) +{ + twin_linux_mouse_t *tm = closure; + char evts[34]; + char *ep; + int n = tm->res_cnt; + twin_event_t tev; + + if (n) + memcpy(evts, tm->residual, n); + n += read(file, evts + n, 32); + + for(ep = evts; n >= 3; n -= 3, ep += 3) { + int dx, dy, btn; + dx = ep[1]; + if (ep[0] & 0x10) + dx -= 256; + dy = ep[2]; + if (ep[0] & 0x20) + dy -= 256; + dy = -dy; + /* we handle only one btn for now */ + btn = ep[0] & 0x1; + if (dx || dy) { + twin_linux_mouse_accel(tm, &dx, &dy); + tm->x += dx; + tm->y += dy; + twin_linux_mouse_check_bounds(tm); + tev.kind = TwinEventMotion; + tev.u.pointer.screen_x = tm->x; + tev.u.pointer.screen_y = tm->y; + tev.u.pointer.button = tm->btns; + twin_screen_dispatch (tm->screen, &tev); + } + if (btn != tm->btns) { + tm->btns = btn; + tev.kind = (btn & 0x1) ? + TwinEventButtonDown : TwinEventButtonUp; + tev.u.pointer.screen_x = tm->x; + tev.u.pointer.screen_y = tm->y; + tev.u.pointer.button = tm->btns; + twin_screen_dispatch(tm->screen, &tev); + } + } + tm->res_cnt = n; + if (n) + memcpy(tm->residual, ep, n); + + return 1; +} + +twin_linux_mouse_t *twin_linux_mouse_create(const char *file, + twin_screen_t *screen) +{ + twin_linux_mouse_t *tm; + + tm = calloc(1, sizeof(twin_linux_mouse_t)); + if (tm == NULL) + return NULL; + + if (file == NULL) + file = "/dev/input/mice"; + + tm->screen = screen; + tm->acc_num = DEFAULT_ACC_NUMERATOR; + tm->acc_den = DEFAULT_ACC_DENOMINATOR; + tm->acc_threshold =DEFAULT_ACC_THRESHOLD; + tm->x = screen->width / 2; + tm->y = screen->height / 2; + tm->fd = open(file, O_RDONLY); + if (tm->fd < 0) { + free(tm); + return NULL; + } + + twin_set_file(twin_linux_mouse_events, tm->fd, TWIN_READ, tm); + + return tm; +} + +void twin_linux_mouse_destroy(twin_linux_mouse_t *tm) +{ + close(tm->fd); + free(tm); +} + +void twin_linux_mouse_screen_changed(twin_linux_mouse_t *tm) +{ + int oldx, oldy; + + oldx = tm->x; + oldy = tm->y; + twin_linux_mouse_check_bounds(tm); + if (tm->x != oldx || tm->y != oldy) { + twin_event_t tev; + + tev.kind = TwinEventMotion; + tev.u.pointer.screen_x = tm->x; + tev.u.pointer.screen_y = tm->y; + tev.u.pointer.button = tm->btns; + twin_screen_dispatch (tm->screen, &tev); + } +} + +void twin_linux_mouse_set_accel(twin_linux_mouse_t *tm, + int num, int den, int threshold) +{ + tm->acc_num = num; + tm->acc_den = den; + tm->acc_threshold = threshold; +} diff --git a/twin_linux_mouse.h b/twin_linux_mouse.h new file mode 100644 index 0000000..1f6cc0c --- /dev/null +++ b/twin_linux_mouse.h @@ -0,0 +1,67 @@ +/* + * Linux mouse driver for Twin + * + * Copyright 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> + * + * This Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Twin Library; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _TWIN_LINUX_MOUSE_H_ +#define _TWIN_LINUX_MOUSE_H_ + +#include "twin.h" + +typedef struct _twin_linux_mouse { + twin_screen_t *screen; + + /* acceleration settings */ + int acc_num; + int acc_den; + int acc_threshold; + + /* internals */ + int fd; + char residual[2]; + int res_cnt; + int btns; + int x,y; +} twin_linux_mouse_t; + +/** + * twin_linux_mouse_create - create the linux mouse driver + * @file: device file to open, pass NULL for default + */ +twin_linux_mouse_t *twin_linux_mouse_create(const char *file, + twin_screen_t *screen); + +/** + * twin_linux_mouse_destroy - destroy the linux mouse driver + */ +void twin_linux_mouse_destroy(twin_linux_mouse_t *tm); + +/** + * twin_linux_mouse_set_bounds - set mouse boundaries + */ +void twin_linux_mouse_screen_changed(twin_linux_mouse_t *tm); + + +/** + * twin_linux_mouse_set_accel - set mouse acceleration data + */ + +void twin_linux_mouse_set_accel(twin_linux_mouse_t *tm, + int num, int den, int threshold); + +#endif /* _TWIN_LINUX_MOUSE_H_ */ diff --git a/twin_screen.c b/twin_screen.c index c5e489a..8de8007 100644 --- a/twin_screen.c +++ b/twin_screen.c @@ -28,7 +28,7 @@ twin_screen_create (twin_coord_t width, twin_put_span_t put_span, void *closure) { - twin_screen_t *screen = malloc (sizeof (twin_screen_t)); + twin_screen_t *screen = calloc (1, sizeof (twin_screen_t)); if (!screen) return 0; screen->top = 0; @@ -93,6 +93,15 @@ twin_screen_damage (twin_screen_t *screen, twin_coord_t left, twin_coord_t top, twin_coord_t right, twin_coord_t bottom) { + if (left < 0) + left = 0; + if (top < 0) + top = 0; + if (right > screen->width) + right = screen->width; + if (bottom > screen->height) + bottom = screen->height; + if (screen->damage.left == screen->damage.right) { screen->damage.left = left; @@ -131,6 +140,37 @@ twin_screen_damaged (twin_screen_t *screen) screen->damage.top < screen->damage.bottom); } +static void +twin_screen_span_pixmap(twin_screen_t *screen, twin_argb32_t *span, + twin_pixmap_t *p, twin_coord_t y, + twin_coord_t left, twin_coord_t right) +{ + twin_pointer_t dst; + twin_source_u src; + twin_coord_t p_left, p_right; + + /* bounds check in y */ + if (y < p->y) + return; + if (p->y + p->height <= y) + return; + /* bounds check in x*/ + p_left = left; + if (p_left < p->x) + p_left = p->x; + p_right = right; + if (p_right > p->x + p->width) + p_right = p->x + p->width; + if (p_left >= p_right) + return; + dst.argb32 = span + (p_left - left); + src.p = twin_pixmap_pointer (p, p_left - p->x, y - p->y); + if (p->format == TWIN_RGB16) + _twin_rgb16_source_argb32 (dst, src, p_right - p_left); + else + _twin_argb32_over_argb32 (dst, src, p_right - p_left); +} + void twin_screen_update (twin_screen_t *screen) { @@ -139,6 +179,11 @@ twin_screen_update (twin_screen_t *screen) twin_coord_t right = screen->damage.right; twin_coord_t bottom = screen->damage.bottom; + if (right > screen->width) + right = screen->width; + if (bottom > screen->height) + bottom = screen->height; + if (!screen->disable && left < right && top < bottom) { twin_argb32_t *span; @@ -181,33 +226,14 @@ twin_screen_update (twin_screen_t *screen) } else memset (span, 0xff, width * sizeof (twin_argb32_t)); + for (p = screen->bottom; p; p = p->up) - { - twin_pointer_t dst; - twin_source_u src; - twin_coord_t p_left, p_right; - - /* bounds check in y */ - if (y < p->y) - continue; - if (p->y + p->height <= y) - continue; - /* bounds check in x*/ - p_left = left; - if (p_left < p->x) - p_left = p->x; - p_right = right; - if (p_right > p->x + p->width) - p_right = p->x + p->width; - if (p_left >= p_right) - continue; - dst.argb32 = span + (p_left - left); - src.p = twin_pixmap_pointer (p, p_left - p->x, y - p->y); - if (p->format == TWIN_RGB16) - _twin_rgb16_source_argb32 (dst, src, p_right - p_left); - else - _twin_argb32_over_argb32 (dst, src, p_right - p_left); - } + twin_screen_span_pixmap(screen, span, p, y, left, right); + + if (screen->cursor) + twin_screen_span_pixmap(screen, span, screen->cursor, + y, left, right); + (*screen->put_span) (left, y, right, span, screen->closure); } free (span); @@ -253,6 +279,50 @@ twin_screen_get_background (twin_screen_t *screen) return screen->background; } +static void +twin_screen_damage_cursor(twin_screen_t *screen) +{ + twin_screen_damage (screen, + screen->cursor->x, + screen->cursor->y, + screen->cursor->x + screen->cursor->width, + screen->cursor->y + screen->cursor->height); +} + +void +twin_screen_set_cursor (twin_screen_t *screen, twin_pixmap_t *pixmap, + twin_fixed_t hotspot_x, twin_fixed_t hotspot_y) +{ + if (screen->cursor) { + twin_screen_damage_cursor(screen); + twin_pixmap_destroy(screen->cursor); + } + screen->cursor = pixmap; + screen->curs_hx = hotspot_x; + screen->curs_hy = hotspot_y; + pixmap->x = screen->curs_x - hotspot_x; + pixmap->y = screen->curs_y - hotspot_y; + if (pixmap) + twin_screen_damage_cursor(screen); +} + +static void +twin_screen_update_cursor(twin_screen_t *screen, + twin_coord_t x, twin_coord_t y) +{ + if (screen->cursor) + twin_screen_damage_cursor(screen); + + screen->curs_x = x; + screen->curs_y = y; + + if (screen->cursor) { + screen->cursor->x = screen->curs_x - screen->curs_hx; + screen->cursor->y = screen->curs_y - screen->curs_hy; + twin_screen_damage_cursor(screen); + } +} + twin_bool_t twin_screen_dispatch (twin_screen_t *screen, twin_event_t *event) @@ -263,6 +333,8 @@ twin_screen_dispatch (twin_screen_t *screen, case TwinEventMotion: case TwinEventButtonDown: case TwinEventButtonUp: + twin_screen_update_cursor(screen, event->u.pointer.screen_x, + event->u.pointer.screen_y); pixmap = screen->pointer; if (!pixmap) { |