executor/src/autorefresh.c

153 lines
3.8 KiB
C

/* Copyright 1995 by Abacus Research and
* Development, Inc. All rights reserved.
*/
#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_autorefresh[] =
"$Id: autorefresh.c 63 2004-12-24 18:19:43Z ctm $";
#endif
#include "rsys/common.h"
#include "rsys/autorefresh.h"
#include "rsys/vdriver.h"
#include "rsys/refresh.h"
#include "rsys/flags.h"
#include "rsys/prefs.h"
/* This file provides a mechanism to detect when applications are
* bypassing QuickDraw and writing directly to screen memory.
* It works by partitioning the screen into NUM_AUTOREFRESH_STRIPS
* horizontal strips of equal size. Each strip is periodically
* checksummed and compared against the last checksum. If the
* checksum value changes, and QuickDraw didn't change that area
* of the screen, we know we need to turn on "refresh" mode.
*/
static boolean_t executor_changed_screen_p[NUM_AUTOREFRESH_STRIPS];
/* Check auto refresh? */
boolean_t do_autorefresh_p;
/* Returns a value that will tend to change when the screen contents change
* for the specified horizontal strip on the screen.
*/
static uint32
checksum_strip (int which_strip)
{
int n, row_size, strip_rows, next_row_delta;
const uint32 *base;
uint32 sum;
strip_rows = vdriver_height / NUM_AUTOREFRESH_STRIPS;
base = (const uint32 *) (vdriver_fbuf
+ which_strip * strip_rows * vdriver_row_bytes);
row_size = vdriver_row_bytes / (4U * sizeof (uint32));
next_row_delta = (vdriver_row_bytes * 2 / sizeof (uint32)) - (row_size * 4U);
/* Sum (most of) the longs on every other row in this strip. */
for (sum = 0, n = strip_rows; n > 0; n -= 2)
{
int r;
for (r = row_size; r > 0; r--)
{
#if defined (i386)
uint32 t1, t2;
/* Hand-scheduled for the Pentium. gcc does a terrible job. */
asm ("movl (%3),%1\n\t"
"movl 4(%3),%2\n\t"
"addl %1,%0\n\t"
"movl 8(%3),%1\n\t"
"addl %2,%0\n\t"
"movl 12(%3),%2\n\t"
"addl %1,%0\n\t"
"addl $16,%3\n\t"
"addl %2,%0"
: "=r" (sum), "=r" (t1), "=r" (t2), "=r" (base)
: "0" (sum), "3" (base)
: "cc");
#else
sum += base[0] + base[1] + base[2] + base[3];
base += 4;
#endif
}
base += next_row_delta;
}
return sum;
}
/* Call this when part of the screen has been changed by Executor,
* so we don't mistakenly think we need refresh mode.
*/
void
note_executor_changed_screen (int top, int bottom)
{
int i, first, last, strip_rows;
strip_rows = vdriver_height / NUM_AUTOREFRESH_STRIPS;
first = top / strip_rows;
last = (bottom - 1) / strip_rows;
if (first >= (int) NUM_AUTOREFRESH_STRIPS || last < 0)
return;
if (first < 0)
first = 0;
if (last > (int) NUM_AUTOREFRESH_STRIPS - 1)
last = NUM_AUTOREFRESH_STRIPS - 1;
for (i = first; i <= last; i++)
executor_changed_screen_p[i] = TRUE;
}
/* Returns TRUE if refresh should be turned on. */
boolean_t
autodetect_refresh (void)
{
static uint32 last_checksum[NUM_AUTOREFRESH_STRIPS];
static boolean_t last_checksum_valid_p[NUM_AUTOREFRESH_STRIPS];
boolean_t need_refresh_p;
int i;
/* We don't need to autodetect refresh if we're already doing refresh
* or if we have direct screen access.
*/
if (ROMlib_refresh != 0 || !ROMlib_shadow_screen_p)
{
memset (last_checksum_valid_p, 0, sizeof last_checksum_valid_p);
return FALSE;
}
need_refresh_p = FALSE; /* default value */
/* Compute checksums for the screen. */
for (i = NUM_AUTOREFRESH_STRIPS - 1; i >= 0; i--)
{
if (executor_changed_screen_p[i])
{
executor_changed_screen_p[i] = FALSE; /* reset it */
last_checksum_valid_p[i] = FALSE;
}
else
{
uint32 c;
c = checksum_strip (i);
if (last_checksum_valid_p[i] && c != last_checksum[i])
need_refresh_p = TRUE;
last_checksum[i] = c;
last_checksum_valid_p[i] = TRUE;
}
}
return need_refresh_p;
}