#ifndef lint static char sccsid[] = "@(#)ws_dispense.c 1.1 94/10/31"; #endif /* * Copyright (c) 1986 by Sun Microsystems, Inc. */ /* * SunWindows Workstation code that is responsible for handling the * top of the input queue, dispensing events at the appropriate times * to the appropriate windows. */ #include #include #include /* for time */ #include /* for plane groups */ static void ws_set_dtop_grabio(); void ws_set_dtop_waiting(); void dtop_restore_grnds(); void dtop_save_grnds(); void dtop_set_bkgnd(); /* TODO: How about some sort of meta mask? */ /* TODO: Worry about ALL_LOCKS */ #ifdef WINDEVDEBUG int ws_qget_debug; /* Temp: Debugging messages when dequeue firm event */ int ws_qputback_debug; /* Temp: Debugging messages when putback firm event */ int ws_set_focus_debug; /* Temp: Debugging messages when ws_set_focus called */ int ws_track_debug; /* Temp: Debug messages in ws_set_pick_focus_next */ #endif int ws_focus_during_each_transit; /* Temp: Set focus if loc moves & in transit*/ int ws_verbose_event_lock; /* Turn on event lock broken messages */ int ws_no_in_transit; int ws_no_collapse; extern int ws_q_collapse_trigger; /* # q items that trigger collapse */ #define ws_is_loc_change(id) \ ((((id) >= LOC_FIRST_DELTA) && ((id) <= LOC_LAST_ABSOLUTE)) || \ ((id) == LOC_MOVE)) Firm_event ws_get_event(); int ws_lock_control_pid; /* Used in lock control enumerator */ int ws_empty_input_flag; /* Used in lock control enumerator */ int shift_statearray_meta[META_LAST-META_FIRST+1]; /* Array to keep the state of the shift keys when a key goes down, if it's a possible META event */ ws_window_nudge(w) Window *w; { register Workstation *ws; register Desktop *dtop; dtop = w->w_desktop; if (dtop == DESKTOP_NULL) return; win_sharedq_shift(w); ws = dtop->dt_ws; if (ws == WORKSTATION_NULL) return; /* * If window's process is currently unsyncronized, see if can * allow it to acquire the event lock. */ if (w->w_flags & WF_UNSYNCHRONIZED) ws_try_event_enable(w); /* If no current event lock holder then nudge next event off queue */ if (~ws->ws_flags & WSF_LOCKED_EVENT) ws_nudge(ws); /* * If window's process is current event lock holder & there is * no input on window's queue then nudge next event (if any) off * of queue. Before do that, unlock current event lock because * next event may not be for this window. */ else if ((ws->ws_event_consumer != WINDOW_NULL) && (ws->ws_event_consumer->w_pid == w->w_pid) && (dtop->dt_sharedq_owner == w ? (dtop->shared_info->shared_eventq.head == dtop->shared_info->shared_eventq.tail) : (ws->ws_event_consumer->w_input.c_cc < sizeof (struct inputevent)))) { wlok_unlock(&ws->ws_eventlock); ws_nudge(ws); } } /* * Try to send an event to the one of the input focuses. * Any caller of ws_nudge that has the event lock should unlock it first. * * We preserve the user's preception of the ordering amoung LOC_WINENTER, * LOC_WINEXIT, KBD_REQUEST, KBD_USE, and KBD_DONE, reguardless of kbd * focus change event type. However, KBD_REQUESTs are not issued when * LOC_WINENTER is the kbd focus change event. Here is the order: * * LOC_WINEXIT * LOC_WINENTER * KBD_REQUEST * KBD_DONE * KBD_USE * * It might seem that the KBD_DONE ought to follow the LOC_WINEXIT in the * LOC_WINEXIT kbd focus change event case. But this is hard to implement * and doesn't move well to a MS_LEFT kbd focus change event case. */ ws_nudge(ws) register Workstation *ws; { Firm_event firm_event; /* Gotten from input q */ Firm_event fe; /* Contructed */ /* * See if some process/window in middle of processing input. */ if (ws->ws_flags & WSF_LOCKED_EVENT) return; /* Collapse locator events at top of queue to keep from being full */ ws_collapse_q(ws); /* * Go around focus change state machine until need to waiting * for event consumer to clear the event lock. */ while (~ws->ws_flags & WSF_LOCKED_EVENT) { /* Pre-set fe fields */ fe.value = 1; /* Set to one because event has no neg */ if (ws->ws_flags & WSF_SEND_FOCUS_EVENT) fe.time = ws->ws_focus_event.time; else if (!vq_is_empty(&ws->ws_q)) fe.time = ws->ws_q.top->firm_event.time; else fe.time = time; fe.pair = 0; fe.pair_type = FE_PAIR_NONE; switch (ws->ws_focus_state) { case SEND_EXIT: /* Send LOC_WINEXIT to ws_pickfocus */ fe.id = LOC_WINEXIT; if (ws->ws_pickfocus) (void) win_send_one(ws->ws_pickfocus, &fe, &ws->ws_pickfocus->w_pickmask, INPUTMASK_NULL); /* Set in-transit flag */ if (!ws_no_in_transit) if (ws->ws_flags & WSF_LATEST_WAS_MOTION) ws->ws_flags |= WSF_LOC_IN_TRANSIT; /* Move to next state */ ws->ws_focus_state = SEND_ENTER; break; case SEND_ENTER: /* * Do in-transit processing, by conditionally gliding * over windows without waking them up. */ ws_window_transit(ws); /* See if completed focus transition */ if (ws->ws_flags & WSF_LOC_IN_TRANSIT) { /* * See if should ignore in-transit locator * event surpression. */ if(ws->ws_pickfocus_next && (ws->ws_pickfocus_next->w_pickmask.im_flags & IM_INTRANSIT)) { /* Continue */ } else return; } ws_set_focus(ws, FF_PICK_CHANGE); /* Send LOC_WINENTER to ws_pickfocus_next */ fe.id = LOC_WINENTER; if (ws->ws_pickfocus_next) (void) win_send_one(ws->ws_pickfocus_next, &fe, &ws->ws_pickfocus_next->w_pickmask, INPUTMASK_NULL); /* Update ws_pickfocus */ ws->ws_pickfocus = ws->ws_pickfocus_next; /* Move to next state */ ws->ws_focus_state = SEND_Q_TOP; break; case SEND_DIRECT_REQUEST: ws_send_kbd_request(ws, ws->ws_kbdfocus_next, &fe); break; case SEND_REQUEST: ws_send_kbd_request(ws, ws->ws_pickfocus, &fe); break; case REQUEST_WAIT: /* * If requesting flag is set then didn't disapprove * of the request. */ if (ws->ws_flags & WSF_KBD_REQUEST_PENDING) { /* Clear requesting flag */ ws->ws_flags &= ~WSF_KBD_REQUEST_PENDING; /* Move to next state */ ws->ws_focus_state = SEND_DONE; } else { /* Reset ws_kbdfocus_next */ ws->ws_kbdfocus_next = ws->ws_kbdfocus; /* Move to next state */ ws->ws_focus_state = SEND_FOCUS_EVENT; } break; case SEND_DONE: /* Send KBD_DONE to ws_kbdfocus */ fe.id = KBD_DONE; if (ws->ws_kbdfocus) (void) win_send_one(ws->ws_kbdfocus, &fe, &ws->ws_kbdfocus->w_kbdmask, INPUTMASK_NULL); /* Move to next state */ ws->ws_focus_state = SEND_USE; break; case SEND_USE: /* Send KBD_USE to ws_kbdfocus_next */ fe.id = KBD_USE; if (ws->ws_kbdfocus_next) (void) win_send_one(ws->ws_kbdfocus_next, &fe, &ws->ws_kbdfocus_next->w_kbdmask, INPUTMASK_NULL); /* Update ws_kbdfocus */ ws->ws_kbdfocus = ws->ws_kbdfocus_next; /* Move to next state */ if ((ws->ws_flags & WSF_SWALLOW_FOCUS_EVENT) || (!(ws->ws_flags & WSF_SEND_FOCUS_EVENT))) ws->ws_focus_state = SEND_Q_TOP; else ws->ws_focus_state = SEND_FOCUS_EVENT; ws->ws_flags &= ~WSF_SWALLOW_FOCUS_EVENT; break; case SEND_FOCUS_EVENT: /* * Send ws_focus_event to kbd or pick focus. If did a * win_send on this event then would have windows * jumping around because of passing, for example, * MS_LEFT's up to a tool parent (which cause a top). */ if ((ws->ws_kbdfocus == WINDOW_NULL) || (win_send_one(ws->ws_kbdfocus, &ws->ws_focus_event, &ws->ws_kbdfocus->w_kbdmask, INPUTMASK_NULL) == -1)) { if (ws->ws_pickfocus) { (void) win_send_one(ws->ws_pickfocus, &ws->ws_focus_event, &ws->ws_pickfocus->w_pickmask, INPUTMASK_NULL); } } /* Clear focus event flags */ ws->ws_flags &= ~WSF_SEND_FOCUS_EVENT; /* Move to next state */ ws->ws_focus_state = SEND_Q_TOP; break; case SEND_Q_TOP: /* Could break into smaller states */ /* * Set state if new focuses and break. Gets any focus * changes since last got to this state. */ if (ws_set_focus_state(ws)) break; /* * Get top of q and use this event to try to change * focuses. Nothing to do if the input queue is empty. */ if (vq_is_empty(&ws->ws_q)) return; firm_event = ws_get_event(ws); /* * Set focus if LOC_MOVE. Also, if LOC_STILL set the * focus because all the LOC_MOVE's may have been * collapsed away. * * Order of swallow vs pass thru test is important. * Since swallow contricts what should be done, it * comes first. */ if ((firm_event.id == LOC_MOVE) || (firm_event.id == LOC_STILL)) ws_set_focus(ws, FF_PICK_CHANGE); else if ((firm_event.id == ws->ws_kbd_focus_sw.id) && ws_focus_match(ws, &ws->ws_kbd_focus_sw, &firm_event)) { ws->ws_flags |= WSF_SWALLOW_FOCUS_EVENT; ws->ws_focus_event = firm_event; ws_set_focus(ws, FF_KBD_EVENT); } else if ((firm_event.id == ws->ws_kbd_focus_pt.id) && ws_focus_match(ws, &ws->ws_kbd_focus_pt, &firm_event)) { ws->ws_flags &= ~WSF_SWALLOW_FOCUS_EVENT; ws->ws_focus_event = firm_event; ws_set_focus(ws, FF_KBD_EVENT); } /* Send firm_event if no new focuses */ if (ws_set_focus_state(ws)) break; else { /* Clear focus event flag if haven't yet */ ws->ws_flags &= ~WSF_SEND_FOCUS_EVENT; /* Send batched events all at once */ if (firm_event.id == VLOC_BATCH) ws_send_batch(ws, firm_event.value); else win_send(ws, &firm_event); } break; #ifdef WINDEVDEBUG default: printf("Unknown input state\n"); #endif } } } ws_send_kbd_request(ws, w, fe) Workstation *ws; Window *w; Firm_event *fe; { /* * If LOC_WINENTER is not the kbd focus change event and * event timeout is not zero (both of which make a * kbd change request not workable) then give * application a chance to say no to change. */ fe->id = KBD_REQUEST; if (((ws->ws_kbd_focus_sw.id != LOC_WINENTER) || (ws->ws_kbd_focus_pt.id != LOC_WINENTER)) && (timerisset(&ws->ws_eventtimeout)) && (w != WINDOW_NULL) && (win_send_one(w, fe, &w->w_pickmask, INPUTMASK_NULL) != -1)) { /* Set requesting flag */ ws->ws_flags |= WSF_KBD_REQUEST_PENDING; /* Move to next state */ ws->ws_focus_state = REQUEST_WAIT; } else /* Move to next state */ ws->ws_focus_state = SEND_DONE; } int ws_focus_match(ws, fs, fe) Workstation *ws; Ws_focus_set *fs; Firm_event *fe; { if ((fe->id == fs->id) && (vuid_get_value(ws->ws_instate, fe->id) == fs->value) && ((fs->shifts == WS_FOCUS_ANY_SHIFT) || (((fs->shifts & ws->ws_shiftmask) ^ fs->shifts) == 0))) return (1); else return (0); } /* Return 1 if change focus state due to new focuses, return 0 otherwise */ int ws_set_focus_state(ws) register Workstation *ws; { /* Do error check */ if (ws->ws_focus_state != SEND_Q_TOP) { #ifdef WINDEVDEBUG printf("Focus state should be SEND_Q_TOP %D\n", ws->ws_focus_state); #endif ws->ws_focus_state = SEND_Q_TOP; return (1); } /* * If current pick focus not equal to next pick focus * then set to new state (SEND_ENTER or SEND_EXIT) * and return 1. */ if (ws->ws_pickfocus != ws->ws_pickfocus_next) { if (ws->ws_pickfocus == WINDOW_NULL) ws->ws_focus_state = SEND_ENTER; else ws->ws_focus_state = SEND_EXIT; return (1); } /* * If current kbd focus not equal to next kbd focus * then set to new state (SEND_REQUEST) and return 1. */ if (ws->ws_kbdfocus != ws->ws_kbdfocus_next) { ws->ws_focus_state = SEND_REQUEST; return (1); } return (0); } /* Conditionally collapse locator events at top of input queue */ ws_collapse_q(ws) register Workstation *ws; { Firm_event peek_fe; Firm_event collapse_fe; int collapsed = 0; extern u_int ws_vq_node_bytes; extern void ws_shrink_queue(); if (ws_no_collapse) return; if (vq_used(&ws->ws_q) == 0 && ws->ws_qbytes > ws_vq_node_bytes) ws_shrink_queue(ws); /* * Don't collapse if going to send focus event because will mess up * shift mask state and loc positions when get around to sending, later. */ if (ws->ws_flags & WSF_SEND_FOCUS_EVENT) return; /* Don't collapse if pick focus is tracking trajectory */ #ifndef lint if ((ws->ws_pickfocus != WINDOW_NULL) && (win_getinputcodebit(&ws->ws_pickfocus->w_pickmask,LOC_TRAJECTORY))) return; #endif while ((vq_used(&ws->ws_q) > 1) && /* Check possibility of collapse */ (vq_used(&ws->ws_q) > ws_q_collapse_trigger) && (vq_peek(&ws->ws_q, &peek_fe) != VUID_Q_EMPTY)) { if ((ws_is_loc_change(peek_fe.id)) || (peek_fe.id == LOC_STILL)) { collapse_fe = ws_get_event(ws); collapsed = 1; } else break; } /* * Put back last event taken so don't wipe out event trigger * and loose an event. */ if (collapsed) { if (ws_is_loc_change(collapse_fe.id)) collapse_fe.id = LOC_MOVE; /* else will be LOC_STILL */ collapse_fe.value = 1; /* Set to one because event has no neg */ /* use given time */ collapse_fe.pair = 0; collapse_fe.pair_type = FE_PAIR_NONE; #ifdef WS_DEBUG if (ws_qputback_debug) printf("q putback id %D value %D avail %D\n", collapse_fe.id, collapse_fe.value, vq_avail(&ws->ws_q)); #endif WS_DEBUG if (vq_putback(&ws->ws_q, &collapse_fe) != VUID_Q_OK) #ifdef WINDEVDEBUG printf("putback error\n"); #else ; #endif } } /* * Non-wakeup window transition capability over windows which are looking * for LOC_MOVEs, LOC_WINENTERs, LOC_WINEXITs, KBD_USE and KBD_DONE. */ ws_window_transit(ws) register Workstation *ws; { Firm_event firm_event; int dequeued_loc_change = 0; while (ws->ws_flags & WSF_LOC_IN_TRANSIT) { /* Peek at top of input queue */ if (vq_peek(&ws->ws_q, &firm_event) == VUID_Q_EMPTY) break; if (!ws_is_loc_change(firm_event.id)) { ws->ws_flags &= ~WSF_LOC_IN_TRANSIT; } else { /* Get locator motion from input queue */ firm_event = ws_get_event(ws); dequeued_loc_change = 1; if (ws_focus_during_each_transit) ws_set_focus(ws, FF_PICK_CHANGE); } } if (dequeued_loc_change && !ws_focus_during_each_transit) ws_set_focus(ws, FF_PICK_CHANGE); } /* * Depending on flags and ws fields update either ws_kbdfocus_next or * ws_pickfocus_next or both. FF_PICK_CHANGE or FF_KBD_EVENT are the flags. * * This routine is called in a variety of circumstances: * * 1) A user action has been pulled off the top of the input q that might * require a change in focus. * 2) A window is being closed that held one or both of the input focuses * thus forcing the selection of a new focus. * 3) A change of window clipping, visibility or position that might * affect locator position dependent focus changing (the pick focus * will surely be affected). Programmatic setting of the locator * position is a similar situation. * 4) When a workstation is first started and there is no focus. * 5) When a window has grabbed or released the input focus. * 6) When trying to complete a focus transition. * * A focus is never null unless starting out or current focus closed. * There is enough difference between a LOC_WINENTER based kbd focus change * event and others (say MS_LEFT) that we treat the others separately. */ void ws_set_focus(ws, flags) register Workstation *ws; int flags; { #ifdef WS_DEBUG if (ws_set_focus_debug) printf( "ws_set_focus start: flags %X k %X kn %X p %X pn %X\n", flags, ws->ws_kbdfocus, ws->ws_kbdfocus_next, ws->ws_pickfocus, ws->ws_pickfocus_next); #endif WS_DEBUG /* Check pick focus if might have changed */ if (flags & (FF_PICK_CHANGE | FF_KBD_EVENT)) ws_set_pick_focus_next(ws); /* If kbd focus event is LOC_WINENTER follow pick focus */ if (ws->ws_kbd_focus_sw.id == LOC_WINENTER && ws->ws_kbd_focus_pt.id == LOC_WINENTER) ws->ws_kbdfocus_next = ws->ws_pickfocus_next; /* Set both focuses from ws_inputgrabber */ if (ws->ws_inputgrabber != WINDOW_NULL) { ws->ws_pickfocus_next = ws->ws_inputgrabber; ws->ws_kbdfocus_next = ws->ws_inputgrabber; return; } /* Update kbd focus on a focus change event */ if (flags & FF_KBD_EVENT) { ws->ws_flags |= WSF_SEND_FOCUS_EVENT; ws->ws_kbdfocus_next = ws->ws_pickfocus_next; } #ifdef WS_DEBUG if (ws_set_focus_debug) printf("ws_set_focus end: k %X kn %X p %X pn %X\n", ws->ws_kbdfocus, ws->ws_kbdfocus_next, ws->ws_pickfocus, ws->ws_pickfocus_next); #endif WS_DEBUG } /* * Take next item off input queue. * Take two if locator changes if have same time. */ Firm_event ws_get_event(ws) register Workstation *ws; { Firm_event firm_event; register Desktop *dtop_pick = ws->ws_pick_dtop; Again: /* Get top of input queue */ if (vq_get(&ws->ws_q, &firm_event) == VUID_Q_EMPTY) { #ifdef WINDEVDEBUG printf("Window input: tried to read empty q\n"); #endif } #ifdef WINDEVDEBUG if (ws_qget_debug) printf("q get id %D value %D avail %D sec %D usec %D\n", firm_event.id, firm_event.value, vq_avail(&ws->ws_q), firm_event.time.tv_sec, firm_event.time.tv_usec); #endif /* Turn locator motion into LOC_MOVE */ if (ws_is_loc_change(firm_event.id)) { Firm_event loc_event; switch (firm_event.id) { case LOC_X_DELTA: dtop_pick->dt_ut_x += firm_event.value; break; case LOC_Y_DELTA: dtop_pick->dt_ut_y += firm_event.value; break; case LOC_X_ABSOLUTE: dtop_pick->dt_ut_x = firm_event.value; break; case LOC_Y_ABSOLUTE: dtop_pick->dt_ut_y = firm_event.value; break; case LOC_MOVE: break; default: {} } ws->ws_flags |= WSF_LATEST_WAS_MOTION; if (vq_peek(&ws->ws_q, &loc_event) == VUID_Q_EMPTY) goto LocMove; /* See if should merge with firm_event in to one event */ if ((ws_is_loc_change(loc_event.id)) && (tv_equal(loc_event.time, firm_event.time))) goto Again; LocMove: firm_event.id = LOC_MOVE; firm_event.value = 1; /* Set to one because event has no neg */ firm_event.pair = 0; firm_event.pair_type = FE_PAIR_NONE; /* use given time */ } else { /* Set user time state */ vuid_set_value(&ws->ws_instate, &firm_event); /* Maintain shiftmask state for win_sendevent */ ws_set_shiftmask(ws, &firm_event); /* Maintain state of shift keys when a key goes down, for META events. This is done as a META event may be missed if shift state has changed when it comes up */ if(firm_event.value == 1 && firm_event.id >= META_FIRST && firm_event.id <= META_LAST) shift_statearray_meta[firm_event.id-META_FIRST] = ws->ws_shiftmask; /* Maintain latest motion flag */ if (firm_event.id == LOC_STILL) ws->ws_flags &= ~WSF_LATEST_WAS_MOTION; } return (firm_event); } ws_flush_input(ws) register Workstation *ws; { register Desktop *dtop; while (!vq_is_empty(&ws->ws_q)) (void) ws_get_event(ws); vuid_destroy_state(ws->ws_instate); ws->ws_instate = vuid_copy_state(ws->ws_rtstate); if ((dtop = ws->ws_loc_dtop) != DESKTOP_NULL) { dtop->dt_ut_x = dtop->dt_rt_x; dtop->dt_ut_y = dtop->dt_rt_y; } bzero((caddr_t)&ws->ws_surpress_mask, sizeof (ws->ws_surpress_mask)); } ws_set_shiftmask(ws, fe) register Workstation *ws; register Firm_event *fe; { #define shift_mask_update(ws, fe, bit) \ if ((fe)->value) \ (ws)->ws_shiftmask |= (1 << (bit)); \ else \ (ws)->ws_shiftmask &= ~(1 << (bit)); switch (fe->id) { case SHIFT_CAPSLOCK: shift_mask_update(ws, fe, CAPSLOCK); break; case SHIFT_LOCK: shift_mask_update(ws, fe, SHIFTLOCK); break; case SHIFT_LEFT: shift_mask_update(ws, fe, LEFTSHIFT); break; case SHIFT_RIGHT: shift_mask_update(ws, fe, RIGHTSHIFT); break; case SHIFT_LEFTCTRL: shift_mask_update(ws, fe, LEFTCTRL); break; case SHIFT_RIGHTCTRL: shift_mask_update(ws, fe, RIGHTCTRL); break; case SHIFT_META: #define META_SHIFT_BIT 6 /* * Use of 6 instead of constant: Done at last minute in 3.0 * release so would have meta mask. Wanted to use METAMASK * (from kbd.h) but value is the same as the no longer * supported UPMASK. Chose to use 6 because corresponds to * the "unused... 0x0040" value in kbd.h. * META_SHIFT_MASK is in win_input.h & need to claim that it * is used in kbd.h. */ shift_mask_update(ws, fe, META_SHIFT_BIT); break; case SHIFT_ALT: shift_mask_update(ws, fe, ALT); break; default: /* No longer supporting UPMASK or CTLSMASK */ {} } } ws_set_pick_focus_next(ws) Workstation *ws; { Desktop *dtop_pick = ws->ws_pick_dtop; /* can't be register */ int x_now, y_now; /* can't be register */ if ((dtop_pick == NULL) || (dtop_pick->dt_rootwin == NULL)) return; /* Clamp locator to desktop or move onto adjoinning desktop */ x_now = dtop_pick->dt_ut_x; y_now = dtop_pick->dt_ut_y; #ifdef WS_DEBUG if (ws_track_debug) printf("TRACK dtop=%X, x=%D, y=%D\n", dtop_pick, x_now, y_now); #endif WS_DEBUG dtop_track_locator(&dtop_pick, &x_now, &y_now); #ifdef WS_DEBUG if (ws_track_debug) printf("RESULT dtop=%X, x=%D, y=%D\n", dtop_pick, x_now, y_now); #endif WS_DEBUG /* Reset pick desktop */ ws->ws_pick_dtop = dtop_pick; /* Update dtop_pick's notion of locator position */ dtop_pick->dt_ut_x = x_now; dtop_pick->dt_ut_y = y_now; /* Use locator position to determine pick focus */ ws->ws_pickfocus_next = wt_intersected(dtop_pick->dt_rootwin, x_now, y_now); } ws_send_batch(ws, n) register Workstation *ws; int n; { Firm_event firm_event; register i; if (vq_used(&ws->ws_q) < n) { #ifdef WINDEVDEBUG printf("ws_send_batch: batch number too large\n"); #endif return; } for (i = 0; i < n; i++) { if (vq_is_empty(&ws->ws_q)) break; firm_event = ws_get_event(ws); win_send(ws, &firm_event); } } /* return TRUE if a workstation lock is set or * a lock is set on the desktop. */ static int ws_check_all_locks(lok_client) caddr_t lok_client; { register Desktop *dtop = (Desktop *) lok_client; if (!dtop) return FALSE; return ((dtop->dt_ws->ws_flags & WSF_ALL_LOCKS) || dtop_check_all_locks((caddr_t) dtop)); } /* * Acquire the event processing lock for the event consumer window's process. * Kernel thread of control can't sleep, so must know that lock is * not on when call. Actually, unlike other locks, callers of this routine * should never block and it should never be locked multiple times. * This lock is acquired before placing an input event on a window's clist. */ extern int noproc; ws_lock_event(ws, w) register Workstation *ws; register Window *w; { void ws_unlock_event(), ws_timedout_event(), ws_force_event(); register struct proc *process; extern int noproc; /* true if no one is running */ if (ws->ws_flags & WSF_LOCKED_EVENT) { #ifdef WINDEVDEBUG printf("ws_lock_event: already locked\n"); #endif return; } if (w == WINDOW_NULL) { #ifdef WINDEVDEBUG printf("ws_lock_event: NULL window arg\n"); #endif return; } /* * If this process can not acquire the * event lock then no event synchronization. */ if (w->w_flags & WF_UNSYNCHRONIZED) return; /* Favor the event recipient */ ws_favor(ws, w); /* If event time out is not set then no event synchronization */ if (!timerisset(&ws->ws_eventtimeout)) return; /* * Get process pointer to use for lock * Note - cannot use u.u_procp if noproc is true! */ if (noproc == 0 && w->w_pid == u.u_procp->p_pid) { /* Case when read or select thread reacquiring lock */ process = u.u_procp; } else { extern struct proc *pfind(); /* Case when kernel thread acquiring lock (more expensive) */ if ((process = pfind(w->w_pid)) == ((struct proc *)0)) { /* Process has gone away */ return; } } wlok_setlock(&ws->ws_eventlock, &ws->ws_flags, WSF_LOCKED_EVENT, &ws->ws_eventlock.lok_count_storage, process); ws->ws_eventlock.lok_client = (caddr_t)w->w_desktop; ws->ws_eventlock.lok_unlock_action = ws_unlock_event; ws->ws_eventlock.lok_limit = ws->ws_eventtimeout; ws->ws_eventlock.lok_timeout_action = ws_timedout_event; ws->ws_eventlock.lok_string = "event"; ws->ws_eventlock.lok_force_action = ws_force_event; ws->ws_eventlock.lok_wakeup = (caddr_t) &ws->ws_flags; if (!ws_verbose_event_lock) ws->ws_eventlock.lok_options |= WLOK_SILENT; ws->ws_eventlock.lok_other_check = ws_check_all_locks; ws->ws_event_consumer = w; } /* * Unlock the access right to the next event on the queue. * ws_unlock_event doesn't care if the unlock is forced or * congenial. */ void ws_unlock_event(wlock) Winlock *wlock; { Desktop *dtop = (Desktop *)wlock->lok_client; Workstation *ws = dtop->dt_ws; ws->ws_event_consumer = WINDOW_NULL; } /* No-op but needed if doing timeout stuff */ void ws_timedout_event() { #ifdef WINDEVDEBUG if (ws_event_timeout_msg) printf("TIMEOUT EVENT\n"); #endif } /* * Make it so that the offending process doesn't acquire the event lock * until input queue empties or gets to read or select. */ void ws_force_event(wlock) Winlock *wlock; { Desktop *dtop = (Desktop *)wlock->lok_client; Workstation *ws = dtop->dt_ws; int win_dont_lock(); /* Find every window in this process and mark as not lockable */ ws_lock_control_pid = wlock->lok_pid; if (ws->ws_event_consumer && ws->ws_event_consumer->w_desktop) wt_enumeratechildren(win_dont_lock, ws->ws_event_consumer->w_desktop->dt_rootwin, (struct rect *)0); ws_lock_control_pid = 0; } /* * If window's process is currently unsyncronized, see if can * allow it to acquire the event lock. */ ws_try_event_enable(w) register Window *w; { int win_empty_input(); /* Find every window in this process and see if all have empty qs */ ws_lock_control_pid = w->w_pid; ws_empty_input_flag = 1; wt_enumeratechildren(win_empty_input, w->w_desktop->dt_rootwin, (struct rect *)0); ws_lock_control_pid = 0; if (ws_empty_input_flag) ws_enable_event(w); } /* * Make it so that the windows in w's process can acquire the event lock. */ ws_enable_event(w) register Window *w; { int win_can_lock(); /* Find every window in this process and mark as not lockable */ ws_lock_control_pid = w->w_pid; wt_enumeratechildren(win_can_lock, w->w_desktop->dt_rootwin, (struct rect *)0); ws_lock_control_pid = 0; } /* * Called from wt_enumeratechildren so don't change calling sequence */ /*ARGSUSED*/ win_dont_lock(w, rect) struct window *w; struct rect *rect; /* Don't use for anything else (not caddr_t) */ { if (!(w->w_flags & WF_OPEN) || (w->w_pid != ws_lock_control_pid)) return (-1); w->w_flags |= WF_UNSYNCHRONIZED; return (0); } /* * Called from wt_enumeratechildren so don't change calling sequence */ /*ARGSUSED*/ win_empty_input(w, rect) struct window *w; struct rect *rect; /* Don't use for anything else (not caddr_t) */ { if (!(w->w_flags & WF_OPEN) || (w->w_pid != ws_lock_control_pid)) return (-1); if (w->w_desktop->dt_sharedq_owner == w) { win_sharedq_shift(w); if (w->w_desktop->shared_info->shared_eventq.head == w->w_desktop->shared_info->shared_eventq.tail) ws_empty_input_flag = 0; } else if (w->w_input.c_cc != 0) ws_empty_input_flag = 0; return (0); } /* * Called from wt_enumeratechildren so don't change calling sequence */ /*ARGSUSED*/ win_can_lock(w, rect) struct window *w; struct rect *rect; /* Don't use for anything else (not caddr_t) */ { if (!(w->w_flags & WF_OPEN) || (w->w_pid != ws_lock_control_pid)) return (-1); w->w_flags &= ~WF_UNSYNCHRONIZED; return (0); } int ws_lockio(dev) dev_t dev; { register struct window *w = winfromdev(dev); register Desktop *dtop = w->w_desktop; register Workstation *ws = dtop->dt_ws; void ws_unlock_io(); /* Currently, locking a dev you have locked increments a ref count */ if ((ws->ws_flags & WSF_LOCKED_IO) == 0) { /* update the shared memory to show we are waiting */ ws_set_dtop_waiting(ws, 1); while ((ws->ws_flags & WSF_LOCKED_IO) && ws_dtop_mutex_locked(ws)) if (sleep((caddr_t)&ws->ws_flags, LOCKPRI|PCATCH)) { ws_set_dtop_waiting(ws, -1); return (-1); } /* update the shared memory to show we are not waiting */ ws_set_dtop_waiting(ws, -1); wlok_setlock(&ws->ws_iolock, &ws->ws_flags, WSF_LOCKED_IO, &ws->ws_iolock.lok_count_storage, u.u_procp); ws->ws_iolock.lok_client = (caddr_t)dtop; /* So can get to ws, too */ ws->ws_iolock.lok_unlock_action = ws_unlock_io; ws->ws_iolock.lok_timeout_action = NULL; ws->ws_iolock.lok_string = "io"; ws->ws_iolock.lok_wakeup = (caddr_t) &ws->ws_flags; ws->ws_iolock.lok_other_check = ws_check_all_locks; /* update the shared memory */ ws_set_dtop_grabio(ws, TRUE, ws->ws_iolock.lok_pid); dtop->dt_displaygrabber = w; ws->ws_inputgrabber = w; ws->ws_pre_grab_kbd_focus = ws->ws_kbdfocus; ws->ws_pre_grab_kbd_focus_next = ws->ws_kbdfocus_next; ws->ws_pre_grab_pick_focus = ws->ws_pickfocus; ws->ws_pre_grab_pick_focus_next = ws->ws_pickfocus_next; ws->ws_kbdfocus = w; ws->ws_pickfocus = w; ws_set_focus(ws, 0); /* Save extremes of std colormap, set to standard values */ dtop_save_grnds(dtop); dtop_set_bkgnd(dtop); dtop->dt_flags |= DTF_NEWCMAP; /* Set cursor */ dtop_set_cursor(dtop); /* Write cursor to enable plane */ if (dtop->dt_flags & DTF_MULTI_PLANE_GROUPS && dtop->dt_plane_groups_available[PIXPG_OVERLAY_ENABLE]) { enable_plane_cursor_set_active(dtop, TRUE); /* Choose color of enable plane cursor */ switch (w->w_plane_group) { case PIXPG_OVERLAY: case PIXPG_CURRENT: case PIXPG_MONO: #ifndef PRE_FLAMINGO case PIXPG_24BIT_COLOR: case PIXPG_VIDEO: #endif dtop->dt_cursor.enable_color = 1; break; default: dtop->dt_cursor.enable_color = 0; } } #ifndef PRE_FLAMINGO /* Write cursor to video enable plane */ if (dtop->dt_flags & DTF_MULTI_PLANE_GROUPS && dtop->dt_plane_groups_available[PIXPG_VIDEO_ENABLE]) videnb_plane_cursor_set_active(dtop, TRUE); #endif } else *(ws->ws_iolock.lok_count) += 1; return (0); } void ws_unlock_io(wlock) Winlock *wlock; { register Desktop *dtop = (Desktop *)wlock->lok_client; register Workstation *ws = dtop->dt_ws; dtop->dt_displaygrabber = NULL; ws->ws_inputgrabber = NULL; /* * This is a hack to force reloading of the colormap even * though it may not be needed. This is meant to give the * user a way that he can cleanup after a non-window * or errant program that left the color map messed up. * Moving a window, envoking a menu both are ways to cause * WINRELEASEIO to be called. */ dtop_restore_grnds(dtop); dtop->dt_flags |= DTF_NEWCMAP; ws->ws_kbdfocus = ws->ws_pre_grab_kbd_focus; ws->ws_kbdfocus_next = ws->ws_pre_grab_kbd_focus_next; ws->ws_pickfocus = ws->ws_pre_grab_pick_focus; ws->ws_pickfocus_next = ws->ws_pre_grab_pick_focus_next; ws_set_focus(ws, FF_PICK_CHANGE); /* * Stop writing cursor to enable plane. * Take down cursor first so that enable plane is restored. */ dtop_cursordown(dtop); enable_plane_cursor_set_active(dtop, FALSE); #ifndef PRE_FLAMINGO videnb_plane_cursor_set_active(dtop, FALSE); #endif dtop_set_cursor(dtop); dtop_cursorup(dtop); /* clear the grabio lock in each dtop's * shared memory. */ ws_set_dtop_grabio(ws, FALSE, 0); /* * The following wakeup is done because a WINLOCKSCREEN * may be waiting on io lock being released. */ wakeup((caddr_t)dtop->shared_info); } static int ws_dtop_mutex_locked(ws) Workstation *ws; { register Desktop *dtop; for (dtop = ws->ws_dtop; dtop; dtop = dtop->dt_next) if (win_lock_mutex_locked(dtop->shared_info)) return TRUE; return FALSE; } static void ws_set_dtop_grabio(ws, on, pid) Workstation *ws; int on; int pid; { register Desktop *dtop; for (dtop = ws->ws_dtop; dtop; dtop = dtop->dt_next) { win_lock_set(&dtop->shared_info->grabio, on); dtop->shared_info->grabio.pid = pid; } } void ws_set_dtop_waiting(ws, num) Workstation *ws; int num; { register Desktop *dtop; for (dtop = ws->ws_dtop; dtop; dtop = dtop->dt_next) { dtop->shared_info->waiting += num; } }