#ifndef lint #ifdef sccs static char sccsid[] = "@(#)pw_shared.c 1.1 94/10/31 Copyr 1987 Sun Micro"; #endif #endif /* * Copyright (c) 1987 by Sun Microsystems, Inc. */ /* * Pw_shared.c: Use shared memory for faster locking. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for crosshairs active check */ static void pw_do_shared_lock(), pw_do_shared_unlock(); #define pw_release_lock(which_lock) \ { \ (which_lock)->count = 0; \ (which_lock)->pid = 0; \ (which_lock)->lock = 0; \ } typedef struct pw_dtop_entry { char dtop_name[SCR_NAMESIZE]; int ref_count; Win_lock_block *shared_info; } Pw_dtop_entry; /* set this to TRUE to turn off the shared locking */ /* initialize pw_disable_shared_locking to 0. */ int pw_disable_shared_locking = 0; static Pw_dtop_entry pw_dtop_table[WIN_LOCK_MAX_DTOP]; static int pw_lock_block_bytes; static int pw_mypid; /* return TRUE if lock already taken */ #define pw_get_lock(which_lock) \ ((pw_test_set(&(which_lock)->lock)) ? \ TRUE : ((which_lock)->id++, (which_lock)->count = 1, \ (which_lock)->pid = pw_mypid, FALSE)) pw_shared_lock(pw, rect_paint) register Pixwin *pw; Rect *rect_paint; { register Win_lock_block *wl = pw->pw_clipdata->pwcd_wl; /* * Cursors, 24-bit planegroups, and shared locking don't currently * mix too well. */ if (pw->pw_clipdata->pwcd_plane_groups_available[PIXPG_24BIT_COLOR]) return(FALSE); /* * if double buffering is on, we do not want to use the shared * memory lock or else pw_rop and friends will behave abnormally * (for cg{3,5} only) */ if ((pw->pw_clipdata->pwcd_flags & PWCD_DBL_AVAIL) && !pw->pw_clipdata->pwcd_plane_groups_available[PIXPG_24BIT_COLOR] && pw_dbl_rect(pw)) return(FALSE); /* * See if locked already. Assume that don't get here if this * pixwin has the display lock already. * Grabio and window data locks take precedence over display lock. */ if (!wl || pw_get_lock(&wl->mutex)) return FALSE; /* * Don't do own locking if io is grabbed. This is because we * aren't prepared to adequately track the cursor when the * pixwin has multiple plane groups available. We could beef * this up to check to see if multiple available planes are * present before we go to pw_release_lock. * Also, bail out if going to to be taking cursor down * that was put up over another plane group. */ if (wl->waiting || wl->go_to_kernel || win_lock_grabio_locked(wl) || (win_lock_data_locked(wl) && wl->data.pid != pw_mypid) || (win_lock_display_locked(wl) && wl->display.pid != pw_mypid) || (pw->pw_clipdata->pwcd_plane_group != wl->cursor_info.plane_group)){ pw_release_lock(&wl->mutex); return FALSE; } /* See if pixwin clip data out-of-date or xhairs involved */ if ((pw->pw_clipdata->pwcd_clipid != wl->clip_ids[pw->pw_clipdata->pwcd_winnum]) || show_horiz_hair(&wl->cursor_info.cursor) || show_vert_hair(&wl->cursor_info.cursor)) { /* Since have to call kernel to get clipping ... */ pw_release_lock(&wl->mutex); return FALSE; } /* acquire display lock */ if (win_lock_display_locked(wl)) { /* already locked, so bump the count */ wl->display.count++; } else if (pw_get_lock(&wl->display)) { /* locked already (somehow) */ pw_release_lock(&wl->mutex); return FALSE; } pw_do_shared_lock(pw, rect_paint); pw_release_lock(&wl->mutex); return TRUE; } static void pw_do_shared_lock(pw, rect_paint) register Pixwin *pw; Rect *rect_paint; { Win_lock_block *wl = pw->pw_clipdata->pwcd_wl; register Win_shared_cursor *shared_cursor = &wl->cursor_info; int original_planes; int planes; #ifdef ecd.cursor short olddata; #endif ecd.cursor /* * See if cursor is active, up on the screen and intersects * area that are going to paint in. If a cursor plane exists - * i.e spare3 is set, you dont have to paint it in */ if (shared_cursor->cursor_active && shared_cursor->cursor_is_up && pw_cursor_intersect_exposed(pw, rect_paint) && (!shared_cursor->spare3)) { /* Take cursor off of the screen */ if (pw->pw_clipdata->pwcd_flags & PWCD_CURSOR_INVERTED) (void)pr_reversevideo(pw->pw_pixrect, 0, 1); win_lock_prepare_screen_pr(shared_cursor); /* Set up full planes */ (void)pr_getattributes(pw->pw_pixrect, &original_planes); planes = PIX_ALL_PLANES; #ifdef planes_fully_implemented pr_putattributes(pw->pw_pixrect, &planes); #else (void)pw_full_putattributes(pw->pw_pixrect, &planes); #endif planes_fully_implemented #ifdef ecd.cusror /* This fix is mainly for MS windows which runs in * reverse video. When we rop from the MS win pixrect * we would use a reverse rop. However, the kernel uses * the shared memory pixrect when it moves the cursor * and doesn't know we rop'd from a reverse video window. * The kernel just uses the display pixrect which has the * attributes that were used when sunview was started. * The fix is to clear the window pixrect MP_REVERSEVIDEO * flag while we rop into the memory pixrect. The kernel * also has some similar code to fix the situation where * sunview was started with -i (inverse). (See sys/ * sunwindowdev/windt.c) */ olddata = ((struct mpr_data *)(pw->pw_pixrect->pr_data))->md_flags; ((struct mpr_data *)(pw->pw_pixrect->pr_data))->md_flags &= ~MP_REVERSEVIDEO; #endif ecd.cursor (void)pr_rop(pw->pw_pixrect, shared_cursor->x, shared_cursor->y, shared_cursor->screen_pr.pr_width, shared_cursor->screen_pr.pr_height, PIX_SRC, &shared_cursor->screen_pr, 0, 0); #ifdef ecd.cursor ((struct mpr_data *)(pw->pw_pixrect->pr_data))->md_flags = olddata; #endif ecd.cursor /* Reset planes */ #ifdef planes_fully_implemented pr_putattributes(pw->pw_pixrect, &original_planes); #else (void)pw_full_putattributes(pw->pw_pixrect, &original_planes); #endif planes_fully_implemented if (pw->pw_clipdata->pwcd_flags & PWCD_CURSOR_INVERTED) (void)pr_reversevideo(pw->pw_pixrect, 0, 1); shared_cursor->cursor_is_up = FALSE; } #ifdef DEBUG printf("locked display\n"); #endif } pw_shared_unlock(pw) register Pixwin *pw; { register Win_lock_block *wl = pw->pw_clipdata->pwcd_wl; register Win_shared_cursor *shared_cursor; /* * Cursors, 24-bit planegroups, and shared locking don't currently * mix too well. */ if (pw->pw_clipdata->pwcd_plane_groups_available[PIXPG_24BIT_COLOR]) return(FALSE); /* * if double buffering is on, we do not want to use the shared * memory unlock or else pw_rop and friends will behave abnormally * (for cg{3,5} only) */ if ((pw->pw_clipdata->pwcd_flags & PWCD_DBL_AVAIL) && !pw->pw_clipdata->pwcd_plane_groups_available[PIXPG_24BIT_COLOR] && pw_dbl_rect(pw)) return(FALSE); if (!wl || pw_get_lock(&wl->mutex)) return FALSE; shared_cursor = &wl->cursor_info; /* See if we have it locked, no one waiting for * lock and no xhairs. Also, bail out if going to * to be putting cursor up over another plane group. * * Also, don't do own locking if io is grabbed. This is because we * aren't prepared to adequately track the cursor when the * pixwin has multiple plane groups available. */ if (wl->waiting || wl->go_to_kernel || win_lock_grabio_locked(wl) || wl->status.new_cursor || !win_lock_display_locked(wl) || (wl->display.pid != pw_mypid) || (pw->pw_clipdata->pwcd_plane_group != wl->mouse_plane_group) || show_horiz_hair(&shared_cursor->cursor) || show_vert_hair(&shared_cursor->cursor)) { pw_release_lock(&wl->mutex); return FALSE; } wl->display.count--; if (wl->display.count <= 0) { /* Release the lock */ pw_do_shared_unlock(pw); pw_release_lock(&wl->display); } /* release the mutex lock */ pw_release_lock(&wl->mutex); return TRUE; } static void pw_do_shared_unlock(pw) register Pixwin *pw; { register Win_lock_block *wl = pw->pw_clipdata->pwcd_wl; register Win_shared_cursor *shared_cursor = &wl->cursor_info; int original_planes; int planes; #ifdef ecd.cursor short olddata; struct mpr_data *mpds, *mpdd; #endif ecd.cursor #ifdef DEBUG printf("unlocked display\n"); #endif /* only draw the cursor if not up, active and want it or * we dont have cursor planes */ if ((shared_cursor->spare3)|| (shared_cursor->cursor_is_up) || (!shared_cursor->cursor_active) || (!(show_cursor(&shared_cursor->cursor) && shared_cursor->pr_data.md_image && shared_cursor->pr.pr_width && shared_cursor->pr.pr_depth && shared_cursor->pr.pr_height))) return; /* Set up full planes */ (void)pr_getattributes(pw->pw_pixrect, &original_planes); planes = PIX_ALL_PLANES; #ifdef planes_fully_implemented pr_putattributes(pw->pw_pixrect, &planes); #else (void)pw_full_putattributes(pw->pw_pixrect, &planes); #endif planes_fully_implemented /* * Get current mouse position. * Race with kernel but not critical. */ shared_cursor->x = wl->mouse_x; shared_cursor->y = wl->mouse_y; /* Note what plane group using */ shared_cursor->plane_group = pw->pw_clipdata->pwcd_plane_group; /* adjust for the hot spot */ shared_cursor->x -= shared_cursor->cursor.cur_xhot; shared_cursor->y -= shared_cursor->cursor.cur_yhot; if (pw->pw_clipdata->pwcd_flags & PWCD_CURSOR_INVERTED) (void)pr_reversevideo(pw->pw_pixrect, 0, 1); /* save the bits under the cursor */ shared_cursor->screen_pr.pr_width = shared_cursor->pr.pr_width; shared_cursor->screen_pr.pr_height = shared_cursor->pr.pr_height; win_lock_prepare_screen_pr(shared_cursor); #ifdef ecd.cursor olddata = ((struct mpr_data *)(pw->pw_pixrect->pr_data))->md_flags; ((struct mpr_data *)(pw->pw_pixrect->pr_data))->md_flags &= ~MP_REVERSEVIDEO; #endif ecd.cursor (void)pr_rop(&shared_cursor->screen_pr, 0, 0, shared_cursor->screen_pr.pr_width, shared_cursor->screen_pr.pr_height, PIX_SRC, pw->pw_pixrect, shared_cursor->x, shared_cursor->y); #ifdef ecd.cursor ((struct mpr_data *)(pw->pw_pixrect->pr_data))->md_flags = olddata; #endif ecd.cursor /* * Reset planes so that will write to foreground and background * of current colormap segment. */ #ifdef planes_fully_implemented pr_putattributes(pw->pw_pixrect, &original_planes); #else (void)pw_full_putattributes(pw->pw_pixrect, &original_planes); #endif planes_fully_implemented /* Put cursor on the screen */ win_lock_prepare_cursor_pr(shared_cursor); (void)pr_rop(pw->pw_pixrect, shared_cursor->x, shared_cursor->y, shared_cursor->screen_pr.pr_width, shared_cursor->screen_pr.pr_height, shared_cursor->cursor.cur_function, &shared_cursor->pr, 0, 0); shared_cursor->cursor_is_up = TRUE; if (pw->pw_clipdata->pwcd_flags & PWCD_CURSOR_INVERTED) (void)pr_reversevideo(pw->pw_pixrect, 0, 1); } static int pw_cursor_intersect_exposed(pw, rect_paint) register Pixwin *pw; Rect *rect_paint; { Win_lock_block *shared_info = pw->pw_clipdata->pwcd_wl; Win_shared_cursor *cursor_info = &shared_info->cursor_info; Rect rect; int have_grabio; /* if the data lock is ours, we can't trust the * screen_x, screen_Y coordinates of the window, since * a win_setrect() could have been done without changing * the clipping id. So play it safe and take down the cursor. */ if (win_lock_data_locked(shared_info) && (shared_info->data.pid == pw_mypid)) return TRUE; rect_construct(&rect, cursor_info->x, cursor_info->y, cursor_info->pr.pr_width, cursor_info->pr.pr_height); /* transform to window space */ rect_passtochild(pw->pw_clipdata->pwcd_screen_x, pw->pw_clipdata->pwcd_screen_y, &rect); /* * Since the clipping list is in the space of the pixwin, * transform the window-space cursor rect to region space. */ if (pw->pw_clipdata->pwcd_regionrect) rect_passtochild(pw->pw_clipdata->pwcd_regionrect->r_left, pw->pw_clipdata->pwcd_regionrect->r_top, &rect); /* cursor must be on the intended paint area to intersect. * Also, either this process must have the grabio lock or * the cursor must be in the exposed rectlist of pw. */ have_grabio = win_lock_grabio_locked(shared_info) && (shared_info->grabio.pid == pw_mypid); return (rect_intersectsrect(&rect, rect_paint) && (have_grabio || rl_rectintersects(&rect, &pw->pw_clipdata->pwcd_clipping))); } /* Defining MAP_FIXED is for compiling under 3.4 environment */ #ifndef MAP_FIXED #define MAP_FIXED 0 #endif Win_lock_block * pw_get_lock_block(windowfd) int windowfd; { struct screen screen; register char *dtop_name = screen.scr_fbname; register int i; register Pw_dtop_entry *entry; Pw_dtop_entry *free_entry = 0; Win_lock_block *shared_info; static Win_lock_block *last_valloc; /* = 0 */ static int wrong_version; /* = 0 */ char *strcpy(); if (wrong_version || pw_disable_shared_locking) return 0; pw_mypid = getpid(); (void)win_screenget(windowfd, &screen); /* see if this dtop is already mapped in */ for (i = 0, entry = &pw_dtop_table[i]; i < WIN_LOCK_MAX_DTOP; i++, entry = &pw_dtop_table[i]) if (!entry->shared_info) { if (!free_entry) free_entry = entry; } else if (strcmp(dtop_name, entry->dtop_name) == 0) { entry->ref_count++; return entry->shared_info; } /* not already in the table, so there better be a * free slot. */ if (!free_entry) return 0; /* there is a free slot, so mmap the shared info */ if (!pw_lock_block_bytes) pw_lock_block_bytes = round_to_page(sizeof(Win_lock_block)); /* Note that here we mmap() using the windowfd and that * other windows will share this mmaped memory. * If close(windowfd) is changed to munmap() the storage, * the other windows will be left pointing to invalid storage. */ if (last_valloc) shared_info = last_valloc; else { extern char *valloc(); shared_info = (Win_lock_block *) valloc((u_int)pw_lock_block_bytes); } if ((int)mmap((caddr_t)shared_info, pw_lock_block_bytes, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, windowfd, 0) == -1) { last_valloc = shared_info; return 0; } /* if the kernel has a different version number, * don't use the shared memory. */ if (shared_info->version != WIN_LOCK_VERSION) { wrong_version = TRUE; return 0; } last_valloc = 0; entry = free_entry; entry->shared_info = shared_info; (void) strcpy(entry->dtop_name, dtop_name); entry->ref_count = 1; return shared_info; } void pw_free_lock_block(shared_info) register Win_lock_block *shared_info; { if (!shared_info) return; /* Don't free up the shared memory, since this is probably * no big win (and a waste of cycles). for (i = 0, entry = &pw_dtop_table[i]; i < WIN_LOCK_MAX_DTOP; i++, entry = &pw_dtop_table[i]) if (shared_info == entry->shared_info) { entry->ref_count--; if (entry->ref_count <= 0) { munmap(shared_info, pw_lock_block_bytes); free(shared_info); } return; } */ } /* Update process specific data */ pw_fork() { pw_mypid = getpid(); } static int round_to_page(bytes) register int bytes; { register int pagesize = getpagesize(); register int roundup = pagesize-(bytes%pagesize); if (roundup != pagesize) bytes += roundup; return(bytes); } pw_reset_lock(pw) Pixwin *pw; { Win_lock_block *wl = pw->pw_clipdata->pwcd_wl; if (!wl) return; pw_release_lock(&wl->mutex); pw_release_lock(&wl->display); } /* turn shared locking on or off */ pw_set_shared_locking(on) int on; { /* pw_disable_shared_locking = !on; */ }