2021-10-11 18:37:13 -03:00

546 lines
15 KiB
C

#ifndef lint
static char sccsid[] = "@(#)ws_interrupt.c 1.1 94/10/31";
#endif
/*
* Copyright (c) 1986 by Sun Microsystems, Inc.
*/
/*
* SunWindows Workstation code concerned with the bottom end of the
* input queue: polling input devices, enqueuing events, tracking the
* cursor. It is also responsible for driving deadlock detection
* from the interrupt loop.
*/
#include <sunwindowdev/wintree.h>
#include <sunwindow/win_input.h>
#include <sunwindow/win_ioctl.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/kernel.h> /* for time */
/* TODO: Place Ws_usr_async ws_flush_default in Workstation */
/* TODO: Turn on ws_sync_msg and debug out of sync problem */
#ifdef WINDEVDEBUG
int ws_kreads; /* Temp: Monitor number of kernel reads */
int ws_q_compress; /* Temp: Monitor number of compresses */
int ws_qput_debug; /* Temp: Debugging messages when enqueue firm event */
int ws_compress_debug;/* Temp: Turn on to monitor collapse statistics */
int ws_sync_msg; /* Temp: Message about locator ut & rt out of sync */
int ws_break_msg; /* Message when event lock break */
int ws_stop_msg; /* Message when stop sent */
int ws_flush_msg; /* Message when user specified flush done */
#endif
int ws_sync_debug = 1;/* Temp: Try to catch locator ut & rt out of sync */
int ws_no_q_compress; /* Non-zero mean don't do q compression */
int ws_poll_rate; /* # of ticks until next input poll */
static int ws_quiet_ticks; /* # of ticks since latest input activity */
extern int ws_fast_timeout; /* Fast polling rate in hz */
extern int ws_slow_timeout; /* Slow polling rate in hz */
extern int ws_fast_poll_duration; /* Stop fast polling after this # hz */
extern int ws_loc_still; /* Locator is still after this # of hz*/
int ws_button_order; /* current button mapping */
static int buttoncodes[6][3] = { /* translate table for buttons */
{ BUT(1), BUT(2), BUT(3) }, /* L M R */
{ BUT(1), BUT(3), BUT(2) }, /* L R M */
{ BUT(2), BUT(1), BUT(3) }, /* M L R */
{ BUT(2), BUT(3), BUT(1) }, /* M R L */
{ BUT(3), BUT(1), BUT(2) }, /* R L M */
{ BUT(3), BUT(2), BUT(1) }, /* R M L */
};
extern Ws_scale ws_scaling[WS_SCALE_MAX_COUNT+1];
#define WS_QUEUE_COMPRESS_FACTOR 4 /* Try to collapse q to 1/4th */
#define WS_QUEUE_COMPRESS_MORE 1 /* ws_q_compress_factor increment */
int ws_q_compress_factor = WS_QUEUE_COMPRESS_FACTOR;
int ws_q_compress_base = WS_QUEUE_COMPRESS_FACTOR;
int ws_q_compress_more = WS_QUEUE_COMPRESS_MORE;
ws_interrupt()
{
register Workstation *ws;
register Desktop *dtop;
register Wsindev *in_dev;
register int i;
int num_ws = 0;
ws_quiet_ticks += ws_poll_rate;
/* Cycle through every workstation */
for (i = 0; i < nworkstations; i++) {
ws = &workstations[i];
if ((~ws->ws_flags & WSF_PRESENT) ||
(ws->ws_flags & WSF_EXITING))
continue;
num_ws++;
/* Cycle through every input device */
for (in_dev = ws->ws_indev; in_dev; in_dev = in_dev->wsid_next){
if (ws_read_indev(ws, in_dev))
/* Some input activity */
ws_quiet_ticks = 0;
}
/*
* Update how long since locator has been still (may change
* in ws_track_locator)
*/
ws->ws_loc_stillticks += ws_poll_rate;
if (ws->ws_flags & WSF_LOC_UPDATED)
/* Do locator tracking */
ws_track_locator(ws);
/* Send LOC_STILL */
if ((ws->ws_loc_stillticks >= ws_loc_still) &&
(ws->ws_loc_stillticks <
ws_loc_still + ws_fast_poll_duration)) {
ws_loc_is_still(ws);
/* Make sure don't send multiple stills */
ws->ws_loc_stillticks = ws_loc_still +
ws_fast_poll_duration;
}
/* If no event lock holder then nudge next event off queue */
if (~ws->ws_flags & WSF_LOCKED_EVENT)
ws_nudge(ws);
/* Do grabio running check */
if (ws->ws_flags & WSF_LOCKED_IO)
wlok_running_check(&ws->ws_iolock);
/* Do current event timeout deadlock resolution */
if (ws->ws_flags & WSF_LOCKED_EVENT)
wlok_lockcheck(&ws->ws_eventlock, ws_poll_rate,
1 /* charge sleep time to elasped time */);
/* Cycle through every desktop */
for (dtop = ws->ws_dtop; dtop; dtop = dtop->dt_next)
/* Do display/data lock timeout resolution */
dtop_interrupt(dtop, ws_poll_rate);
}
/* Adjust polling rate */
if (ws_quiet_ticks >= ws_fast_poll_duration)
/*
* Slow input polling down when no activity
* in order to save processor cycles.
*/
ws_poll_rate = ws_slow_timeout;
else
ws_poll_rate = ws_fast_timeout;
if (num_ws == 0) {
/* Turn self off if no more active workstations */
untimeout(ws_interrupt, (caddr_t)0);
/* Resetting ws_poll_rate means ws_open should restart */
ws_poll_rate = 0;
} else
/* Come around again */
timeout(ws_interrupt, (caddr_t)0, ws_poll_rate);
}
int
ws_read_indev(ws, in_dev)
register Workstation *ws;
register Wsindev *in_dev;
{
#define EVENT_BUF_LEN 20
Firm_event events[EVENT_BUF_LEN];
Firm_event afe;
caddr_t aevents;
int len;
register int loop = 1;
register int e;
int read_one = 0;
int error;
/* Drain input device of all its input in loop */
while (loop) {
len = sizeof(Firm_event) * EVENT_BUF_LEN;
#ifdef WINDEVDEBUG
ws_kreads++;
#endif
error = kern_read(in_dev->wsid_vp, (caddr_t)&events[0], &len);
switch (error) {
case 0:
/* Turn ascii into Firm_events */
if (in_dev->wsid_flags & WSID_TREAT_AS_ASCII) {
aevents = (caddr_t)&events[0];
for (e = 0; e < len; e++) {
afe.id = *(aevents+e);
afe.pair_type = FE_PAIR_NONE;
afe.pair = 0;
afe.value = 1;
afe.time = time;
read_one =
ws_consume_input_event(ws, &afe);
}
continue;
}
/* Break out of loop if read less then max */
if (len < sizeof(Firm_event) * EVENT_BUF_LEN)
loop = 0;
/* Enqueue each event with workstation */
for (e = 0; len >= sizeof(Firm_event);
e++, len -= sizeof(Firm_event)) {
read_one =
ws_consume_input_event(ws, &events[e]);
}
#ifdef WINDEVDEBUG
/* Check for incomplete firm event read */
if (len != 0)
printf("ws_read_indev length error %D\n", len);
#endif
break;
case EWOULDBLOCK:
/* No more input from the device */
loop = 0;
continue;
#ifdef WINDEVDEBUG
default:
printf("ws_read_indev error %D\n", error);
#endif
}
}
return (read_one);
}
/*
* Update cursor if locator moved and jump between desktops.
*/
ws_track_locator(ws)
register Workstation *ws;
{
Desktop *dtop_loc = ws->ws_loc_dtop; /* can't be register */
int x_now, y_now; /* can't be register */
register Desktop *dtop_old;
ws->ws_flags &= ~WSF_LOC_UPDATED;
if (dtop_loc == DESKTOP_NULL)
return;
/* Get locator position on desktop */
x_now = dtop_loc->dt_rt_x;
y_now = dtop_loc->dt_rt_y;
/* Restart timer to see if still */
ws->ws_loc_stillticks = 0;
/* Clamp locator to desktop or move onto adjoinning desktop */
dtop_old = dtop_loc;
dtop_track_locator(&dtop_loc, &x_now, &y_now);
/* Clear old dtop cursor and cursor owner */
if (dtop_loc != dtop_old) {
void dtop_update_enable();
dtop_cursordown(dtop_old);
win_shared_update_cursor_active(dtop_old->shared_info, FALSE);
ws->ws_loc_dtop = dtop_loc;
win_shared_update_cursor_active(dtop_loc->shared_info, TRUE);
dtop_old->dt_cursorwin = WINDOW_NULL;
win_shared_update_cursor(dtop_old);
dtop_update_enable(dtop_loc, dtop_old, 0);
/* Reload colormap as enter new dtop */
dtop_loc->dt_flags |= DTF_NEWCMAP;
}
/*
* Update the shared memory mouse x, y
*/
if (x_now != dtop_loc->dt_rt_x || y_now != dtop_loc->dt_rt_y)
win_shared_update_mouse_xy(dtop_loc->shared_info, x_now, y_now);
/* Update dtop_loc's notion of locator position */
dtop_loc->dt_rt_x = x_now;
dtop_loc->dt_rt_y = y_now;
/*
* Find out new cursor owner, arrange for new cursor, update
* the shared cursor if window changed.
*/
dtop_set_cursor(dtop_loc);
}
ws_consume_event(ws, event)
register Workstation *ws;
register Firm_event *event;
{
register Desktop *dtop_loc = ws->ws_loc_dtop;
int num;
int orig_x, orig_y;
/* Deal with full input queue */
while ((vq_is_full(&ws->ws_q)) && (!ws_no_q_compress)) {
num = vq_compress(&ws->ws_q, ws_q_compress_factor);
#ifdef WINDEVDEBUG
ws_q_compress++;
if (ws_compress_debug)
printf("Win input q collapsed %D, factor %D\n", num, ws_q_compress_factor);
#endif
if (num == 0) {
/* Handle compression failure */
if (ws_q_compress_factor != 0)
/* Zero factor means super compress */
ws_q_compress_factor = 0;
else {
extern void ws_handle_overflow();
ws_handle_overflow(ws);
}
} else
/* Up compress factor for next time (reset on empty q)*/
ws_q_compress_factor += ws_q_compress_more;
}
/* Reset compression factor if q empties */
if (vq_is_empty(&ws->ws_q)) {
ws_q_compress_factor = ws_q_compress_base;
#ifdef WINDEVDEBUG
if (dtop_loc == DESKTOP_NULL) printf("Unexpected dtop_loc == NULL!\n");
#endif
if (ws_sync_debug &&
(ws->ws_pick_dtop == dtop_loc) &&
((dtop_loc->dt_rt_x != dtop_loc->dt_ut_x) ||
(dtop_loc->dt_rt_y != dtop_loc->dt_ut_y))) {
#ifdef WINDEVDEBUG
if (ws_sync_msg)
printf("Ut (%D,%D) & rt(%D,%D) out of sync: now in sync\n",
dtop_loc->dt_ut_x, dtop_loc->dt_ut_y,
dtop_loc->dt_rt_x, dtop_loc->dt_rt_y);
#endif
dtop_loc->dt_ut_x = dtop_loc->dt_rt_x;
dtop_loc->dt_ut_y = dtop_loc->dt_rt_y;
}
}
/* Update state of virtual user input device */
orig_x = dtop_loc->dt_rt_x;
orig_y = dtop_loc->dt_rt_y;
switch (event->id) {
case LOC_X_DELTA:
/* Modify locator delta events for acceleration */
if (ws->ws_loc_stillticks < ws_loc_still)
ws_scale_event(&event->value);
dtop_loc->dt_rt_x += event->value;
ws->ws_flags |= WSF_LOC_UPDATED;
break;
case LOC_Y_DELTA:
/* Modify locator delta events for acceleration */
if (ws->ws_loc_stillticks < ws_loc_still)
ws_scale_event(&event->value);
/* Adjust y direction from locator */
event->value = 0 - event->value;
dtop_loc->dt_rt_y += event->value;
ws->ws_flags |= WSF_LOC_UPDATED;
break;
case LOC_X_ABSOLUTE:
dtop_loc->dt_rt_x = event->value;
ws->ws_flags |= WSF_LOC_UPDATED;
break;
case LOC_Y_ABSOLUTE:
dtop_loc->dt_rt_y = event->value;
ws->ws_flags |= WSF_LOC_UPDATED;
break;
case LOC_MOVE:
ws->ws_flags |= WSF_LOC_UPDATED;
break;
case MS_LEFT:
case MS_MIDDLE:
case MS_RIGHT:
if (ws_button_order > 0)
event->id =
buttoncodes[ws_button_order][event->id - MS_LEFT];
/* FALL THROUGH to do all the normal processing */
default: {
extern Ws_usr_async ws_flush_default;
register int async_triggered = 0;
/* Set real time state */
vuid_set_value(&ws->ws_rtstate, event);
/*
* Check for escapes. Can have single escape trigger
* multiple actions.
*/
if (ws_usr_async_check(ws, &ws_flush_default, event))
async_triggered++;
if (ws_usr_async_check(ws, &ws->ws_break, event))
async_triggered++;
if (ws_usr_async_check(ws, &ws->ws_stop, event))
async_triggered++;
if (async_triggered)
/*
* Toss trigger event (leaving first event in queue)
* This has the unpleasant side affect of messing up
* the maintainence of the user time vuid state.
* TODO: Perhaps we should always flush the input queue
* which syncs the real time and user time input states?
*/
return;
}
}
if (orig_x != dtop_loc->dt_rt_x || orig_y != dtop_loc->dt_rt_y)
/*
* Update the shared memory mouse x, y
*/
win_shared_update_mouse_xy(dtop_loc->shared_info,
dtop_loc->dt_rt_x, dtop_loc->dt_rt_y);
#ifdef WINSVJ
if (ws->ws_flags&WSF_RECORD_EVENT)
(void) svj_consume_event(ws, event);
#endif
/* Enqueue event on input queue */
if (vq_put(&ws->ws_q, event) == VUID_Q_OVERFLOW)
#ifdef WINDEVDEBUG
printf("Window input queue unexpectedly full!\n");
#else
;
#endif
#ifdef WINDEVDEBUG
if (ws_qput_debug) printf("q put id %D value %D avail %D sec %D usec %D\n",
event->id, event->value, vq_avail(&ws->ws_q),
event->time.tv_sec, event->time.tv_usec);
#endif
}
int
ws_usr_async_check(ws, wua, fe)
Workstation *ws;
register Ws_usr_async *wua;
register Firm_event *fe;
{
if (wua->flags & WUA_1ST_HAPPENED) {
wua->flags &= ~WUA_1ST_HAPPENED;
/* This is second event; see if matches */
if ((fe->id == wua->second_id) &&
(fe->value == wua->second_value)) {
wua->action(ws);
return (-1);
}
} else {
/* This is first event; see if matches */
if ((fe->id == wua->first_id) &&
(fe->value == wua->first_value)) {
if (wua->flags & WUA_IGNORE_2ND) {
wua->action(ws);
return (-1);
} else
wua->flags |= WUA_1ST_HAPPENED;
}
}
return (0);
}
ws_loc_is_still(ws)
Workstation *ws;
{
Firm_event fe;
fe.id = LOC_STILL;
fe.pair_type = FE_PAIR_NONE;
fe.pair = 0;
fe.value = 1;
fe.time = time;
ws_consume_event(ws, &fe);
}
void
ws_break_handler(ws)
Workstation *ws;
{
if (ws->ws_flags & WSF_LOCKED_EVENT) {
wlok_forceunlock(&ws->ws_eventlock);
#ifdef WINDEVDEBUG
if (ws_break_msg)
printf("Event lock broken by user\n");
#endif
}
}
void
ws_flush_handler(ws)
Workstation *ws;
{
ws_flush_input(ws);
#ifdef WINDEVDEBUG
if (ws_flush_msg)
printf("Window input queue flushed by user!\n");
#endif
}
void
ws_stop_handler(ws)
register Workstation *ws;
{
Firm_event fe;
Window *w;
fe.id = WIN_STOP;
fe.pair_type = FE_PAIR_NONE;
fe.pair = 0;
fe.value = 1;
fe.time = time;
if ((ws->ws_loc_dtop != DESKTOP_NULL) &&
((w = ws->ws_loc_dtop->dt_cursorwin) != WINDOW_NULL)) {
/* Break event lock so that this gets through right away */
wlok_forceunlock(&ws->ws_eventlock);
/* Send stop event to real time pick window right now */
(void) win_send_one(w, &fe, &w->w_pickmask, INPUTMASK_NULL);
/* Send a urgent io signal */
winsignal(w, SIGURG);
#ifdef WINDEVDEBUG
if (ws_stop_msg)
printf("Stop sent\n");
#endif
}
}
/*
* ws_scale_event returns the scale factor of a locator motion.
* It assumes the locator sample shows motion (is a delta, not an
* absolute, coordinate), whose value fits in 16 bits. (The current
* mouse protocol implies the value is in the range +/- 127.)
*
* The old decision not to scale on the first motion after some
* time with the locator still is applied before ws_scale_event
* is called; however, it's now possible to leave an identity scaling
* for small values of motion in any case.
*
* It also scales X and Y independently, which may be considered an
* error. The alternative is to return a scale factor, given a
* dx and dy (or the sum of their squares, to do it right). But
* that assumes dx and dy are being handled together, which isn't
* currently the case. Getting that would require beefing up the
* vuid queue & state.
*/
int
ws_scale_event(sample)
register int *sample;
{
register short value;
register Ws_scale *scale;
value = *sample;
if (value < 0)
value = -value;
scale = ws_scaling;
while (value > scale->ceiling
)
scale++;
*sample *= scale->factor;
}
/*
* Check if in playback mode. If in playback, consume by svj routine.
*/
int
ws_consume_input_event(ws, event)
register Workstation *ws;
register Firm_event *event;
{
#ifdef WINSVJ
if (ws->ws_flags & WSF_PLAY_BACK) /* In playback mode */
return(svj_consume_input_event(ws, event));
#endif
ws_consume_event(ws, event); /* Normal processing */
return(1);
}