diff options
author | Marco Gerards <marco@gnu.org> | 2005-01-06 21:43:53 +0000 |
---|---|---|
committer | Marco Gerards <marco@gnu.org> | 2005-01-06 21:43:53 +0000 |
commit | 38fc4e4d1cef98a8b3d60b717647a9c65d91307f (patch) | |
tree | 805792e935ebbf353301070209d0af1aea1d908f /console-client | |
parent | 4456094d61a6f5baabadc18096fc1f516921b736 (diff) |
2005-01-06 Marco Gerards <metgerards@student.han.nl>
* Makefile (SRCS): Add `trans.c'.
(LCLHDRS): Add `mach-inputdev.h'.
(HURDLIBS): Add `netfs', `fshelp' and `iohelp'.
(modules): Add `pc_mouse'.
(pc_kbd.so.$(hurd-version)): Add `kdioctlServer.o' and
`kbd-repeat.c'.
(pc_mouse.so.$(hurd-version)): New variable.
* console.c: Include <trans.h>.
(DEFAULT_CONSOLE_NODE): New macro.
(saved_id, saved_cons, consnode_path): New variables.
(console_move_mouse): New function.
(console_switch_away): New function.
(console_switch_back): Likewise.
(cons_vcons_set_mousecursor_pos): Likewise.
(cons_vcons_set_mousecursor_status): Likewise.
(options): Add the option `--console-node'.
(parse_opt): Parse the options that were added to `options'.
(main): Setup the console client translator node.
* display.h (display_ops): New members `set_mousecursor_pos' and
`set_mousecursor_status'.
* driver.c (driver_start): Change the type of `i' to `unsigned
int'.
* driver.h (driver_ops): New members `save_status' and
`restore_status'.
* input.h (console_switch_away): New prototype.
(console_switch_back): Likewise.
(console_move_mouse): Likewise.
* kbd-repeat.c: New file.
* mach-inputdev.h: Likewise.
* pc-mouse.c: Likewise.
* trans.c: Likewise.
* trans.h: Likewise.
* pc-kbd.c: Include <argp.h> and "mach-inputdev.h".
(DEFAULT_REPEATER_NODE): New macro.
(repeater_node, cnode): New variables.
(kev_type, mouse_motion, Scancode, m_deltaX, m_deltaY, MOUSE_LEFT)
(MOUSE_MIDDLE, MOUSE_RIGHT, MOUSE_MOTION, KEYBD_EVENT)
(IOCPARM_MASK, IOC_OUT, IOC_IN, _IOC, _IOR, _IOW, KDSKBDMODE,
(KB_EVENT, KB_ASCII, KDGKBDTYPE, KB_VANILLAKB, KDSETLEDS):
Removed.
(gnumach_v1_input_next): Call the repeater when repeating is
active.
(doc, options, argp): New variables.
(parse_opt): New function.
(pc_kbd_init): Function rewritten.
(pc_kbd_start): Initialize the repeater, when it is active.
(pc_kbd_fini): Destroy the console node.
* vga.c (vga_mousecursor): New struct.
(vga_mousecursor_t): New type.
(mousecursor): New variable.
(hide_mousecursor): New function.
(draw_mousecursor): Likewise.
(vga_display_restore_status): Likewise.
(vga_display_update): Likewise.
(vga_set_mousecursor_pos): Likewise.
(vga_set_mousecursor_status): Likewise.
(vga_display_scroll): Update the mousecursor state.
(driver_vga_ops): Add `vga_display_restore_status'.
(vga_display_op): Add `vga_display_update', `vga_set_mousecursor_pos'
and `vga_set_mousecursor_status'.
Diffstat (limited to 'console-client')
-rw-r--r-- | console-client/ChangeLog | 63 | ||||
-rw-r--r-- | console-client/Makefile | 13 | ||||
-rw-r--r-- | console-client/console.c | 140 | ||||
-rw-r--r-- | console-client/display.h | 10 | ||||
-rw-r--r-- | console-client/driver.c | 7 | ||||
-rw-r--r-- | console-client/driver.h | 9 | ||||
-rw-r--r-- | console-client/input.h | 15 | ||||
-rw-r--r-- | console-client/kbd-repeat.c | 258 | ||||
-rw-r--r-- | console-client/mach-inputdev.h | 138 | ||||
-rw-r--r-- | console-client/pc-kbd.c | 153 | ||||
-rw-r--r-- | console-client/pc-mouse.c | 509 | ||||
-rw-r--r-- | console-client/trans.c | 806 | ||||
-rw-r--r-- | console-client/trans.h | 75 | ||||
-rw-r--r-- | console-client/vga.c | 124 |
14 files changed, 2221 insertions, 99 deletions
diff --git a/console-client/ChangeLog b/console-client/ChangeLog index 3064e4a0..11e21643 100644 --- a/console-client/ChangeLog +++ b/console-client/ChangeLog @@ -1,3 +1,66 @@ +2005-01-06 Marco Gerards <metgerards@student.han.nl> + + * Makefile (SRCS): Add `trans.c'. + (LCLHDRS): Add `mach-inputdev.h'. + (HURDLIBS): Add `netfs', `fshelp' and `iohelp'. + (modules): Add `pc_mouse'. + (pc_kbd.so.$(hurd-version)): Add `kdioctlServer.o' and + `kbd-repeat.c'. + (pc_mouse.so.$(hurd-version)): New variable. + * console.c: Include <trans.h>. + (DEFAULT_CONSOLE_NODE): New macro. + (saved_id, saved_cons, consnode_path): New variables. + (console_move_mouse): New function. + (console_switch_away): New function. + (console_switch_back): Likewise. + (cons_vcons_set_mousecursor_pos): Likewise. + (cons_vcons_set_mousecursor_status): Likewise. + (options): Add the option `--console-node'. + (parse_opt): Parse the options that were added to `options'. + (main): Setup the console client translator node. + * display.h (display_ops): New members `set_mousecursor_pos' and + `set_mousecursor_status'. + * driver.c (driver_start): Change the type of `i' to `unsigned + int'. + * driver.h (driver_ops): New members `save_status' and + `restore_status'. + * input.h (console_switch_away): New prototype. + (console_switch_back): Likewise. + (console_move_mouse): Likewise. + * kbd-repeat.c: New file. + * mach-inputdev.h: Likewise. + * pc-mouse.c: Likewise. + * trans.c: Likewise. + * trans.h: Likewise. + * pc-kbd.c: Include <argp.h> and "mach-inputdev.h". + (DEFAULT_REPEATER_NODE): New macro. + (repeater_node, cnode): New variables. + (kev_type, mouse_motion, Scancode, m_deltaX, m_deltaY, MOUSE_LEFT) + (MOUSE_MIDDLE, MOUSE_RIGHT, MOUSE_MOTION, KEYBD_EVENT) + (IOCPARM_MASK, IOC_OUT, IOC_IN, _IOC, _IOR, _IOW, KDSKBDMODE, + (KB_EVENT, KB_ASCII, KDGKBDTYPE, KB_VANILLAKB, KDSETLEDS): + Removed. + (gnumach_v1_input_next): Call the repeater when repeating is + active. + (doc, options, argp): New variables. + (parse_opt): New function. + (pc_kbd_init): Function rewritten. + (pc_kbd_start): Initialize the repeater, when it is active. + (pc_kbd_fini): Destroy the console node. + * vga.c (vga_mousecursor): New struct. + (vga_mousecursor_t): New type. + (mousecursor): New variable. + (hide_mousecursor): New function. + (draw_mousecursor): Likewise. + (vga_display_restore_status): Likewise. + (vga_display_update): Likewise. + (vga_set_mousecursor_pos): Likewise. + (vga_set_mousecursor_status): Likewise. + (vga_display_scroll): Update the mousecursor state. + (driver_vga_ops): Add `vga_display_restore_status'. + (vga_display_op): Add `vga_display_update', `vga_set_mousecursor_pos' + and `vga_set_mousecursor_status'. + 2004-11-15 Marcus Brinkmann <marcus@gnu.org> * pc-kbd.c (input_loop): Make the left shift key behave the same diff --git a/console-client/Makefile b/console-client/Makefile index b0f33907..1a8bbe28 100644 --- a/console-client/Makefile +++ b/console-client/Makefile @@ -1,5 +1,5 @@ # -# Copyright (C) 1994,95,96,97,98,99,2000,01,02,04 Free Software Foundation, Inc. +# Copyright (C) 1994,95,96,97,98,99,2000,01,02,04,2005 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -19,13 +19,13 @@ dir := console-client makemode := utility target = console -SRCS = console.c timer.c driver.c +SRCS = console.c timer.c driver.c trans.c LCLHDRS = timer.h driver.h display.h input.h bell.h \ - unicode.h bdf.h \ + unicode.h bdf.h mach-inputdev.h \ vga-dynafont.h vga-dynacolor.h vga-hw.h vga.h OBJS = $(filter-out %.sh,$(SRCS:.c=.o)) -HURDLIBS = cons threads ports +HURDLIBS = cons threads ports netfs fshelp iohelp LDLIBS = -ldl module-dir = $(libdir)/hurd/console console-LDFLAGS = -Wl,-E @@ -41,12 +41,13 @@ driver-CPPFLAGS = -D'CONSOLE_DEFPATH="$(module-dir)\0"' \ console: ../libcons/libcons.a ../libports/libports.a \ ../libthreads/libthreads.a ../libshouldbeinlibc/libshouldbeinlibc.a -modules = vga pc_kbd generic_speaker +modules = vga pc_kbd generic_speaker pc_mouse vga-CPPFLAGS = -DDEFAULT_VGA_FONT_DIR=\"${datadir}/hurd/\" vga.so.$(hurd-version): $(patsubst %.c,%_pic.o,bdf.c vga-dynafont.c \ vga-dynacolor.c vga-support.c vga.c) -pc_kbd.so.$(hurd-version): $(patsubst %.c,%_pic.o,pc-kbd.c) +pc_kbd.so.$(hurd-version): $(patsubst %.c,%_pic.o,pc-kbd.c kdioctlServer.o kbd-repeat.c) +pc_mouse.so.$(hurd-version): $(patsubst %.c,%_pic.o,pc-mouse.c) generic_speaker.so.$(hurd-version): $(patsubst %.c,%_pic.o,generic-speaker.c) ifneq ($(LIBNCURSESW),) diff --git a/console-client/console.c b/console-client/console.c index 4bc9d6f4..ec518ddb 100644 --- a/console-client/console.c +++ b/console-client/console.c @@ -1,5 +1,5 @@ /* console.c -- A pluggable console client. - Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Written by Marcus Brinkmann. This program is free software; you can redistribute it and/or @@ -34,10 +34,14 @@ #include "driver.h" #include "timer.h" +#include "trans.h" const char *cons_client_name = "console"; const char *cons_client_version = HURD_VERSION; +/* The default node on which the console node is started. */ +#define DEFAULT_CONSOLE_NODE "/dev/cons" + /* The global lock protects the active_vcons variable, and thus all operations on the virtual console that is currently active. */ @@ -47,6 +51,15 @@ static struct mutex global_lock; displayed. */ static vcons_t active_vcons = NULL; +/* Contains the VT id when switched away. */ +static int saved_id = 0; + +/* The console, used to switch back. */ +static cons_t saved_cons; + +/* The pathname of the node on which the translator is set. */ +static char *consnode_path; + /* Callbacks for input source drivers. */ @@ -121,6 +134,37 @@ console_input (char *buf, size_t size) } +/* Report the mouse event EV to the currently active console. This + can be called by the input driver at any time. */ +error_t +console_move_mouse (mouse_event_t ev) +{ + error_t err; + vcons_t vcons; + + mutex_lock (&global_lock); + + vcons = active_vcons; + if (!vcons) + { + mutex_unlock (&global_lock); + return EINVAL; + } + ports_port_ref (vcons); + mutex_unlock (&global_lock); + + if (vcons) + { + err = cons_vcons_move_mouse (vcons, ev); + ports_port_deref (vcons); + } + + mutex_unlock (&global_lock); + + return 0; +} + + /* Scroll the active console by TYPE and VALUE as specified by cons_vcons_scrollback. */ int @@ -148,6 +192,62 @@ console_scrollback (cons_scroll_t type, float value) } +/* Switch away from the console an external use of the console like + XFree. */ +void +console_switch_away (void) +{ + mutex_lock (&global_lock); + + driver_iterate + if (driver->ops->save_status) + driver->ops->save_status (driver->handle); + + saved_id = active_vcons->id; + saved_cons = active_vcons->cons; + cons_vcons_close (active_vcons); + active_vcons = NULL; + mutex_unlock (&global_lock); +} + +/* Switch back to the console client from an external user of the + console like XFree. */ +void +console_switch_back (void) +{ + vcons_list_t conslist; + mutex_lock (&global_lock); + + driver_iterate + if (driver->ops->restore_status) + driver->ops->restore_status (driver->handle); + + if (saved_cons) + { + error_t err; + + err = cons_lookup (saved_cons, saved_id, 1, &conslist); + if (err) + { + mutex_unlock (&global_lock); + return; + } + + err = cons_vcons_open (saved_cons, conslist, &active_vcons); + if (err) + { + mutex_unlock (&global_lock); + return; + } + + conslist->vcons = active_vcons; + saved_cons = NULL; + mutex_unlock (&active_vcons->lock); + } + mutex_unlock (&global_lock); +} + + /* Exit the console client. Does not return. */ void console_exit (void) @@ -371,6 +471,33 @@ cons_vcons_set_dimension (vcons_t vcons, uint32_t col, uint32_t row) return 0; } + +error_t +cons_vcons_set_mousecursor_pos (vcons_t vcons, float x, float y) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + display_iterate + if (display->ops->set_mousecursor_pos) + display->ops->set_mousecursor_pos (display->handle, x, y); + mutex_unlock (&global_lock); + return 0; +} + + +error_t +cons_vcons_set_mousecursor_status (vcons_t vcons, int status) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + display_iterate + if (display->ops->set_mousecursor_status) + display->ops->set_mousecursor_status (display->handle, status); + mutex_unlock (&global_lock); + return 0; + +} + /* Console-specific options. */ static const struct argp_option @@ -378,6 +505,8 @@ options[] = { {"driver-path", 'D', "PATH", 0, "Specify search path for driver modules" }, {"driver", 'd', "NAME", 0, "Add driver NAME to the console" }, + {"console-node", 'c', "FILE", OPTION_ARG_OPTIONAL, + "Set a translator on the node FILE (default: " DEFAULT_CONSOLE_NODE ")" }, {0} }; @@ -426,6 +555,12 @@ parse_opt (int key, char *arg, struct argp_state *state) devcount++; break; + case 'c': + consnode_path = arg ? arg : DEFAULT_CONSOLE_NODE; + if (!consnode_path) + return ENOMEM; + break; + case ARGP_KEY_SUCCESS: if (!devcount) { @@ -477,6 +612,9 @@ main (int argc, char *argv[]) error (1, err, "Timer thread initialization failed"); } + if (consnode_path) + console_setup_node (consnode_path); + cons_server_loop (); /* Never reached. */ diff --git a/console-client/display.h b/console-client/display.h index 01ddd733..1e73bdee 100644 --- a/console-client/display.h +++ b/console-client/display.h @@ -1,5 +1,5 @@ /* display.h - The interface to and for a display driver. - Copyright (C) 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Written by Marcus Brinkmann. This file is part of the GNU Hurd. @@ -141,6 +141,14 @@ struct display_ops write that covers the whole new screen. */ error_t (*set_dimension) (void *handle, unsigned int width, unsigned int height); + + /* Move the mouse cursor to the position X, Y. If the mouse cursor + is visible, update its position. */ + error_t (*set_mousecursor_pos) (void *handle, float x, float y); + + /* If STATUS is set to 0, hide the mouse cursor, otherwise show + it. */ + error_t (*set_mousecursor_status) (void *handle, int status); }; #endif /* _DISPLAY_H_ */ diff --git a/console-client/driver.c b/console-client/driver.c index 0c1834d2..2a56729d 100644 --- a/console-client/driver.c +++ b/console-client/driver.c @@ -1,5 +1,5 @@ /* driver.c - The console client driver code. - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. Written by Marcus Brinkmann. This file is part of the GNU Hurd. @@ -248,8 +248,8 @@ error_t driver_start (char **name) { error_t err = 0; - unsigned int i; - + int i; + mutex_lock (&driver_list_lock); for (i = 0; i < driver_list_len; i++) { @@ -304,7 +304,6 @@ error_t driver_remove (const char *const name) return ESRCH; } - #define ADD_REMOVE_COMPONENT(component) \ struct mutex component##_list_lock; \ component##_t component##_list; \ diff --git a/console-client/driver.h b/console-client/driver.h index ac8f5c98..aacd5890 100644 --- a/console-client/driver.h +++ b/console-client/driver.h @@ -1,5 +1,5 @@ /* driver.h - The interface to and for a console client driver. - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. Written by Marcus Brinkmann. This file is part of the GNU Hurd. @@ -158,6 +158,13 @@ struct driver_ops The function is allowed to fail if FORCE is 0. If FORCE is not 0, the driver should remove itself no matter what. */ error_t (*fini) (void *handle, int force); + + + /* Save the status of the hardware. */ + void (*save_status) (void *handle); + + /* Restore the status of the hardware. */ + void (*restore_status) (void *handle); }; diff --git a/console-client/input.h b/console-client/input.h index 38c2a64f..e5683a8e 100644 --- a/console-client/input.h +++ b/console-client/input.h @@ -1,5 +1,5 @@ /* input.h - The interface to and for an input driver. - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. Written by Marcus Brinkmann. This file is part of the GNU Hurd. @@ -65,6 +65,19 @@ void console_error (const wchar_t *const err_msg); /* Exit the console client. Does not return. */ void console_exit (void); +/* Switch away from the console an external use of the console like + XFree. */ +void console_switch_away (void); + +/* Switch back to the console client from an external user of the + console like XFree. */ +void console_switch_back (void); + +/* Report the mouse event EV to the currently active console. This + can be called by the input driver at any time. */ +error_t console_move_mouse (mouse_event_t ev); + + #if QUAERENDO_INVENIETIS /* Do not use, do not remove. */ void console_deprecated (int key); diff --git a/console-client/kbd-repeat.c b/console-client/kbd-repeat.c new file mode 100644 index 00000000..4bf9268e --- /dev/null +++ b/console-client/kbd-repeat.c @@ -0,0 +1,258 @@ +/* kbd-repeat.c - Keyboard repeater. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Written by Marco Gerards. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <hurd/netfs.h> +#include <stdlib.h> +#include <error.h> +#include <string.h> +#include <fcntl.h> +#include <sys/mman.h> + +#include "kdioctl_S.h" +#include "mach-inputdev.h" +#include "input.h" + +/* The amount of keyboard events that can be stored in the keyboard buffer. */ +#define KBDEVTBUFSZ 20 + +/* The size of the keyboard buffer in bytes. */ +#define KBDBUFSZ (KBDEVTBUFSZ * sizeof (kd_event)) + +/* Return the position of X in the buffer. */ +#define KBDBUF_POS(x) ((x) % KBDBUFSZ) + +/* The keyboard buffer. */ +static struct kbdbuf +{ + char keybuffer[KBDBUFSZ]; + int pos; + size_t size; + struct condition readcond; + struct condition writecond; +} kbdbuf; + +/* Wakeup for select */ +static struct condition select_alert; + +/* The global lock */ +static struct mutex global_lock; + +/* Amount of times the device was opened. Normally this translator + should be only opened once. */ +int kbd_repeater_opened; + + +/* Place the keyboard event KEY in the keyboard buffer. */ +void +kbd_repeat_key (kd_event *key) +{ + kd_event *ev; + + mutex_lock (&global_lock); + while (kbdbuf.size + sizeof (kd_event) > KBDBUFSZ) + { + /* The input buffer is full, wait until there is some space. */ + if (hurd_condition_wait (&kbdbuf.writecond, &global_lock)) + { + mutex_unlock (&global_lock); + /* Interrupt, silently continue. */ + } + } + ev = (kd_event *) &kbdbuf.keybuffer[KBDBUF_POS (kbdbuf.pos + + kbdbuf.size)]; + kbdbuf.size += sizeof (kd_event); + memcpy (ev, key, sizeof (kd_event)); + + condition_broadcast (&kbdbuf.readcond); + mutex_unlock (&global_lock); +} + + +static error_t +repeater_select (struct protid *cred, mach_port_t reply, + mach_msg_type_name_t replytype, int *type) +{ + if (!cred) + return EOPNOTSUPP; + + if (*type & ~SELECT_READ) + return EINVAL; + + if (*type == 0) + return 0; + + mutex_lock (&global_lock); + while (1) + { + if (kbdbuf.size > 0) + { + *type = SELECT_READ; + mutex_unlock (&global_lock); + + return 0; + } + + ports_interrupt_self_on_port_death (cred, reply); + if (hurd_condition_wait (&select_alert, &global_lock)) + { + *type = 0; + mutex_unlock (&global_lock); + + return EINTR; + } + } +} + + +static error_t +repeater_read (struct protid *cred, char **data, + mach_msg_type_number_t *datalen, off_t offset, + mach_msg_type_number_t amount) +{ + /* Deny access if they have bad credentials. */ + if (! cred) + return EOPNOTSUPP; + else if (! (cred->po->openstat & O_READ)) + return EBADF; + + mutex_lock (&global_lock); + while (amount > kbdbuf.size) + { + if (cred->po->openstat & O_NONBLOCK && amount > kbdbuf.size) + { + mutex_unlock (&global_lock); + return EWOULDBLOCK; + } + + if (hurd_condition_wait (&kbdbuf.readcond, &global_lock)) + { + mutex_unlock (&global_lock); + return EINTR; + } + } + + if (amount > 0) + { + char *keydata; + unsigned int i = 0; + + /* Allocate a buffer when this is required. */ + if (*datalen < amount) + { + *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (*data == MAP_FAILED) + { + mutex_unlock (&global_lock); + return ENOMEM; + } + } + + /* Copy the bytes to the user's buffer and remove them from the + keyboard buffer. */ + keydata = *data; + while (i != amount) + { + keydata[i++] = kbdbuf.keybuffer[kbdbuf.pos++]; + kbdbuf.pos = KBDBUF_POS (kbdbuf.pos); + } + kbdbuf.size -= amount; + condition_broadcast (&kbdbuf.writecond); + } + + *datalen = amount; + mutex_unlock (&global_lock); + + return 0; +} + + +static void +repeater_open (void) +{ + /* Make sure the console does not access the hardware anymore. */ + if (! kbd_repeater_opened) + console_switch_away (); + kbd_repeater_opened++; +} + + +static void +repeater_close (void) +{ + kbd_repeater_opened--; + + /* Allow the console to access the hardware again. */ + if (! kbd_repeater_opened) + { + console_switch_back (); + kbdbuf.pos = 0; + kbdbuf.size = 0; + } +} + + +/* Set the repeater translator. The node will be named NODENAME and + NODE will be filled with information about this node. */ +error_t +kbd_setrepeater (const char *nodename, consnode_t *cn) +{ + extern int kdioctl_server (mach_msg_header_t *inp, mach_msg_header_t *outp); + error_t err; + + err = console_create_consnode (nodename, cn); + if (err) + return err; + + (*cn)->read = repeater_read; + (*cn)->write = 0; + (*cn)->select = repeater_select; + (*cn)->open = repeater_open; + (*cn)->close = repeater_close; + (*cn)->demuxer = kdioctl_server; + + mutex_init (&global_lock); + + condition_init (&kbdbuf.readcond); + condition_init (&kbdbuf.writecond); + + condition_init (&select_alert); + condition_implies (&kbdbuf.readcond, &select_alert); + + console_register_consnode (*cn); + + return 0; +} + + +/* Some RPC calls for controlling the keyboard. These calls are just + ignored and just exist to make XFree happy. */ + +kern_return_t +S_kdioctl_kdskbdmode (io_t port, int mode) +{ + return 0; +} + + +kern_return_t +S_kdioctl_kdgkbdmode (io_t port, int *mode) +{ + return 0; +} diff --git a/console-client/mach-inputdev.h b/console-client/mach-inputdev.h new file mode 100644 index 00000000..985e1e1d --- /dev/null +++ b/console-client/mach-inputdev.h @@ -0,0 +1,138 @@ +/* mach-inputdev.h - Interfaces for the PC pc-kbd and mouse input drivers. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Written by Marco Gerards. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* This gross stuff is cut & pasted from Mach sources, as Mach doesn't + export the interface we are using here. */ + +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef _INPUTDEV_H_ +#define _INPUTDEV_H_ 1 + +#include <trans.h> + +typedef u_short kev_type; /* kd event type */ + +/* (used for event records) */ +struct mouse_motion { + short mm_deltaX; /* units? */ + short mm_deltaY; +}; +typedef u_char Scancode; + +typedef struct { + kev_type type; /* see below */ + struct timeval time; /* timestamp */ + union { /* value associated with event */ + boolean_t up; /* MOUSE_LEFT .. MOUSE_RIGHT */ + Scancode sc; /* KEYBD_EVENT */ + struct mouse_motion mmotion; /* MOUSE_MOTION */ + } value; +} kd_event; +#define m_deltaX mmotion.mm_deltaX +#define m_deltaY mmotion.mm_deltaY + +/* + * kd_event ID's. + */ +#define MOUSE_LEFT 1 /* mouse left button up/down */ +#define MOUSE_MIDDLE 2 +#define MOUSE_RIGHT 3 +#define MOUSE_MOTION 4 /* mouse motion */ +#define KEYBD_EVENT 5 /* key up/down */ + + +#define IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000U /* copy in parameters */ + +#ifndef _IOC +#define _IOC(inout,group,num,len) \ + (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) +#endif +#ifndef _IOR +#define _IOR(g,n,t) _IOC(IOC_OUT, (g), (n), sizeof(t)) +#endif +#ifndef _IOW +#define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t)) +#endif + +#define KDSKBDMODE _IOW('K', 1, int) /* set keyboard mode */ +#define KB_EVENT 1 +#define KB_ASCII 2 + +#define KDGKBDTYPE _IOR('K', 2, int) /* get keyboard type */ +#define KB_VANILLAKB 0 + +#define KDSETLEDS _IOW('K', 5, int) /* set keyboard leds */ + +/* + * Low 3 bits of minor are the com port #. + * The high 5 bits of minor are the mouse type + */ +#define MOUSE_SYSTEM_MOUSE 0 +#define MICROSOFT_MOUSE 1 +#define IBM_MOUSE 2 +#define NO_MOUSE 3 +#define LOGITECH_TRACKMAN 4 +#define MICROSOFT_MOUSE7 5 + +#define DEV_COM0 "com0" +#define DEV_COM1 "com1" + +/* End of Mach code. */ + + +/* Amount of times the device was opened. Normally this translator + should be only opened once. */ +extern int kbd_repeater_opened; + +/* Place the keyboard event KEY in the keyboard buffer. */ +void kbd_repeat_key (kd_event *key); + +/* Set the repeater translator. The node will be named NODENAME and + NODE will be filled with information about this node. */ +error_t kbd_setrepeater (const char *nodename, consnode_t *node); + +#endif /* _INPUTDEV_H_ */ diff --git a/console-client/pc-kbd.c b/console-client/pc-kbd.c index 485eace1..35e25617 100644 --- a/console-client/pc-kbd.c +++ b/console-client/pc-kbd.c @@ -1,5 +1,5 @@ /* pc-kbd.c - The PC Keyboard input driver. - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. Written by Marcus Brinkmann. This file is part of the GNU Hurd. @@ -23,6 +23,7 @@ #include <string.h> #include <iconv.h> #include <sys/mman.h> +#include <argp.h> #include <device/device.h> #include <cthreads.h> @@ -31,8 +32,12 @@ #include <hurd/cons.h> #include "driver.h" +#include "mach-inputdev.h" +/* The default name of the node of the repeater. */ +#define DEFAULT_REPEATER_NODE "kbd" + /* The keyboard device in the kernel. */ static device_t kbd_dev; @@ -52,6 +57,11 @@ int gnumach_v1_compat; /* Forward declaration. */ static struct input_ops pc_kbd_ops; +/* The name of the repeater node. */ +static char *repeater_node; + +/* The repeater node. */ +static consnode_t cnode; /* A list of scan codes generated by the keyboard, in the set 2 encoding. */ enum scancode @@ -558,87 +568,6 @@ enum scancode sc_set1_to_set2_x1[] = SC_X1_DEL }; - -/* This gross stuff is cut & pasted from Mach sources, as Mach doesn't - export the interface we are using here. */ - -/* - * Mach Operating System - * Copyright (c) 1991,1990,1989 Carnegie Mellon University - * All Rights Reserved. - * - * Permission to use, copy, modify and distribute this software and its - * documentation is hereby granted, provided that both the copyright - * notice and this permission notice appear in all copies of the - * software, derivative works or modified versions, and any portions - * thereof, and that both notices appear in supporting documentation. - * - * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" - * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR - * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. - * - * Carnegie Mellon requests users of this software to return to - * - * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU - * School of Computer Science - * Carnegie Mellon University - * Pittsburgh PA 15213-3890 - * - * any improvements or extensions that they make and grant Carnegie Mellon - * the rights to redistribute these changes. - */ - -typedef u_short kev_type; /* kd event type */ - -/* (used for event records) */ -struct mouse_motion { - short mm_deltaX; /* units? */ - short mm_deltaY; -}; -typedef u_char Scancode; - -typedef struct { - kev_type type; /* see below */ - struct timeval time; /* timestamp */ - union { /* value associated with event */ - boolean_t up; /* MOUSE_LEFT .. MOUSE_RIGHT */ - Scancode sc; /* KEYBD_EVENT */ - struct mouse_motion mmotion; /* MOUSE_MOTION */ - } value; -} kd_event; -#define m_deltaX mmotion.mm_deltaX -#define m_deltaY mmotion.mm_deltaY - -/* - * kd_event ID's. - */ -#define MOUSE_LEFT 1 /* mouse left button up/down */ -#define MOUSE_MIDDLE 2 -#define MOUSE_RIGHT 3 -#define MOUSE_MOTION 4 /* mouse motion */ -#define KEYBD_EVENT 5 /* key up/down */ - - -#define IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */ -#define IOC_OUT 0x40000000 /* copy out parameters */ -#define IOC_IN 0x80000000U /* copy in parameters */ -#define _IOC(inout,group,num,len) \ - (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) -#define _IOR(g,n,t) _IOC(IOC_OUT, (g), (n), sizeof(t)) -#define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t)) - -#define KDSKBDMODE _IOW('K', 1, int) /* set keyboard mode */ -#define KB_EVENT 1 -#define KB_ASCII 2 - -#define KDGKBDTYPE _IOR('K', 2, int) /* get keyboard type */ -#define KB_VANILLAKB 0 - -#define KDSETLEDS _IOW('K', 5, int) /* set keyboard leds */ - - -/* End of Mach code. */ - static enum scancode gnumach_v1_input_next () { @@ -658,6 +587,13 @@ gnumach_v1_input_next () terminate. */ if (err) return 0; + + if (kbd_repeater_opened && data_buf.type == KEYBD_EVENT) + { + kbd_repeat_key (&data_buf); + data_buf.type = 0; + continue; + } } while (data_buf.type != KEYBD_EVENT); @@ -1090,10 +1026,56 @@ input_loop (any_t unused) + +static const char doc[] = "PC Keyboard Driver"; + +static const struct argp_option options[] = + { + {"repeat", 'r', "NODE", OPTION_ARG_OPTIONAL, + "Set a repeater translator on NODE (default: " DEFAULT_REPEATER_NODE ")"}, + { 0 } + }; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + int *pos = (int *) state->input; + + switch (key) + { + case 'r': + repeater_node = arg ? arg: DEFAULT_REPEATER_NODE; + break; + + case ARGP_KEY_END: + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + *pos = state->next; + return 0; +} + +static struct argp argp = {options, parse_opt, 0, doc}; + /* Initialize the PC keyboard driver. */ static error_t pc_kbd_init (void **handle, int no_exit, int argc, char *argv[], int *next) { + error_t err; + int pos = 1; + + /* Parse the arguments. */ + err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT + | ARGP_SILENT, 0 , &pos); + *next += pos - 1; + + if (err && err != EINVAL) + return err; + + return 0; } @@ -1158,6 +1140,10 @@ pc_kbd_start (void *handle) iconv_close (cd); return err; } + + if (repeater_node) + kbd_setrepeater (repeater_node, &cnode); + cthread_detach (cthread_fork (input_loop, NULL)); return 0; @@ -1177,6 +1163,9 @@ pc_kbd_fini (void *handle, int force) mach_port_deallocate (mach_task_self (), kbd_dev); iconv_close (cd); + console_unregister_consnode (cnode); + console_destroy_consnode (cnode); + return 0; } diff --git a/console-client/pc-mouse.c b/console-client/pc-mouse.c new file mode 100644 index 00000000..cf8987bf --- /dev/null +++ b/console-client/pc-mouse.c @@ -0,0 +1,509 @@ +/* pc-mouse.c - Mouse driver. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Written by Marco Gerards. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <argp.h> +#include <hurd.h> +#include <hurd/ports.h> +#include <device/device.h> +#include <fcntl.h> +#include <sys/mman.h> +#include "driver.h" +#include "mach-inputdev.h" + +static struct input_ops pc_mouse_ops; + +/* Default to the protocol I use :). */ +static int majordev = IBM_MOUSE; +static int minordev = 0; + +static device_t mousedev; + + +/* The default name of the node of the repeater. */ +#define DEFAULT_REPEATER_NODE "mouse" + +/* The amount of mouse events that can be stored in the event buffer. */ +#define MOUSEDEVTBUFSZ 256 + +/* The size of the event buffer in bytes. */ +#define MOUSEBUFSZ (MOUSEDEVTBUFSZ * sizeof (kd_event)) + +/* Return the position of X in the buffer. */ +#define MOUSEBUF_POS(x) ((x) % MOUSEBUFSZ) + +/* The mouse sensitivity. */ +#define STRINGIFY(x) STRINGIFY_1(x) +#define STRINGIFY_1(x) #x +#define DEFAULT_MOUSE_SENS 1.0 +#define DEFAULT_MOUSE_SENS_STRING STRINGIFY(DEFAULT_MOUSE_SENS) + +/* The mouse event buffer. */ +static struct mousebuf +{ + char evtbuffer[MOUSEBUFSZ]; + int pos; + size_t size; + struct condition readcond; + struct condition writecond; +} mousebuf; + +/* Wakeup for select */ +static struct condition select_alert; + +/* The global lock */ +static struct mutex global_lock; + +/* Amount of times the device was opened. Normally this translator + should be only opened once. */ +static int mouse_repeater_opened; + +/* The name of the repeater node. */ +static char *repeater_node; + +/* The repeater node. */ +static consnode_t cnode; + +/* The mouse sensitivity. */ +float mouse_sens = DEFAULT_MOUSE_SENS; + +/* Place the mouse event EVNT in the mouse event buffer. */ +static void +repeat_event (kd_event *evt) +{ + kd_event *ev; + + mutex_lock (&global_lock); + while (mousebuf.size + sizeof (kd_event) > MOUSEBUFSZ) + { + /* The input buffer is full, wait until there is some space. */ + if (hurd_condition_wait (&mousebuf.writecond, &global_lock)) + { + mutex_unlock (&global_lock); + /* Interrupt, silently continue. */ + } + } + ev = (kd_event *) &mousebuf.evtbuffer[MOUSEBUF_POS (mousebuf.pos + + mousebuf.size)]; + mousebuf.size += sizeof (kd_event); + memcpy (ev, evt, sizeof (kd_event)); + + condition_broadcast (&mousebuf.readcond); + mutex_unlock (&global_lock); +} + + +static error_t +repeater_select (struct protid *cred, mach_port_t reply, + mach_msg_type_name_t replytype, int *type) +{ + if (!cred) + return EOPNOTSUPP; + + if (*type & ~SELECT_READ) + return EINVAL; + + if (*type == 0) + return 0; + + mutex_lock (&global_lock); + while (1) + { + if (mousebuf.size > 0) + { + *type = SELECT_READ; + mutex_unlock (&global_lock); + + return 0; + } + + ports_interrupt_self_on_port_death (cred, reply); + if (hurd_condition_wait (&select_alert, &global_lock)) + { + *type = 0; + mutex_unlock (&global_lock); + + return EINTR; + } + } +} + + +static void +repeater_open (void) +{ + mouse_repeater_opened++; +} + + +static void +repeater_close (void) +{ + mouse_repeater_opened--; + if (!mouse_repeater_opened) + { + mousebuf.pos = 0; + mousebuf.size = 0; + } +} + + +static error_t +repeater_read (struct protid *cred, char **data, + mach_msg_type_number_t *datalen, off_t offset, + mach_msg_type_number_t amount) +{ + /* Deny access if they have bad credentials. */ + if (! cred) + return EOPNOTSUPP; + else if (! (cred->po->openstat & O_READ)) + return EBADF; + + mutex_lock (&global_lock); + while (!mousebuf.size) + { + if (cred->po->openstat & O_NONBLOCK && mousebuf.size == 0) + { + mutex_unlock (&global_lock); + return EWOULDBLOCK; + } + + if (hurd_condition_wait (&mousebuf.readcond, &global_lock)) + { + mutex_unlock (&global_lock); + return EINTR; + } + } + + amount = (amount / sizeof (kd_event) - 1) * sizeof (kd_event); + if (amount > mousebuf.size) + amount = mousebuf.size; + + if (amount > 0) + { + char *mousedata; + unsigned int i = 0; + + /* Allocate a buffer when this is required. */ + if (*datalen < amount) + { + *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (*data == MAP_FAILED) + { + mutex_unlock (&global_lock); + return ENOMEM; + } + } + + /* Copy the bytes to the user's buffer and remove them from the + mouse events buffer. */ + mousedata = *data; + while (i != amount) + { + mousedata[i++] = mousebuf.evtbuffer[mousebuf.pos++]; + mousebuf.pos = MOUSEBUF_POS (mousebuf.pos); + } + mousebuf.size -= amount; + condition_broadcast (&mousebuf.writecond); + } + + *datalen = amount; + mutex_unlock (&global_lock); + + return 0; +} + + + +static any_t +input_loop (any_t unused) +{ + kd_event *ev; + vm_offset_t buf; + mach_msg_type_number_t buf_size; + + while (1) + { + struct mouse_event evt = { 0 }; + device_read (mousedev, 0, 0, sizeof (kd_event), + (char **) &buf, &buf_size); + ev = (kd_event *) buf; + + /* The repeater is set, send the event to the repeater. */ + if (mouse_repeater_opened) + { + repeat_event (ev); + vm_deallocate (mach_task_self(), buf, buf_size); + continue; + } + + evt.mouse_movement = CONS_VCONS_MOUSE_MOVE_REL; + + switch (ev->type) + { + case MOUSE_LEFT: + evt.button = CONS_MOUSE_BUTTON1; + break; + case MOUSE_MIDDLE: + evt.button = CONS_MOUSE_BUTTON2; + break; + case MOUSE_RIGHT: + evt.button = CONS_MOUSE_BUTTON3; + break; + + case MOUSE_MOTION: + evt.x = ev->value.mmotion.mm_deltaX * mouse_sens; + evt.y = -ev->value.mmotion.mm_deltaY * mouse_sens; + break; + } + + if (ev->type > 0 && ev->type <= 3) + { + if (ev->value.up) + evt.mouse_button = CONS_VCONS_MOUSE_BUTTON_RELEASED; + else + evt.mouse_button = CONS_VCONS_MOUSE_BUTTON_PRESSED; + } + + /* Generate a mouse movement event. */ + console_move_mouse (&evt); + vm_deallocate (mach_task_self(), buf, buf_size); + } +} + + +#define PROTO_MOUSESYSTEM "mousesystem" +#define PROTO_MICROSOFT "microsoft" +#define PROTO_PS2 "ps/2" +#define PROTO_NOMOUSE "nomouse" +#define PROTO_LOGITECH "logitech" +#define PROTO_MOUSE7 "mouse7" + +/* The supported mouse protocols. Be careful with adding more, the + protocols are carefully ordered so the index is the major device + number. */ +static char *mouse_protocols[] = + { + PROTO_MOUSESYSTEM, + PROTO_MICROSOFT, + PROTO_PS2, + PROTO_NOMOUSE, + PROTO_LOGITECH, + PROTO_MOUSE7 + }; + +static const char doc[] = "Mouse Driver"; + +static const struct argp_option options[] = + { + { "protocol", 'p', "PROTOCOL", 0, "One of the protocols: " + PROTO_MOUSESYSTEM ", " PROTO_MICROSOFT ", " PROTO_PS2 ", " + PROTO_NOMOUSE ", " PROTO_LOGITECH ", " PROTO_MOUSE7 }, + { "device", 'e', "DEVICE" , 0, + "One of the devices: " DEV_COM0 ", " DEV_COM1 }, + { "sensitivity", 's', "SENSITIVITY", 0, "The mouse" + " sensitivity (default " DEFAULT_MOUSE_SENS_STRING "). A lower value" + " means more sensitive" }, + { "repeat", 'r', "NODE", OPTION_ARG_OPTIONAL, + "Set a repeater translator on NODE (default: " DEFAULT_REPEATER_NODE ")"}, + { 0 } + }; + +static error_t setrepeater (const char *nodename); + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + int *pos = (int *) state->input; + + switch (key) + { + case 'p': + { + unsigned int i; + + for (i = 0; i < (sizeof (mouse_protocols) / sizeof (char *)); i++) + { + if (!strcasecmp (arg, mouse_protocols[i])) + { + majordev = i; + *pos = state->next; + return 0; + } + } + fprintf (stderr, "Unknown protocol `%s'\n", arg); + argp_usage (state); + return ARGP_ERR_UNKNOWN; + } + + case 'e': + { + if (!strcasecmp (DEV_COM0, arg)) + minordev = 0; + else if (!strcasecmp (DEV_COM1, arg)) + minordev = 1; + else + { + fprintf (stderr, "Unknown device `%s'\n", arg); + argp_usage (state); + return ARGP_ERR_UNKNOWN; + } + break; + } + + case 'r': + repeater_node = arg ? arg : DEFAULT_REPEATER_NODE; + break; + + case 's': + { + char *tail; + + errno = 0; + mouse_sens = strtod (arg, &tail); + if (tail == NULL || tail == arg || *tail != '\0') + argp_error (state, "SENSITIVITY is not a number: %s", arg); + if (errno) + argp_error (state, "Overflow in argument SENSITIVITY %s", arg); + break; + } + + case ARGP_KEY_END: + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + *pos = state->next; + return 0; +} + + +static struct argp argp = {options, parse_opt, 0, doc}; + +static error_t +pc_mouse_init (void **handle, int no_exit, int argc, char *argv[], int *next) +{ + error_t err; + int pos = 1; + + /* Parse the arguments. */ + err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT + | ARGP_SILENT, 0, &pos); + *next += pos - 1; + if (err && err != EINVAL) + return err; + + return 0; +} + + +static error_t +pc_mouse_start (void *handle) +{ + error_t err; + char device_name[9]; + int devnum = majordev << 3 | minordev; + device_t device_master; + + sprintf (device_name, "mouse%d", devnum); + err = get_privileged_ports (0, &device_master); + if (err) + return err; + + err = device_open (device_master, D_READ, device_name, &mousedev); + mach_port_deallocate (mach_task_self (), device_master); + if (err) + return ENODEV; + + err = driver_add_input (&pc_mouse_ops, NULL); + if (err) + { + device_close (mousedev); + mach_port_deallocate (mach_task_self (), mousedev); + + return err; + } + + cthread_detach (cthread_fork (input_loop, NULL)); + + if (repeater_node) + setrepeater (repeater_node); + + return 0; +} + + +static error_t +pc_mouse_fini (void *handle, int force) +{ + device_close (mousedev); + mach_port_deallocate (mach_task_self (), mousedev); + console_unregister_consnode (cnode); + console_destroy_consnode (cnode); + + return 0; +} + + + +struct driver_ops driver_pc_mouse_ops = + { + pc_mouse_init, + pc_mouse_start, + pc_mouse_fini + }; + +static struct input_ops pc_mouse_ops = + { + NULL, + NULL + }; + + +/* Set make repeater translator node named NODENAME. */ +static error_t +setrepeater (const char *nodename) +{ + error_t err; + + err = console_create_consnode (nodename, &cnode); + if (err) + return err; + + cnode->read = repeater_read; + cnode->write = 0; + cnode->select = repeater_select; + cnode->open = repeater_open; + cnode->close = repeater_close; + cnode->demuxer = 0; + + mutex_init (&global_lock); + + condition_init (&mousebuf.readcond); + condition_init (&mousebuf.writecond); + + condition_init (&select_alert); + condition_implies (&mousebuf.readcond, &select_alert); + + console_register_consnode (cnode); + + return 0; +} diff --git a/console-client/trans.c b/console-client/trans.c new file mode 100644 index 00000000..83342bcc --- /dev/null +++ b/console-client/trans.c @@ -0,0 +1,806 @@ +/* trans.c -- Control a translator node for the repeaters. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Written by Marco Gerards. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <fcntl.h> +#include <maptime.h> +#include <stddef.h> +#include <dirent.h> +#include <sys/mman.h> +#include <unistd.h> +#include <hurd/hurd_types.h> +#include <error.h> +#include <version.h> + +#include "trans.h" + + +char *netfs_server_name = "console"; +char *netfs_server_version = HURD_VERSION; +int netfs_maxsymlinks = 0; + +/* Handy source of time. */ +static volatile struct mapped_time_value *console_maptime; + +static consnode_t node_list = 0; + +struct netnode +{ + consnode_t node; +}; + +typedef mach_msg_header_t request_t; + + +int +console_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + int ret; + struct protid *user = (struct protid *) inp; + request_t *inop = (request_t *) inp; + + ret = netfs_demuxer (inp, outp); + if (ret) + return ret; + + user = ports_lookup_port (netfs_port_bucket, inop->msgh_local_port, netfs_protid_class); + if (!user) + return ret; + + /* Don't do anything for the root node. */ + if (user->po->np == netfs_root_node) + { + ports_port_deref (user); + return 0; + } + + if (!ret && user->po->np->nn->node->demuxer) + ret = user->po->np->nn->node->demuxer (inp, outp); + + ports_port_deref (user); + return ret; +} + + + +/* Make sure that NP->nn_stat is filled with current information. CRED + identifies the user responsible for the operation. */ +error_t +netfs_validate_stat (struct node *np, struct iouser *cred) +{ + return 0; +} + + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the owner to UID and the group to GID. */ +error_t +netfs_attempt_chown (struct iouser *cred, struct node *np, + uid_t uid, uid_t gid) +{ + return EOPNOTSUPP; +} + + +/* This should attempt a chauthor call for the user specified by CRED on node + NODE, to change the author to AUTHOR. */ +error_t +netfs_attempt_chauthor (struct iouser *cred, struct node *np, + uid_t author) +{ + return EOPNOTSUPP; +} + + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning + of chmod, this function is also used to attempt to change files into other + types. If such a transition is attempted which is impossible, then return + EOPNOTSUPP. */ +error_t +netfs_attempt_chmod (struct iouser *cred, struct node *np, + mode_t mode) +{ + return EOPNOTSUPP; +} + + +/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */ +error_t +netfs_attempt_mksymlink (struct iouser *cred, struct node *np, + char *name) +{ + return EOPNOTSUPP; +} + + +/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or + S_IFCHR. */ +error_t +netfs_attempt_mkdev (struct iouser *cred, struct node *np, + mode_t type, dev_t indexes) +{ + return EOPNOTSUPP; +} + + +/* This should attempt a chflags call for the user specified by CRED on node + NODE, to change the flags to FLAGS. */ +error_t +netfs_attempt_chflags (struct iouser *cred, struct node *np, + int flags) +{ + return EOPNOTSUPP; +} + + +/* This should attempt a utimes call for the user specified by CRED on + locked node NP, to change the atime to ATIME and the mtime to + MTIME. If ATIME or MTIME is null, then set to the current + time. */ +error_t +netfs_attempt_utimes (struct iouser *cred, struct node *np, + struct timespec *atime, struct timespec *mtime) +{ + error_t err = fshelp_isowner (&np->nn_stat, cred); + int flags = TOUCH_CTIME; + + if (! err) + { + if (mtime) + { + np->nn_stat.st_mtime = mtime->tv_sec; + np->nn_stat.st_mtime_usec = mtime->tv_nsec / 1000; + } + else + flags |= TOUCH_MTIME; + + if (atime) + { + np->nn_stat.st_atime = atime->tv_sec; + np->nn_stat.st_atime_usec = atime->tv_nsec / 1000; + } + else + flags |= TOUCH_ATIME; + + fshelp_touch (&np->nn_stat, flags, console_maptime); + } + return err; + +} + + +/* This should attempt to set the size of the locked file NP (for user + CRED) to SIZE bytes long. */ +error_t +netfs_attempt_set_size (struct iouser *cred, struct node *np, + loff_t size) +{ + return EOPNOTSUPP; +} + +/* This should attempt to fetch filesystem status information for the + remote filesystem, for the user CRED. NP is locked. */ +error_t +netfs_attempt_statfs (struct iouser *cred, struct node *np, + fsys_statfsbuf_t *st) +{ + return EOPNOTSUPP; +} + + +/* This should sync the locked file NP completely to disk, for the + user CRED. If WAIT is set, return only after the sync is + completely finished. */ +error_t +netfs_attempt_sync (struct iouser *cred, struct node *np, + int wait) +{ + return 0; +} + + +/* This should sync the entire remote filesystem. If WAIT is set, + return only after the sync is completely finished. */ +error_t +netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + return 0; +} + + +/* Lookup NAME in DIR (which is locked) for USER; set *NP to the found + name upon return. If the name was not found, then return ENOENT. + On any error, clear *NP. (*NP, if found, should be locked and a + reference to it generated. This call should unlock DIR no matter + what.) */ +error_t +netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **node) +{ + error_t err; + consnode_t cn; + + *node = 0; + err = fshelp_access (&dir->nn_stat, S_IEXEC, user); + if (err) + goto out; + + if (strcmp (name, ".") == 0) + { + /* Current directory -- just add an additional reference to DIR + and return it. */ + netfs_nref (dir); + *node = dir; + goto out; + } + + if (strcmp (name, "..") == 0) + { + err = EAGAIN; + goto out; + } + + for (cn = node_list; cn; cn = cn->next) + if (!strcmp (name, cn->name)) + { + if (cn->node == NULL) + { + struct netnode *nn = calloc (1, sizeof *nn); + if (nn == NULL) + { + err = ENOMEM; + goto out; + } + + *node = netfs_make_node (nn); + + nn->node = cn; + (*node)->nn_stat = netfs_root_node->nn_stat; + (*node)->nn_stat.st_mode = S_IFCHR | (netfs_root_node->nn_stat.st_mode & ~S_IFMT & ~S_ITRANS); + (*node)->nn_stat.st_ino = 5; + cn->node = *node; + goto out; + } + else + { + *node = cn->node; + + netfs_nref (*node); + goto out; + } + } + + err = ENOENT; + + out: + mutex_unlock (&dir->lock); + if (err) + *node = 0; + else + mutex_lock (&(*node)->lock); + + if (!err && *node != dir && (*node)->nn->node->open) + (*node)->nn->node->open (); + + return err; +} + + +error_t +netfs_S_io_seek (struct protid *user, off_t offset, + int whence, off_t *newoffset) +{ + /* XXX: Will all nodes be device nodes? */ + if (!user) + return EOPNOTSUPP; + else + return ESPIPE; +} + + +error_t +netfs_S_io_select (struct protid *user, mach_port_t reply, + mach_msg_type_name_t replytype, int *type) +{ + struct node *np; + + if (!user) + return EOPNOTSUPP; + + np = user->po->np; + + if (np->nn->node->select) + return np->nn->node->select (user, reply, replytype, type); + return EOPNOTSUPP; +} + + +/* Delete NAME in DIR (which is locked) for USER. */ +error_t +netfs_attempt_unlink (struct iouser *user, struct node *dir, + char *name) +{ + return EOPNOTSUPP; +} + + +/* Attempt to rename the directory FROMDIR to TODIR. Note that neither + of the specific nodes are locked. */ +error_t +netfs_attempt_rename (struct iouser *user, struct node *fromdir, + char *fromname, struct node *todir, + char *toname, int excl) +{ + return EOPNOTSUPP; +} + + +/* Attempt to create a new directory named NAME in DIR (which is + locked) for USER with mode MODE. */ +error_t +netfs_attempt_mkdir (struct iouser *user, struct node *dir, + char *name, mode_t mode) +{ + return EOPNOTSUPP; +} + + +/* Attempt to remove directory named NAME in DIR (which is locked) for + USER. */ +error_t +netfs_attempt_rmdir (struct iouser *user, + struct node *dir, char *name) +{ + return EOPNOTSUPP; +} + + +/* Create a link in DIR with name NAME to FILE for USER. Note that + neither DIR nor FILE are locked. If EXCL is set, do not delete the + target. Return EEXIST if NAME is already found in DIR. */ +error_t +netfs_attempt_link (struct iouser *user, struct node *dir, + struct node *file, char *name, int excl) +{ + return EOPNOTSUPP; +} + + +/* Attempt to create an anonymous file related to DIR (which is + locked) for USER with MODE. Set *NP to the returned file upon + success. No matter what, unlock DIR. */ +error_t +netfs_attempt_mkfile (struct iouser *user, struct node *dir, + mode_t mode, struct node **np) +{ + return EOPNOTSUPP; +} + + +/* Attempt to create a file named NAME in DIR (which is locked) for + USER with MODE. Set *NP to the new node upon return. On any + error, clear *NP. *NP should be locked on success; no matter what, + unlock DIR before returning. */ +error_t +netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **np) +{ + *np = 0; + mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + + +/* Read the contents of locked node NP (a symlink), for USER, into + BUF. */ +error_t +netfs_attempt_readlink (struct iouser *user, struct node *np, + char *buf) +{ + return EOPNOTSUPP; +} + + +/* Locked node NP is being opened by USER, with FLAGS. NEWNODE is + nonzero if we just created this node. Return an error if we should + not permit the open to complete because of a permission + restriction. */ +error_t +netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + error_t err = 0; + + if (flags & O_READ) + err = fshelp_access (&np->nn_stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&np->nn_stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&np->nn_stat, S_IEXEC, user); + return err; + +} + + +/* This function will never be called. It is only used when a node is + a symlink or by io_read, which is overridden. */ +error_t +netfs_attempt_read (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + return EOPNOTSUPP; +} + + +/* This function will never be called. It is only called from + io_write, which is overridden. */ +error_t +netfs_attempt_write (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + return EOPNOTSUPP; +} + + +error_t +netfs_S_io_read (struct protid *user, + char **data, + mach_msg_type_number_t *datalen, + off_t offset, + mach_msg_type_number_t amount) +{ + struct node *np; + + if (!user) + return EOPNOTSUPP; + np = user->po->np; + + if (np->nn->node->read) + return np->nn->node->read (user, data, datalen, offset, amount); + return EOPNOTSUPP; +} + + +error_t +netfs_S_io_write (struct protid *user, + char *data, + mach_msg_type_number_t datalen, + off_t offset, + mach_msg_type_number_t *amount) +{ + struct node *np; + + if (!user) + return EOPNOTSUPP; + + np = user->po->np; + if (np->nn->node->read) + return np->nn->node->write (user, data, datalen, offset, amount); + return EOPNOTSUPP; +} + + +/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and + O_EXEC) in *TYPES for locked file NP and user CRED. */ +error_t +netfs_report_access (struct iouser *cred, struct node *np, + int *types) +{ + *types = 0; + if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + return 0; + +} + +/* Node NP has no more references; free all its associated storage. */ +void netfs_node_norefs (struct node *np) + +{ + if (np->nn->node->close) + np->nn->node->close (); + np->nn->node->node = 0; + + spin_unlock (&netfs_node_refcnt_lock); + free (np->nn); + free (np); +} + + +/* Returned directory entries are aligned to blocks this many bytes long. + Must be a power of two. */ +#define DIRENT_ALIGN 4 +#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name) +/* Length is structure before the name + the name + '\0', all + padded to a four-byte alignment. */ +#define DIRENT_LEN(name_len) \ + ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \ + & ~(DIRENT_ALIGN - 1)) + +/* Fill the array *DATA of size BUFSIZE with up to NENTRIES dirents + from DIR (which is locked) starting with entry ENTRY for user CRED. + The number of entries in the array is stored in *AMT and the number + of bytes in *DATACNT. If the supplied buffer is not large enough + to hold the data, it should be grown. */ +error_t +netfs_get_dirents (struct iouser *cred, struct node *dir, + int first_entry, int num_entries, char **data, + mach_msg_type_number_t *data_len, + vm_size_t max_data_len, int *data_entries) +{ + error_t err; + int count = 0; + size_t size = 0; /* Total size of our return block. */ + consnode_t cn = node_list; + consnode_t first_node; + + + /* Add the length of a directory entry for NAME to SIZE and return true, + unless it would overflow MAX_DATA_LEN or NUM_ENTRIES, in which case + return false. */ + int bump_size (const char *name) + { + if (num_entries == -1 || count < num_entries) + { + size_t new_size = size + DIRENT_LEN (strlen (name)); + if (max_data_len > 0 && new_size > max_data_len) + return 0; + size = new_size; + count++; + return 1; + } + else + return 0; + } + + if (dir != netfs_root_node) + return ENOTDIR; + + for (first_node = node_list, count = 2; + first_node && first_entry > count; + first_node = first_node->next); + count++; + + count = 0; + + /* Make space for the `.' and `..' entries. */ + if (first_entry == 0) + bump_size ("."); + if (first_entry <= 1) + bump_size (".."); + + for (cn = first_node; cn; cn = cn->next) + bump_size (cn->name); + + + /* Allocate it. */ + *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + err = ((void *) *data == (void *) -1) ? errno : 0; + + if (! err) + /* Copy out the result. */ + { + char *p = *data; + + int add_dir_entry (const char *name, ino_t fileno, int type) + { + if (num_entries == -1 || count < num_entries) + { + struct dirent hdr; + size_t name_len = strlen (name); + size_t sz = DIRENT_LEN (name_len); + + if (sz > size) + return 0; + else + size -= sz; + + hdr.d_fileno = fileno; + hdr.d_reclen = sz; + hdr.d_type = type; + hdr.d_namlen = name_len; + + memcpy (p, &hdr, DIRENT_NAME_OFFS); + strcpy (p + DIRENT_NAME_OFFS, name); + p += sz; + + count++; + + return 1; + } + else + return 0; + } + + *data_len = size; + *data_entries = count; + + count = 0; + + /* Add `.' and `..' entries. */ + if (first_entry == 0) + add_dir_entry (".", 2, DT_DIR); + if (first_entry <= 1) + add_dir_entry ("..", 2, DT_DIR); + + /* Fill in the real directory entries. */ + for (cn = first_node; cn; cn = cn->next) + if (!add_dir_entry (cn->name, cn->id, DT_CHR)) + break; + } + + fshelp_touch (&dir->nn_stat, TOUCH_ATIME, console_maptime); + return err; +} + + + +static any_t +console_client_translator (any_t unused) +{ + error_t err; + + do + { + ports_manage_port_operations_multithread (netfs_port_bucket, + console_demuxer, + 1000 * 60 * 2, + 1000 * 60 * 10, + 0); + err = netfs_shutdown (0); + } + while (err); + return 0; +} + + +/* Create a node with the name NAME and return it in *CN. */ +error_t +console_create_consnode (const char *name, consnode_t *cn) +{ + *cn = malloc (sizeof (struct consnode)); + if (!*cn) + return ENOMEM; + + (*cn)->name = strdup (name); + if (!(*cn)->name) + { + free (cn); + return ENOMEM; + } + + return 0; +} + + +/* Destroy the node CN. */ +void +console_destroy_consnode (consnode_t cn) +{ + if (!cn) + return; + free (cn->name); + free (cn); +} + + +/* Register the node CN with the translator. */ +void +console_register_consnode (consnode_t cn) +{ + cn->node = 0; + cn->next = node_list; + node_list = cn; +} + + +/* Unregister the node CN from the translator. */ +void +console_unregister_consnode (consnode_t cn) +{ + if (!cn) + return; + + if (node_list == cn) + node_list = cn->next; + else + { + consnode_t prev = node_list; + + for (prev = node_list; prev->next != cn; prev = prev->next) + ; + + prev->next = cn->next; + } +} + + +error_t +console_setup_node (char *path) +{ + mach_port_t underlying; + mach_port_t bootstrap; + error_t err; + struct stat ul_stat; + file_t node; + struct port_info *newpi; + mach_port_t right; + + node = file_name_lookup (path, O_CREAT|O_NOTRANS, 0664); + if (node == MACH_PORT_NULL) + return errno; + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + netfs_init (); + + /* Create the root node (some attributes initialized below). */ + netfs_root_node = netfs_make_node (0); + if (! netfs_root_node) + error (1, ENOMEM, "Cannot create root node"); + + err = maptime_map (0, 0, &console_maptime); + if (err) + error (1, err, "Cannot map time"); + + err = ports_create_port (netfs_control_class, netfs_port_bucket, sizeof (struct port_info), &newpi); + right = ports_get_send_right (newpi); + err = file_set_translator (node, 0, FS_TRANS_EXCL | FS_TRANS_SET, 0, 0, 0, + right, MACH_MSG_TYPE_COPY_SEND); + mach_port_deallocate (mach_task_self (), right); + underlying = node; + + err = io_stat (node, &ul_stat); + if (err) + error (1, err, "Cannot stat underlying node"); + + netfs_root_node->nn_stat.st_ino = 2; + netfs_root_node->nn_stat.st_uid = ul_stat.st_uid; + netfs_root_node->nn_stat.st_gid = ul_stat.st_gid; + netfs_root_node->nn_stat.st_author = ul_stat.st_author; + netfs_root_node->nn_stat.st_mode = S_IFDIR | (ul_stat.st_mode & ~S_IFMT & ~S_ITRANS); + netfs_root_node->nn_stat.st_fsid = getpid (); + netfs_root_node->nn_stat.st_nlink = 1; + netfs_root_node->nn_stat.st_size = 0; + netfs_root_node->nn_stat.st_blocks = 0; + netfs_root_node->nn_stat.st_fstype = FSTYPE_MISC; + netfs_root_node->nn_translated = 0; + + /* If the underlying node isn't a directory, propagate read permission to + execute permission since we need that for lookups. */ + if (! S_ISDIR (ul_stat.st_mode)) + { + if (ul_stat.st_mode & S_IRUSR) + netfs_root_node->nn_stat.st_mode |= S_IXUSR; + if (ul_stat.st_mode & S_IRGRP) + netfs_root_node->nn_stat.st_mode |= S_IXGRP; + if (ul_stat.st_mode & S_IROTH) + netfs_root_node->nn_stat.st_mode |= S_IXOTH; + } + + fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME, + console_maptime); + + cthread_detach (cthread_fork (console_client_translator, NULL)); + + return 0; +} diff --git a/console-client/trans.h b/console-client/trans.h new file mode 100644 index 00000000..1b5156ae --- /dev/null +++ b/console-client/trans.h @@ -0,0 +1,75 @@ +/* trans.h -- Control a translator node for the repeaters. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Written by Marco Gerards. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <hurd/netfs.h> + +struct consnode +{ + /* The filename of the node. */ + char *name; + + /* The id of the node. */ + int id; + + /* Cached if the node is already opened. */ + struct node *node; + + /* Read data from a node. This is exactly the same as io_read + does. */ + error_t (*read) (struct protid *user, char **data, + mach_msg_type_number_t *datalen, off_t offset, + mach_msg_type_number_t amount); + + /* Read data to a node. This is exactly the same as io_write + does. */ + error_t (*write) (struct protid *user, char *data, + mach_msg_type_number_t datalen, off_t offset, + mach_msg_type_number_t *amount); + + /* This is exactly the same as io_select does. */ + error_t (*select) (struct protid *user, mach_port_t reply, + mach_msg_type_name_t replytype, int *type); + + /* Called when the node is opened. */ + void (*open) (void); + + /* Called when the node is closed. */ + void (*close) (void); + + /* The demuxer used for this node. */ + int (*demuxer) (mach_msg_header_t *inp, mach_msg_header_t *outp); + + struct consnode *next; +}; + +typedef struct consnode *consnode_t; + +/* Register the node CN with the translator. */ +void console_register_consnode (consnode_t cn); + +/* Unregister the node CN from the translator. */ +void console_unregister_consnode (consnode_t cn); + +/* Create a node with the name NAME and return it in *CN. */ +error_t console_create_consnode (const char *name, consnode_t *cn); + +/* Destroy the node CN. */ +void console_destroy_consnode (consnode_t cn); + +/* Setup the translator for console client nodes on PATH. */ +error_t console_setup_node (char *path); diff --git a/console-client/vga.c b/console-client/vga.c index c2d55f87..3cc2135a 100644 --- a/console-client/vga.c +++ b/console-client/vga.c @@ -1,5 +1,5 @@ /* vga.c - The VGA device display driver. - Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Written by Marcus Brinkmann. This file is part of the GNU Hurd. @@ -93,6 +93,15 @@ struct refchr }; +typedef struct vga_mousecursor +{ + float posx; + float posy; + char oldcolor; + int visible; + int enabled; +} vga_mousecursor_t; + struct vga_display { /* The VGA font for this display. */ @@ -110,6 +119,9 @@ struct vga_display conchar_attr_t cur_conchar_attr; char cur_attr; + /* The state of the mouse cursor. */ + vga_mousecursor_t mousecursor; + /* Remember for each cell on the display the glyph written to it and the colors (in the upper byte) assigned. 0 means unassigned. */ @@ -151,6 +163,37 @@ vga_display_flash (void *handle) return 0; } + +static void +hide_mousecursor (struct vga_display *disp) +{ + char *oldpos = vga_videomem + 2 * ((int) disp->mousecursor.posy * disp->width + + (int) disp->mousecursor.posx) + 1; + + if (!disp->mousecursor.visible) + return; + + /* First remove the old cursor. */ + *oldpos = disp->mousecursor.oldcolor; + disp->mousecursor.visible = 0; +} + + +static void +draw_mousecursor (struct vga_display *disp) +{ + char *newpos = vga_videomem + 2 * ((int) disp->mousecursor.posy * disp->width + + (int) disp->mousecursor.posx) + 1; + + if (disp->mousecursor.visible) + return; + + /* Draw the new cursor. */ + disp->mousecursor.oldcolor = *newpos; + *newpos = (127) ^ *newpos; + + disp->mousecursor.visible = 1; +} static const char doc[] = "VGA Driver"; @@ -344,6 +387,16 @@ vga_display_fini (void *handle, int force) return 0; } + +static void +vga_display_restore_status (void *handle) +{ + /* Read/write in interleaved mode. This is not preserved by the + XFree VESA driver. */ + outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + outb (VGA_GFX_MISC_CHAINOE | VGA_GFX_MISC_A0TOAF, VGA_GFX_DATA_REG); +} + /* Set the cursor's state to STATE on display HANDLE. */ static error_t @@ -408,6 +461,8 @@ vga_display_scroll (void *handle, int delta) int count = abs(delta) * disp->width; int i; struct refchr *refpos; + + hide_mousecursor (disp); /* XXX: If the virtual console is bigger than the physical console it is impossible to scroll because the data to scroll is not in memory. */ @@ -595,6 +650,7 @@ vga_display_write (void *handle, conchar_t *str, size_t length, struct vga_display *disp = handle; char *pos; struct refchr *refpos = &disp->refmatrix[row][col]; + char *mouse_cursor_pos; /* The starting column is outside the physical screen. */ if (disp->width < current_width && col >= disp->width) @@ -607,6 +663,9 @@ vga_display_write (void *handle, conchar_t *str, size_t length, } pos = vga_videomem + 2 * (row * disp->width + col); + mouse_cursor_pos = (vga_videomem + 2 + * ((int) disp->mousecursor.posy + * disp->width + (int) disp->mousecursor.posx) + 1); /* Although all references to the current fgcol or bgcol could have been released here, for example due to a scroll operation, we @@ -662,6 +721,10 @@ vga_display_write (void *handle, conchar_t *str, size_t length, } *(pos++) = charval & 0xff; + + if (pos == mouse_cursor_pos) + disp->mousecursor.visible = 0; + *(pos++) = disp->cur_attr | (disp->df_size == 512 ? (charval >> 5) & 0x8 : 0); @@ -700,12 +763,65 @@ vga_set_dimension (void *handle, unsigned int width, unsigned int height) return 0; } + +static error_t +vga_display_update (void *handle) +{ + struct vga_display *disp = handle; + + if (disp->mousecursor.enabled) + draw_mousecursor (disp); + + return 0; +} + + +static error_t +vga_set_mousecursor_pos (void *handle, float x, float y) +{ + struct vga_display *disp = handle; + + /* If the mouse did not move from the character position, don't + bother about updating the cursor position. */ + if (disp->mousecursor.visible && x == (int) disp->mousecursor.posx + && y == (int) disp->mousecursor.posy) + return 0; + + hide_mousecursor (disp); + + disp->mousecursor.posx = x; + disp->mousecursor.posy = y; + + if (disp->mousecursor.enabled) + draw_mousecursor (disp); + + return 0; +} + + +static error_t +vga_set_mousecursor_status (void *handle, int status) +{ + struct vga_display *disp = handle; + + disp->mousecursor.enabled = status; + if (!status) + hide_mousecursor (disp); + else + draw_mousecursor (disp); + + return 0; +} + + struct driver_ops driver_vga_ops = { vga_display_init, vga_display_start, vga_display_fini, + NULL, + vga_display_restore_status }; static struct display_ops vga_display_ops = @@ -715,8 +831,10 @@ static struct display_ops vga_display_ops = vga_display_scroll, vga_display_clear, vga_display_write, - NULL, + vga_display_update, vga_display_flash, NULL, - vga_set_dimension + vga_set_dimension, + vga_set_mousecursor_pos, + vga_set_mousecursor_status }; |