- ADB has its own interrupt flag, INTFLAG_ADB

- ADBMouseMoved(), ADBMouseDown/Up() and ADBKeyDown/Up() trigger the ADB
  interrupt
- ADB mutex is only used for mouse movement (the only input state where it
  matters)
- adb.cpp: toggling relative mouse mode resets mouse_x/y
- PrimeTime(0) schedules a timer task with 0 delay time; this is still not
  the correct implementation, but it makes MacSyndicate work...
- Unix: pthreads are preferred to POSIX.4 timers for 60Hz ticks because the
  timers drift badly under Linux and the thread can compensate for drifting
  well enough
- Unix: moved GetTicks_usec() and Delay_usec() to timer_unix.cpp
- video_x.cpp: X mouse acceleration is disabled in relative mouse mode because
  MacOS does its own acceleration
- video_x.cpp: palette[].pixel and palette[].flags are always preset
- video_x.cpp: decoupled X event handling from 60Hz video refresh cycle by
  using select() with a timeout on the X fd
This commit is contained in:
cebix 2001-07-09 11:22:01 +00:00
parent 8d733ed691
commit 5868a40a37
10 changed files with 266 additions and 194 deletions

View File

@ -7,10 +7,15 @@ V1.0 (snapshot) - <date>
thread-safe mouse handling
- the TIME_OFFSET constant has been replaced by a (portable) function
TimeToMacTime(); file dates in ExtFS should now be correct
- ADBInterrupt() is no longer called from the 60Hz interrupt but has
its own interrupt flag, potentially increasing the smoothness of
mouse movement
- Unix: windowed display mode supports different resolutions and color
depth, which can be switched on-the-fly
depths, which can be switched on-the-fly
- Unix: Ctrl-F5 grabs mouse in windowed mode (enhanced compatibility
with games like flight simulators)
- Unix: X11 events are handled as soon as they arrive, outside of the
60Hz video refresh raster
- Unix: audio sample rate, bit depth and channel count are adjustable
in the MacOS "Sound" control panel

View File

@ -616,9 +616,14 @@ Keyboard:
On PC-style keyboards, "Alt" is the Mac "Command" key, while the "Windows"
key is the Mac "Option" key.
Mouse:
Under Unix, press Ctrl-F5 while the Basilisk II window is active will grab
the mouse. This is needed for compatibility with some MacOS programs,
especially games. Press Ctrl-F5 again to return to normal mouse operation.
Floppy:
Basilisk II can only handle 1.44MB MFM floppies. Depending on your platform,
flopyy disk changes might not be detected automatically. Under Linux, press
floppy disk changes might not be detected automatically. Under Unix, press
Ctrl-F1 to mount a floppy. Under BeOS, select the appropriate "Mount" menu
item or press Ctrl-F1 to mount a floppy. Under Windows, press Ctrl-Shift-F11.

View File

