523 lines
14 KiB
C
523 lines
14 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)ws.c 1.1 94/10/31";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1986 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* SunWindows Workstation/Wsindev open and close code. Includes tuning
|
|
* parameters.
|
|
*/
|
|
|
|
#include <sunwindowdev/wintree.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/file.h> /* For f_data */
|
|
#include <sys/kmem_alloc.h>
|
|
#include <sundev/kbio.h> /* For keyboard ioctls */
|
|
|
|
#define WS_VQ_NODE_BYTES 8192
|
|
u_int ws_vq_node_bytes = WS_VQ_NODE_BYTES;
|
|
/* Number of bytes to use for input q nodes */
|
|
int ws_vq_expand_times = 6;
|
|
/* Number of times can expand q by ws_vq_node_bytes */
|
|
#define WS_QUEUE_COLLAPSE_FACTOR 0 /* Try to collapse q to 1/nth */
|
|
/* Zero mean collapse all the time */
|
|
int ws_q_collapse_factor = WS_QUEUE_COLLAPSE_FACTOR;
|
|
|
|
extern void ws_break_handler();
|
|
extern void ws_stop_handler();
|
|
extern void ws_flush_handler();
|
|
Ws_usr_async ws_break_default =
|
|
{0, SHIFT_TOP, 1, TOP_FIRST + 'i', 1, ws_break_handler};
|
|
Ws_usr_async ws_stop_default =
|
|
{0, SHIFT_TOP, 1, SHIFT_TOP, 0, ws_stop_handler};
|
|
Ws_usr_async ws_flush_default =
|
|
{0, SHIFT_TOP, 1, TOP_FIRST + 'f', 1, ws_flush_handler};
|
|
|
|
Ws_focus_set ws_kbd_focus_pt_default = {LOC_WINENTER, 1, 0};
|
|
Ws_focus_set ws_kbd_focus_sw_default = {LOC_WINENTER, 1, 0};
|
|
struct timeval ws_event_timeout_default = {2, 0};
|
|
/* 2 secs give micro synchronization, thus collapsing when tracking */
|
|
|
|
/*
|
|
* Tuning parameters that are set if ws_tuning_done is 0 when start SunWindows.
|
|
*/
|
|
int ws_tuning_done; /* Controls whether the following should be */
|
|
/* initialized. */
|
|
int ws_fast_timeout; /* Fast polling rate in hz */
|
|
int ws_slow_timeout; /* Slow polling rate in hz */
|
|
int ws_fast_poll_duration; /* Stop fast polling after this # hz */
|
|
int ws_loc_still; /* Locator is still after this number of hz */
|
|
int ws_check_lock; /* Check locks every this # of hz if pid */
|
|
int ws_no_pid_check_lock; /* Check locks every this # of hz if no pid */
|
|
struct timeval ws_check_time; /* Check locks every this # usec */
|
|
int winclistcharsmax; /* Limit the amount of clist space can use */
|
|
int ws_q_collapse_trigger; /* # q items that trigger collapse */
|
|
|
|
ws_init_tuning()
|
|
{
|
|
#define WS_FAST_TIMEOUT (hz/40) /* Fast polling rate */
|
|
#define WS_SLOW_TIMEOUT (hz/8) /* Slow polling rate */
|
|
#define WS_FAST_POLL_DURATION (hz/5) /* Stop fast polling after this # hz */
|
|
#define WS_MOUSE_STILL (hz/5) /* Locator is still after this # of hz*/
|
|
#define WS_CHECK_LOCK (hz/2) /* Check locks every #of hz if pid */
|
|
#define WS_NO_PID_CHECK_LOCK (hz*20) /* Check locks every #of hz if no pid */
|
|
|
|
if (!ws_tuning_done) {
|
|
extern int hz;
|
|
|
|
/* Init things can't do statically */
|
|
if (!ws_fast_timeout)
|
|
ws_fast_timeout = WS_FAST_TIMEOUT;
|
|
if (!ws_slow_timeout)
|
|
ws_slow_timeout = WS_SLOW_TIMEOUT;
|
|
if (!ws_fast_poll_duration)
|
|
ws_fast_poll_duration = WS_FAST_POLL_DURATION;
|
|
if (!ws_loc_still)
|
|
ws_loc_still = WS_MOUSE_STILL;
|
|
if (!ws_check_lock)
|
|
ws_check_lock = WS_CHECK_LOCK;
|
|
if (!ws_no_pid_check_lock)
|
|
ws_no_pid_check_lock = WS_NO_PID_CHECK_LOCK;
|
|
if (!ws_check_time.tv_usec)
|
|
ws_check_time.tv_usec = 1000000 / hz * ws_check_lock;
|
|
if (!winclistcharsmax)
|
|
/* Use half of all clist space */
|
|
winclistcharsmax = nclist * CBSIZE / 2;
|
|
if ((!ws_q_collapse_trigger) && ws_q_collapse_factor)
|
|
ws_q_collapse_trigger =
|
|
(ws_vq_node_bytes / sizeof (Vuid_q_node)) /
|
|
ws_q_collapse_factor;
|
|
ws_tuning_done = 1;
|
|
}
|
|
}
|
|
|
|
/* Find workstation using input_device_name */
|
|
Workstation *
|
|
ws_indev_match_name(input_device_name, indev_ptr)
|
|
char *input_device_name;
|
|
Wsindev **indev_ptr;
|
|
{
|
|
register Workstation *ws;
|
|
register Wsindev *indev;
|
|
|
|
if (input_device_name[0] == '\0')
|
|
return (WORKSTATION_NULL);
|
|
for (ws = workstations; ws < &workstations[nworkstations]; ws++) {
|
|
for (indev = ws->ws_indev; indev != WSINDEV_NULL;
|
|
indev = indev->wsid_next) {
|
|
if (indev->wsid_name[0] != '\0' &&
|
|
(strncmp(input_device_name,
|
|
indev->wsid_name, SCR_NAMESIZE) == 0)) {
|
|
if (indev_ptr)
|
|
*indev_ptr = indev;
|
|
return (ws);
|
|
}
|
|
}
|
|
}
|
|
return (WORKSTATION_NULL);
|
|
}
|
|
|
|
/* Find workstation using dev */
|
|
Workstation *
|
|
ws_indev_match_dev(dev)
|
|
dev_t dev;
|
|
{
|
|
register Workstation *ws;
|
|
register Wsindev *indev;
|
|
|
|
for (ws = workstations; ws < &workstations[nworkstations]; ws++) {
|
|
for (indev = ws->ws_indev; indev != WSINDEV_NULL;
|
|
indev = indev->wsid_next) {
|
|
if (indev->wsid_dev == dev)
|
|
return (ws);
|
|
}
|
|
}
|
|
return (WORKSTATION_NULL);
|
|
}
|
|
|
|
/*
|
|
* A workstation is opened by a window that trying to establish itself
|
|
* as the root window of a first desktop at a workstation. The list of
|
|
* desktops is searched for a conflict of names of framebuffers to avoid
|
|
* blitzing an existing screen. The list of workstations is searched for
|
|
* a conflict of names of input devices that the window has opened for
|
|
* itself.
|
|
*/
|
|
Workstation *
|
|
ws_open()
|
|
{
|
|
register Workstation *ws = WORKSTATION_NULL;
|
|
register Workstation *ws_probe = WORKSTATION_NULL;
|
|
extern int ws_poll_rate;
|
|
|
|
/* Initialize tuning parameters */
|
|
ws_init_tuning();
|
|
/* Allocate an unused workstation */
|
|
for (ws_probe = workstations; ws_probe < &workstations[nworkstations];
|
|
ws_probe++) {
|
|
if (!(ws_probe->ws_flags & WSF_PRESENT)) {
|
|
ws = ws_probe;
|
|
break;
|
|
}
|
|
}
|
|
if ((ws == WORKSTATION_NULL) || (ws->ws_dtop != DESKTOP_NULL)) {
|
|
win_errno = EBUSY;
|
|
return (WORKSTATION_NULL);
|
|
}
|
|
/* Allocate the input q */
|
|
ws->ws_qbytes = ws_vq_node_bytes;
|
|
ws->ws_qdata = new_kmem_zalloc((u_int)ws->ws_qbytes, KMEM_SLEEP);
|
|
/*
|
|
* There is no need to check for kmem_zalloc here because
|
|
* kmem_zalloc will wait until there is space before it returns
|
|
if (ws->ws_qdata == NULL) {
|
|
#ifdef WINDEVDEBUG
|
|
printf("Couldn't allocate %D byte event buffer bytes\n",
|
|
ws->ws_qbytes);
|
|
#endif
|
|
win_errno = ENOMEM;
|
|
return (WORKSTATION_NULL);
|
|
}
|
|
*/
|
|
vq_initialize(&ws->ws_q, ws->ws_qdata, ws->ws_qbytes);
|
|
|
|
/* Make present so that need ws_close to clean up after this point */
|
|
ws->ws_flags |= WSF_PRESENT;
|
|
/* Initialize input queue synchronization parameters */
|
|
ws->ws_break = ws_break_default;
|
|
ws->ws_stop = ws_stop_default;
|
|
ws->ws_eventtimeout = ws_event_timeout_default;
|
|
/* Initialize kbd focus changing events */
|
|
ws->ws_kbd_focus_pt = ws_kbd_focus_pt_default;
|
|
ws->ws_kbd_focus_sw = ws_kbd_focus_sw_default;
|
|
/*
|
|
* Start device driver looking for input and doing deadlock
|
|
* resolution if haven't started yet.
|
|
*/
|
|
if (ws_poll_rate == 0) {
|
|
int ws_interrupt();
|
|
|
|
ws_poll_rate = ws_fast_timeout;
|
|
timeout(ws_interrupt, (caddr_t)0, ws_poll_rate);
|
|
}
|
|
return (ws);
|
|
}
|
|
|
|
/*
|
|
* A workstation is told by its last desktop to close, as is a desktop
|
|
* told by its last window to close.
|
|
*/
|
|
ws_close(ws)
|
|
register Workstation *ws;
|
|
{
|
|
#ifdef WINSVJ
|
|
void ws_dealloc_rec_q(); /* Recording q dealloc routine */
|
|
#endif
|
|
|
|
/*
|
|
* ws_close_indev can sleep (see ws_close_indev) which can
|
|
* allow ws_interrupt to be called as a timeout. Make this
|
|
* workstation invisible to ws_interrupt now.
|
|
*/
|
|
ws->ws_flags |= WSF_EXITING;
|
|
/* Close input devices used by this workstation */
|
|
while (ws->ws_indev != WSINDEV_NULL)
|
|
/* ws_close_indev deletes ws_indev from list */
|
|
if (ws_close_indev(ws, ws->ws_indev) == -1)
|
|
break;
|
|
|
|
/* All desktops should be removed by now */
|
|
#ifdef WINDEVDEBUG
|
|
if (ws->ws_dtop != DESKTOP_NULL)
|
|
printf("ws_close: desktop still opened\n");
|
|
#endif
|
|
|
|
/* Deallocate input queue */
|
|
if (ws->ws_qdata != NULL)
|
|
kmem_free(ws->ws_qdata, (u_int)ws->ws_qbytes);
|
|
|
|
/* Deallocate input state descriptions */
|
|
vuid_destroy_state(ws->ws_instate);
|
|
vuid_destroy_state(ws->ws_rtstate);
|
|
|
|
/* Deallocate any event recording queue */
|
|
#ifdef WINSVJ
|
|
(void) ws_dealloc_rec_q(ws);
|
|
#endif
|
|
bzero((caddr_t)ws, sizeof (*ws));
|
|
/* ws_interrupt turns self off after last workstation goes away */
|
|
}
|
|
|
|
Wsindev *
|
|
ws_open_indev(ws, name, fd)
|
|
Workstation *ws;
|
|
char *name;
|
|
int fd;
|
|
{
|
|
register Wsindev *wsid;
|
|
register int i;
|
|
int mode;
|
|
struct file *fp = 0;
|
|
struct vnode *vp;
|
|
Wsindev *wsid_tmp;
|
|
dev_t dev;
|
|
struct termios tios;
|
|
|
|
/* Dup file descriptor for device */
|
|
if ((u.u_error = kern_dupfd(fd, &fp)))
|
|
goto SilentError;
|
|
vp = (struct vnode *)fp->f_data;
|
|
if (vp->v_stream == NULL) {
|
|
u.u_error = ENOSTR; /* not a streams device */
|
|
goto SilentError;
|
|
}
|
|
dev = vp->v_rdev;
|
|
/* See if can find this device open already */
|
|
if (ws_indev_match_dev(dev) != WORKSTATION_NULL) {
|
|
u.u_error = EBUSY;
|
|
goto SilentError;
|
|
}
|
|
/* Allocate new input record */
|
|
wsid = (Wsindev *)new_kmem_zalloc(sizeof (*wsid), KMEM_SLEEP);
|
|
if (wsid == WSINDEV_NULL) {
|
|
#ifdef WINDEVDEBUG
|
|
printf("ws_open_indev: heap alloc failed\n");
|
|
#endif
|
|
u.u_error = ENOMEM;
|
|
goto SilentError;
|
|
}
|
|
bzero((caddr_t)wsid, sizeof (*wsid));
|
|
/* Initialize record */
|
|
wsid->wsid_fp = fp;
|
|
wsid->wsid_dev = dev;
|
|
wsid->wsid_vp = vp;
|
|
wsid->wsid_flags = 0;
|
|
/* Copy name */
|
|
for (i = 0; i < SCR_NAMESIZE; i++)
|
|
wsid->wsid_name[i] = name[i];
|
|
/* Determine current byte stream mode */
|
|
strioctl(vp, VUIDGFORMAT, (caddr_t)&wsid->wsid_previous_mode, 0);
|
|
if (u.u_error) {
|
|
/* ioctl err VUIDGFORMAT */
|
|
if ((u.u_error == ENOTTY) || (u.u_error == EINVAL)) {
|
|
wsid->wsid_flags |= WSID_TREAT_AS_ASCII;
|
|
wsid->wsid_previous_mode = VUID_NATIVE;
|
|
} else
|
|
goto Error;
|
|
}
|
|
/* Set up to send firm events */
|
|
mode = VUID_FIRM_EVENT;
|
|
strioctl(vp, VUIDSFORMAT, (caddr_t)&mode, 0);
|
|
if (u.u_error) {
|
|
/* ioctl err VUIDSFORMAT */
|
|
if ((u.u_error == ENOTTY) || (u.u_error == EINVAL)) {
|
|
wsid->wsid_flags |= WSID_TREAT_AS_ASCII;
|
|
wsid->wsid_previous_mode = VUID_NATIVE;
|
|
} else
|
|
goto Error;
|
|
}
|
|
/* Get sgttyb flags */
|
|
strioctl(vp, TCGETS, (caddr_t)&tios, 0);
|
|
if (u.u_error) {
|
|
/* ioctl err TCGETS */
|
|
goto Error;
|
|
}
|
|
wsid->wsid_iflag = tios.c_iflag;
|
|
wsid->wsid_oflag = tios.c_oflag;
|
|
wsid->wsid_cflag = tios.c_cflag;
|
|
wsid->wsid_lflag = tios.c_lflag;
|
|
/* Set raw */
|
|
tios.c_iflag = 0;
|
|
tios.c_oflag = 0;
|
|
tios.c_cflag &= ~(CSIZE|PARENB);
|
|
tios.c_cflag |= CS8;
|
|
tios.c_lflag = 0;
|
|
strioctl(vp, TCSETSF, (caddr_t)&tios, 0);
|
|
if (u.u_error) {
|
|
/* ioctl err TCSETSF */
|
|
goto Error;
|
|
}
|
|
/*
|
|
* Get/set direct I/O flag. This is a hack to avoid /dev/console
|
|
* interference when using /dev/kbd. Silent about any error
|
|
* on KIOCGDIRECT because only /dev/kbd will respond.
|
|
*/
|
|
strioctl(vp, KIOCGDIRECT, (caddr_t)&wsid->wsid_direct_flag, 0);
|
|
if (u.u_error == 0) {
|
|
int directio = 1;
|
|
|
|
strioctl(vp, KIOCSDIRECT, (caddr_t)&directio, 0);
|
|
if (u.u_error) {
|
|
/* ioctl err KIOCSDIRECT */
|
|
goto Error;
|
|
}
|
|
wsid->wsid_flags |= WSID_DIRECT_VALID;
|
|
/*
|
|
* It's a keyboard; set up not to OR in bucky bits.
|
|
*/
|
|
mode = 0;
|
|
strioctl(vp, KIOCSCOMPAT, (caddr_t)&mode, 0);
|
|
if (u.u_error) {
|
|
/* ioctl err KIOCSCOMPAT */
|
|
goto Error;
|
|
}
|
|
}
|
|
/* Put new record in list */
|
|
wsid_tmp = ws->ws_indev;
|
|
ws->ws_indev = wsid;
|
|
wsid->wsid_next = wsid_tmp;
|
|
return (wsid);
|
|
Error:
|
|
#ifdef WINDEVDEBUG
|
|
printf("err = %D\n", u.u_error);
|
|
#endif
|
|
SilentError:
|
|
return (WSINDEV_NULL);
|
|
}
|
|
|
|
int
|
|
ws_close_indev(ws, wsid)
|
|
Workstation *ws;
|
|
Wsindev *wsid;
|
|
{
|
|
struct termios tios;
|
|
register struct vnode *vp = (struct vnode *)wsid->wsid_fp->f_data;
|
|
register Wsindev *indev, **indev_ptr;
|
|
int on = 1;
|
|
|
|
/*
|
|
* Remove wsid from list associated with workstation.
|
|
* This will avoid any further read of this device because zsclose
|
|
* (called from closef below) might sleep.
|
|
* In other words, when zsclose sleeps, the ws_interrupt timeout
|
|
* can occur. By closef the device's original blocking state
|
|
* has been restored. Thus, when ws_interrupt does a read of this
|
|
* device, it might block (sleep). Blocking from a timeout notification
|
|
* will lead to a panic: sleep.
|
|
*/
|
|
indev_ptr = &ws->ws_indev;
|
|
for (indev = ws->ws_indev; indev != WSINDEV_NULL;
|
|
indev = indev->wsid_next) {
|
|
if (indev == wsid) {
|
|
*indev_ptr = indev->wsid_next;
|
|
indev->wsid_next = WSINDEV_NULL;
|
|
goto Found;
|
|
}
|
|
indev_ptr = &indev->wsid_next;
|
|
}
|
|
/* ws_close_indev: device not found */
|
|
return (-1);
|
|
Found:
|
|
u.u_error = 0;
|
|
/* Reset sgttyb flags */
|
|
strioctl(vp, TCGETS, (caddr_t)&tios, 0);
|
|
#ifdef WINDEVDEBUG
|
|
if (u.u_error)
|
|
printf("ioctl err %D TCGETS\n", u.u_error);
|
|
#endif
|
|
tios.c_iflag = wsid->wsid_iflag;
|
|
tios.c_oflag = wsid->wsid_oflag;
|
|
tios.c_cflag = wsid->wsid_cflag;
|
|
tios.c_lflag = wsid->wsid_lflag;
|
|
strioctl(vp, TCSETSF, (caddr_t)&tios, 0);
|
|
#ifdef WINDEVDEBUG
|
|
if (u.u_error)
|
|
printf("ioctl err %D TCSETSF\n", u.u_error);
|
|
#endif
|
|
/* Reset vuid format */
|
|
strioctl(vp, VUIDSFORMAT, (caddr_t)&wsid->wsid_previous_mode, 0);
|
|
#ifdef WINDEVDEBUG
|
|
if (u.u_error)
|
|
printf("ioctl err %D VUIDSFORMAT\n", u.u_error);
|
|
#endif
|
|
/* Reset direct flag */
|
|
if (wsid->wsid_flags & WSID_DIRECT_VALID) {
|
|
strioctl(vp, KIOCSDIRECT, (caddr_t)&wsid->wsid_direct_flag, 0);
|
|
#ifdef WINDEVDEBUG
|
|
if (u.u_error)
|
|
printf("ioctl err %d KIOCSDIRECT\n", u.u_error);
|
|
#endif
|
|
/*
|
|
* It's a keyboard; set up to OR in bucky bits.
|
|
*/
|
|
strioctl(vp, KIOCSCOMPAT, (caddr_t)&on, 0);
|
|
#ifdef WINDEVDEBUG
|
|
if (u.u_error)
|
|
printf("ioctl err %D KIOCSCOMPAT\n", u.u_error);
|
|
#endif
|
|
}
|
|
/* Cleanup file pointer */
|
|
if (wsid->wsid_fp)
|
|
closef(wsid->wsid_fp);
|
|
/* Release memory */
|
|
kmem_free((caddr_t)wsid, sizeof (*wsid));
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
ws_shrink_queue(ws)
|
|
register Workstation *ws;
|
|
{
|
|
Vuid_queue q;
|
|
u_int qbytes;
|
|
caddr_t qdata;
|
|
|
|
/* Allocate new input q */
|
|
qbytes = ws_vq_node_bytes;
|
|
qdata = new_kmem_zalloc(qbytes, KMEM_NOSLEEP);
|
|
if (qdata == NULL)
|
|
return;
|
|
vq_initialize(&q, qdata, (u_int)qbytes);
|
|
/* Deallocate old input queue */
|
|
if (ws->ws_qdata != NULL)
|
|
kmem_free(ws->ws_qdata, ws->ws_qbytes);
|
|
/* Update ws */
|
|
ws->ws_qbytes = qbytes;
|
|
ws->ws_qdata = qdata;
|
|
ws->ws_q = q;
|
|
}
|
|
|
|
/* Deal with input queue overflow */
|
|
void
|
|
ws_handle_overflow(ws)
|
|
register Workstation *ws;
|
|
{
|
|
Vuid_queue q;
|
|
u_int qbytes;
|
|
caddr_t qdata;
|
|
Firm_event firm_event;
|
|
|
|
if (ws->ws_qbytes >= ws_vq_node_bytes * ws_vq_expand_times)
|
|
goto Flush;
|
|
/* Allocate new input q */
|
|
qbytes = ws->ws_qbytes + ws_vq_node_bytes;
|
|
qdata = new_kmem_zalloc(qbytes, KMEM_NOSLEEP);
|
|
if (qdata == NULL)
|
|
goto Flush;
|
|
vq_initialize(&q, qdata, (u_int)qbytes);
|
|
/* Copy contents of old q onto new one */
|
|
while (vq_get(&ws->ws_q, &firm_event) != VUID_Q_EMPTY)
|
|
if (vq_put(&q, &firm_event) == VUID_Q_OVERFLOW)
|
|
#ifdef WINDEVDEBUG
|
|
printf("Window input queue unexpectedly full!\n");
|
|
#else
|
|
;
|
|
#endif
|
|
/* Deallocate old input queue */
|
|
if (ws->ws_qdata != NULL)
|
|
kmem_free(ws->ws_qdata, ws->ws_qbytes);
|
|
/* Update ws */
|
|
ws->ws_qbytes = qbytes;
|
|
ws->ws_qdata = qdata;
|
|
ws->ws_q = q;
|
|
return;
|
|
Flush:
|
|
/* Punt and flush queue */
|
|
printf("Window input queue overflow!\n");
|
|
ws_flush_input(ws);
|
|
printf("Window input queue flushed!\n");
|
|
}
|