#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 #include #include /* For f_data */ #include #include /* 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"); }