@ -475,7 +475,28 @@ int main(int argc, char **argv)
sigaction(SIGINT, &sigint_sa, NULL);
#endif
#if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
#if defined(HAVE_PTHREADS)
// POSIX threads available, start 60Hz thread
pthread_attr_init(&tick_thread_attr);
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
if (geteuid() == 0) {
pthread_attr_setinheritsched(&tick_thread_attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&tick_thread_attr, SCHED_FIFO);
struct sched_param fifo_param;
fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2;
pthread_attr_setschedparam(&tick_thread_attr, &fifo_param);
}
#endif
tick_thread_active = (pthread_create(&tick_thread, &tick_thread_attr, tick_func, NULL) == 0);
if (!tick_thread_active) {
sprintf(str, GetString(STR_TICK_THREAD_ERR), strerror(errno));
ErrorAlert(str);
QuitEmulator();
}
D(bug("60Hz thread started\n"));
#elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
// POSIX.4 timers and real-time signals available, start 60Hz timer
sigemptyset(&timer_sa.sa_mask);
@ -506,27 +527,6 @@ int main(int argc, char **argv)
}
D(bug("60Hz timer started\n"));
#elif defined(HAVE_PTHREADS)
// POSIX threads available, start 60Hz thread
pthread_attr_init(&tick_thread_attr);
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
if (geteuid() == 0) {
pthread_attr_setinheritsched(&tick_thread_attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&tick_thread_attr, SCHED_FIFO);
struct sched_param fifo_param;
fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2;
pthread_attr_setschedparam(&tick_thread_attr, &fifo_param);
}
#endif
tick_thread_active = (pthread_create(&tick_thread, &tick_thread_attr, tick_func, NULL) == 0);
if (!tick_thread_active) {
sprintf(str, GetString(STR_TICK_THREAD_ERR), strerror(errno));
ErrorAlert(str);
QuitEmulator();
}
D(bug("60Hz thread started\n"));
#else
// Start 60Hz timer
@ -575,10 +575,7 @@ void QuitEmulator(void)
Exit680x0();
#endif
#if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
// Stop 60Hz timer
timer_delete(timer);
#elif defined(HAVE_PTHREADS)
#if defined(HAVE_PTHREADS)
// Stop 60Hz thread
if (tick_thread_active) {
tick_thread_cancel = true;
@ -587,6 +584,9 @@ void QuitEmulator(void)
#endif
pthread_join(tick_thread, NULL);
}
#elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS)
// Stop 60Hz timer
timer_delete(timer);
#else
struct itimerval req;
req.it_interval.tv_sec = req.it_value.tv_sec = 0;
@ -849,6 +849,8 @@ static void one_tick(...)
#ifdef HAVE_PTHREADS
static void *tick_func(void *arg)
{
uint64 start = GetTicks_usec();
int64 ticks = 0;
uint64 next = GetTicks_usec();
while (!tick_thread_cancel) {
one_tick();
@ -858,110 +860,15 @@ static void *tick_func(void *arg)
Delay_usec(delay);
else if (delay < -16625)
next = GetTicks_usec();
ticks++;
}
uint64 end = GetTicks_usec();
D(bug("%Ld ticks in %Ld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
return NULL;
}
#endif
/*
* Get current value of microsecond timer
*/
uint64 GetTicks_usec(void)
{
#ifdef HAVE_CLOCK_GETTIME
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
return (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000;
#else
struct timeval t;
gettimeofday(&t, NULL);
return (uint64)t.tv_sec * 1000000 + t.tv_usec;
#endif
}
/*
* Delay by specified number of microseconds (<1 second)
* (adapted from SDL_Delay() source; this function is designed to provide
* the highest accuracy possible)
*/
#if defined(linux)
// Linux select() changes its timeout parameter upon return to contain
// the remaining time. Most other unixen leave it unchanged or undefined.
#define SELECT_SETS_REMAINING
#elif defined(__FreeBSD__) || defined(__sun__)
#define USE_NANOSLEEP
#elif defined(HAVE_PTHREADS) && defined(sgi)
// SGI pthreads has a bug when using pthreads+signals+nanosleep,
// so instead of using nanosleep, wait on a CV which is never signalled.
#define USE_COND_TIMEDWAIT
#endif
void Delay_usec(uint32 usec)
{
int was_error;
#if defined(USE_NANOSLEEP)
struct timespec elapsed, tv;
#elif defined(USE_COND_TIMEDWAIT)
// Use a local mutex and cv, so threads remain independent
pthread_cond_t delay_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t delay_mutex = PTHREAD_MUTEX_INITIALIZER;
struct timespec elapsed;
uint64 future;
#else
struct timeval tv;
#ifndef SELECT_SETS_REMAINING
uint64 then, now, elapsed;
#endif
#endif
// Set the timeout interval - Linux only needs to do this once
#if defined(SELECT_SETS_REMAINING)
tv.tv_sec = 0;
tv.tv_usec = usec;
#elif defined(USE_NANOSLEEP)
elapsed.tv_sec = 0;
elapsed.tv_nsec = usec * 1000;
#elif defined(USE_COND_TIMEDWAIT)
future = GetTicks_usec() + usec;
elapsed.tv_sec = future / 1000000;
elapsed.tv_nsec = (future % 1000000) * 1000;
#else
then = GetTicks_usec();
#endif
do {
errno = 0;
#if defined(USE_NANOSLEEP)
tv.tv_sec = elapsed.tv_sec;
tv.tv_nsec = elapsed.tv_nsec;
was_error = nanosleep(&tv, &elapsed);
#elif defined(USE_COND_TIMEDWAIT)
was_error = pthread_mutex_lock(&delay_mutex);
was_error = pthread_cond_timedwait(&delay_cond, &delay_mutex, &elapsed);
was_error = pthread_mutex_unlock(&delay_mutex);
#else
#ifndef SELECT_SETS_REMAINING
// Calculate the time interval left (in case of interrupt)
now = GetTicks_usec();
elapsed = now - then;
then = now;
if (elapsed >= usec)
break;
usec -= elapsed;
tv.tv_sec = 0;
tv.tv_usec = usec;
#endif
was_error = select(0, NULL, NULL, NULL, &tv);
#endif
} while (was_error && (errno == EINTR));
}
#if !EMULATED_68K
/*
* Virtual 68k interrupt handler

View File

@ -22,6 +22,8 @@
#include "macos_util.h"
#include "timer.h"
#include <errno.h>
#define DEBUG 0
#include "debug.h"
@ -196,3 +198,101 @@ int32 timer_host2mac_time(tm_time_t hosttime)
return -t; // Time in negative microseconds
}
}
/*
* Get current value of microsecond timer
*/
uint64 GetTicks_usec(void)
{
#ifdef HAVE_CLOCK_GETTIME
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
return (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000;
#else
struct timeval t;
gettimeofday(&t, NULL);
return (uint64)t.tv_sec * 1000000 + t.tv_usec;
#endif
}
/*
* Delay by specified number of microseconds (<1 second)
* (adapted from SDL_Delay() source; this function is designed to provide
* the highest accuracy possible)
*/
#if defined(linux)
// Linux select() changes its timeout parameter upon return to contain
// the remaining time. Most other unixen leave it unchanged or undefined.
#define SELECT_SETS_REMAINING
#elif defined(__FreeBSD__) || defined(__sun__)
#define USE_NANOSLEEP
#elif defined(HAVE_PTHREADS) && defined(sgi)
// SGI pthreads has a bug when using pthreads+signals+nanosleep,
// so instead of using nanosleep, wait on a CV which is never signalled.
#define USE_COND_TIMEDWAIT
#endif
void Delay_usec(uint32 usec)
{
int was_error;
#if defined(USE_NANOSLEEP)
struct timespec elapsed, tv;
#elif defined(USE_COND_TIMEDWAIT)
// Use a local mutex and cv, so threads remain independent
pthread_cond_t delay_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t delay_mutex = PTHREAD_MUTEX_INITIALIZER;
struct timespec elapsed;
uint64 future;
#else
struct timeval tv;
#ifndef SELECT_SETS_REMAINING
uint64 then, now, elapsed;
#endif
#endif
// Set the timeout interval - Linux only needs to do this once
#if defined(SELECT_SETS_REMAINING)
tv.tv_sec = 0;
tv.tv_usec = usec;
#elif defined(USE_NANOSLEEP)
elapsed.tv_sec = 0;
elapsed.tv_nsec = usec * 1000;
#elif defined(USE_COND_TIMEDWAIT)
future = GetTicks_usec() + usec;
elapsed.tv_sec = future / 1000000;
elapsed.tv_nsec = (future % 1000000) * 1000;
#else
then = GetTicks_usec();
#endif
do {
errno = 0;
#if defined(USE_NANOSLEEP)
tv.tv_sec = elapsed.tv_sec;
tv.tv_nsec = elapsed.tv_nsec;
was_error = nanosleep(&tv, &elapsed);
#elif defined(USE_COND_TIMEDWAIT)
was_error = pthread_mutex_lock(&delay_mutex);
was_error = pthread_cond_timedwait(&delay_cond, &delay_mutex, &elapsed);
was_error = pthread_mutex_unlock(&delay_mutex);
#else
#ifndef SELECT_SETS_REMAINING
// Calculate the time interval left (in case of interrupt)
now = GetTicks_usec();
elapsed = now - then;
then = now;
if (elapsed >= usec)
break;
usec -= elapsed;
tv.tv_sec = 0;
tv.tv_usec = usec;
#endif
was_error = select(0, NULL, NULL, NULL, &tv);
#endif
} while (was_error && (errno == EINTR));
}

View File

@ -80,7 +80,7 @@ enum {
// Constants
const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | StructureNotifyMask;
static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask | StructureNotifyMask;
static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
@ -429,12 +429,17 @@ public:
virtual void toggle_mouse_grab(void) {}
virtual void mouse_moved(int x, int y) { ADBMouseMoved(x, y); }
void disable_mouse_accel(void);
void restore_mouse_accel(void);
virtual void grab_mouse(void) {}
virtual void ungrab_mouse(void) {}
public:
bool init_ok; // Initialization succeeded (we can't use exceptions because of -fomit-frame-pointer)
Window w; // The window we draw into
int orig_accel_numer, orig_accel_denom, orig_threshold; // Original mouse acceleration
};
class driver_window;
@ -478,11 +483,13 @@ driver_base::driver_base()
{
the_buffer = NULL;
the_buffer_copy = NULL;
XGetPointerControl(x_display, &orig_accel_numer, &orig_accel_denom, &orig_threshold);
}
driver_base::~driver_base()
{
ungrab_mouse();
restore_mouse_accel();
if (w) {
XUnmapWindow(x_display, w);
@ -535,6 +542,18 @@ void driver_base::update_palette(void)
XSync(x_display, false);
}
// Disable mouse acceleration
void driver_base::disable_mouse_accel(void)
{
XChangePointerControl(x_display, True, False, 1, 1, 0);
}
// Restore mouse acceleration to original value
void driver_base::restore_mouse_accel(void)
{
XChangePointerControl(x_display, True, True, orig_accel_numer, orig_accel_denom, orig_threshold);
}
/*
* Windowed display driver
@ -728,9 +747,9 @@ void driver_window::grab_mouse(void)
Delay_usec(100000);
}
if (result == GrabSuccess) {
ADBSetRelMouseMode(mouse_grabbed = true);
XStoreName(x_display, w, GetString(STR_WINDOW_TITLE_GRABBED));
XSync(x_display, false);
ADBSetRelMouseMode(mouse_grabbed = true);
disable_mouse_accel();
}
}
@ -741,6 +760,7 @@ void driver_window::ungrab_mouse(void)
XUngrabPointer(x_display, CurrentTime);
XStoreName(x_display, w, GetString(STR_WINDOW_TITLE));
ADBSetRelMouseMode(mouse_grabbed = false);
restore_mouse_accel();
}
}
@ -836,6 +856,7 @@ void driver_dga::suspend(void)
#endif
XUngrabPointer(x_display, CurrentTime);
XUngrabKeyboard(x_display, CurrentTime);
restore_mouse_accel();
XUnmapWindow(x_display, w);
wait_unmapped(w);
@ -865,6 +886,7 @@ void driver_dga::resume(void)
XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
disable_mouse_accel();
#ifdef ENABLE_XF86_DGA
XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
XF86DGASetViewPort(x_display, screen, 0, 0);
@ -1011,6 +1033,7 @@ driver_fbdev::driver_fbdev(const video_mode &mode)
XGrabPointer(x_display, w, True,
PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
GrabModeAsync, GrabModeAsync, w, None, CurrentTime);
disable_mouse_accel();
// Calculate bytes per row
int bytes_per_row = TrivialBytesPerRow(mode.x, mode.depth);
@ -1150,6 +1173,7 @@ driver_xf86dga::driver_xf86dga(const video_mode &mode)
XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
disable_mouse_accel();
int v_width, v_bank, v_size;
XF86DGAGetVideo(x_display, screen, (char **)&the_buffer, &v_width, &v_bank, &v_size);
@ -1344,12 +1368,18 @@ static bool video_open(const video_mode &mode)
--bloss;
}
// Preset palette pixel values for gamma table
// Preset palette pixel values for CLUT or gamma table
if (color_class == DirectColor) {
int num = vis->map_entries;
for (int i=0; i<num; i++) {
int c = (i * 256) / num;
palette[i].pixel = map_rgb(c, c, c);
palette[i].flags = DoRed | DoGreen | DoBlue;
}
} else if (color_class == PseudoColor) {
for (int i=0; i<256; i++) {
palette[i].pixel = i;
palette[i].flags = DoRed | DoGreen | DoBlue;
}
}
@ -1360,9 +1390,6 @@ static bool video_open(const video_mode &mode)
palette[i].red = c * 0x0101;
palette[i].green = c * 0x0101;
palette[i].blue = c * 0x0101;
if (color_class == PseudoColor)
palette[i].pixel = i;
palette[i].flags = DoRed | DoGreen | DoBlue;
}
if (cmap[0] && cmap[1]) {
XStoreColors(x_display, cmap[0], palette, num);
@ -1371,7 +1398,7 @@ static bool video_open(const video_mode &mode)
#ifdef ENABLE_VOSF
// Load gray ramp to 8->16/32 expand map
if (!IsDirectMode(mode) && (color_class == TrueColor || color_class == DirectColor))
if (!IsDirectMode(mode) && xdepth > 8)
for (int i=0; i<256; i++)
ExpandMap[i] = map_rgb(i, i, i);
#endif
@ -1722,15 +1749,12 @@ void video_set_palette(uint8 *pal, int num_in)
p->red = pal[c*3 + 0] * 0x0101;
p->green = pal[c*3 + 1] * 0x0101;
p->blue = pal[c*3 + 2] * 0x0101;
if (color_class == PseudoColor)
p->pixel = i;
p->flags = DoRed | DoGreen | DoBlue;
p++;
}
#ifdef ENABLE_VOSF
// Recalculate pixel color expansion map
if (!IsDirectMode(VideoMonitor.mode) && (color_class == TrueColor || color_class == DirectColor)) {
if (!IsDirectMode(VideoMonitor.mode) && xdepth > 8) {
for (int i=0; i<256; i++) {
int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier)
ExpandMap[i] = map_rgb(pal[c*3+0], pal[c*3+1], pal[c*3+2]);
@ -1943,6 +1967,7 @@ static void handle_events(void)
XNextEvent(x_display, &event);
switch (event.type) {
// Mouse button
case ButtonPress: {
unsigned int button = event.xbutton.button;
@ -1975,6 +2000,12 @@ static void handle_events(void)
drv->mouse_moved(event.xmotion.x, event.xmotion.y);
break;
// Mouse entered window
case EnterNotify:
if (event.xcrossing.mode != NotifyGrab && event.xcrossing.mode != NotifyUngrab)
drv->mouse_moved(event.xmotion.x, event.xmotion.y);
break;
// Keyboard
case KeyPress: {
int code = -1;
@ -2311,12 +2342,6 @@ static void video_refresh_dga(void)
{
// Quit DGA mode if requested
possibly_quit_dga_mode();
// Handle X events
handle_events();
// Handle palette changes
handle_palette_changes();
}
#ifdef ENABLE_VOSF
@ -2326,12 +2351,6 @@ static void video_refresh_dga_vosf(void)
// Quit DGA mode if requested
possibly_quit_dga_mode();
// Handle X events
handle_events();
// Handle palette changes
handle_palette_changes();
// Update display (VOSF variant)
static int tick_counter = 0;
if (++tick_counter >= frame_skip) {
@ -2350,12 +2369,6 @@ static void video_refresh_window_vosf(void)
// Ungrab mouse if requested
possibly_ungrab_mouse();
// Handle X events
handle_events();
// Handle palette changes
handle_palette_changes();
// Update display (VOSF variant)
static int tick_counter = 0;
if (++tick_counter >= frame_skip) {
@ -2375,12 +2388,6 @@ static void video_refresh_window_static(void)
// Ungrab mouse if requested
possibly_ungrab_mouse();
// Handle X events
handle_events();
// Handle_palette changes
handle_palette_changes();
// Update display (static variant)
static int tick_counter = 0;
if (++tick_counter >= frame_skip) {
@ -2394,12 +2401,6 @@ static void video_refresh_window_dynamic(void)
// Ungrab mouse if requested
possibly_ungrab_mouse();
// Handle X events
handle_events();
// Handle_palette changes
handle_palette_changes();
// Update display (dynamic variant)
static int tick_counter = 0;
tick_counter++;
@ -2435,32 +2436,69 @@ static void VideoRefreshInit(void)
}
}
// This function is called on non-threaded platforms from a timer interrupt
void VideoRefresh(void)
{
// We need to check redraw_thread_active to inhibit refreshed during
// mode changes on non-threaded platforms
if (redraw_thread_active)
video_refresh();
if (!redraw_thread_active)
return;
// Handle X events
handle_events();
// Handle palette changes
handle_palette_changes();
// Update display
video_refresh();
}
const int VIDEO_REFRESH_HZ = 60;
const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ;
#ifdef HAVE_PTHREADS
static void *redraw_func(void *arg)
{
int fd = ConnectionNumber(x_display);
uint64 start = GetTicks_usec();
int64 ticks = 0;
uint64 next = GetTicks_usec();
uint64 next = GetTicks_usec() + VIDEO_REFRESH_DELAY;
while (!redraw_thread_cancel) {
video_refresh();
next += 16667;
int64 delay = next - GetTicks_usec();
if (delay > 0)
Delay_usec(delay);
else if (delay < -16667)
if (delay < -VIDEO_REFRESH_DELAY) {
// We are lagging far behind, so we reset the delay mechanism
next = GetTicks_usec();
ticks++;
} else if (delay <= 0) {
// Delay expired, refresh display
handle_events();
handle_palette_changes();
video_refresh();
next += VIDEO_REFRESH_DELAY;
ticks++;
} else {
// No display refresh pending, check for X events
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = delay;
if (select(fd+1, &readfds, NULL, NULL, &timeout) > 0)
handle_events();
}
}
uint64 end = GetTicks_usec();
// printf("%Ld ticks in %Ld usec = %Ld ticks/sec\n", ticks, end - start, ticks * 1000000 / (end - start));
D(bug("%Ld refreshes in %Ld usec = %f refreshes/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
return NULL;
}
#endif

View File

@ -57,7 +57,7 @@ static uint8 mouse_reg_3[2] = {0x63, 0x01}; // Mouse ADB register 3
static uint8 key_reg_2[2] = {0xff, 0xff}; // Keyboard ADB register 2
static uint8 key_reg_3[2] = {0x62, 0x05}; // Keyboard ADB register 3
// ADB mouse input state lock (for platforms that use separate input thread)
// ADB mouse motion lock (for platforms that use separate input thread)
static B2_mutex *mouse_lock;
@ -231,6 +231,8 @@ void ADBMouseMoved(int x, int y)
mouse_x = x; mouse_y = y;
}
B2_unlock_mutex(mouse_lock);
SetInterruptFlag(INTFLAG_ADB);
TriggerInterrupt();
}
@ -240,21 +242,21 @@ void ADBMouseMoved(int x, int y)
void ADBMouseDown(int button)
{
B2_lock_mutex(mouse_lock);
mouse_button[button] = true;
B2_unlock_mutex(mouse_lock);
SetInterruptFlag(INTFLAG_ADB);
TriggerInterrupt();
}
/*
* First mouse button released
* Mouse button released
*/
void ADBMouseUp(int button)
{
B2_lock_mutex(mouse_lock);
mouse_button[button] = false;
B2_unlock_mutex(mouse_lock);
SetInterruptFlag(INTFLAG_ADB);
TriggerInterrupt();
}
@ -264,7 +266,10 @@ void ADBMouseUp(int button)
void ADBSetRelMouseMode(bool relative)
{
relative_mouse = relative;
if (relative_mouse != relative) {
relative_mouse = relative;
mouse_x = mouse_y = 0;
}
}
@ -280,6 +285,10 @@ void ADBKeyDown(int code)
// Set key in matrix
key_states[code >> 3] |= (1 << (~code & 7));
// Trigger interrupt
SetInterruptFlag(INTFLAG_ADB);
TriggerInterrupt();
}
@ -295,6 +304,10 @@ void ADBKeyUp(int code)
// Clear key in matrix
key_states[code >> 3] &= ~(1 << (~code & 7));
// Trigger interrupt
SetInterruptFlag(INTFLAG_ADB);
TriggerInterrupt();
}

View File

@ -440,7 +440,6 @@ void EmulOp(uint16 opcode, M68kRegisters *r)
if (HasMacStarted()) {
// Mac has started, execute all 60Hz interrupt functions
ADBInterrupt();
TimerInterrupt();
VideoInterrupt();
@ -457,7 +456,6 @@ void EmulOp(uint16 opcode, M68kRegisters *r)
if (InterruptFlags & INTFLAG_1HZ) {
ClearInterruptFlag(INTFLAG_1HZ);
if (HasMacStarted()) {
SonyInterrupt();
DiskInterrupt();
@ -480,11 +478,16 @@ void EmulOp(uint16 opcode, M68kRegisters *r)
AudioInterrupt();
}
if (InterruptFlags & INTFLAG_ADB) {
ClearInterruptFlag(INTFLAG_ADB);
if (HasMacStarted())
ADBInterrupt();
}
if (InterruptFlags & INTFLAG_NMI) {
ClearInterruptFlag(INTFLAG_NMI);
if (HasMacStarted()) {
if (HasMacStarted())
TriggerNMI();
}
}
break;

View File

@ -66,7 +66,8 @@ enum {
INTFLAG_ETHER = 8, // Ethernet driver
INTFLAG_AUDIO = 16, // Audio block read
INTFLAG_TIMER = 32, // Time Manager
INTFLAG_NMI = 64 // NMI
INTFLAG_ADB = 64, // ADB
INTFLAG_NMI = 128 // NMI
};
extern uint32 InterruptFlags; // Currently pending interrupts

View File

@ -249,10 +249,8 @@ int16 PrimeTime(uint32 tm, int32 time)
//!! PrimeTime(0) means continue previous delay
// (save wakeup time in RmvTime?)
if (time == 0) {
printf("FATAL: Unsupported PrimeTime(0)\n");
return 0;
}
if (time == 0)
printf("WARNING: Unsupported PrimeTime(0)\n");
// Yes, calculate wakeup time relative to last scheduled time
tm_time_t wakeup;

View File

@ -23,6 +23,8 @@
* SEE ALSO
* Inside Macintosh: Devices, chapter 1 "Device Manager"
* Designing Cards and Drivers for the Macintosh Family, Second Edition
* Designing PCI Cards and Drivers for Power Macintosh Computers
* Display Device Driver Guide
*/
#include <stdio.h>