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/pc-mouse.c | |
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/pc-mouse.c')
-rw-r--r-- | console-client/pc-mouse.c | 509 |
1 files changed, 509 insertions, 0 deletions
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; +} |