From 1ffde615e02056012c6838ec8370026852786cec Mon Sep 17 00:00:00 2001
From: johns <>
Date: Sun, 31 Mar 2002 17:26:09 +0000
Subject: [PATCH] New video frame and game simulation timing code for X11.

---
 src/video/X11.cpp | 305 +++++++++++++++++-----------------------------
 src/video/sdl.cpp |   2 +-
 2 files changed, 116 insertions(+), 191 deletions(-)

diff --git a/src/video/X11.cpp b/src/video/X11.cpp
index 638e372ff..5f0fd5b24 100644
--- a/src/video/X11.cpp
+++ b/src/video/X11.cpp
@@ -87,7 +87,7 @@ local struct timeval X11TicksStart;	/// My counter start
 --	Sync
 ----------------------------------------------------------------------------*/
 
-#define USE_ITIMER
+#define noUSE_ITIMER			/// Use the old ITIMER code (obsolete)
 
 #ifdef USE_ITIMER
 
@@ -138,11 +138,35 @@ global void SetVideoSync(void)
 
 #else
 
+local int FrameTicks;			/// Frame length in ms
+local int FrameRemainder;		/// Frame remainder 0.1 ms
+local int FrameFraction;		/// Frame fractional term
+local int SkipFrames;			/// Skip this frames
+
 /**
 **	Initialise video sync.
+**	Calculate the length of video frame and any simulation skips.
+**
+**	@see VideoSyncSpeed @see SkipFrames @see FrameTicks @see FrameRemainder
 */
 global void SetVideoSync(void)
 {
+    int ms;
+
+    if( VideoSyncSpeed ) {
+	ms = (1000 * 1000 / CYCLES_PER_SECOND) / VideoSyncSpeed;
+    } else {
+	ms = INT_MAX;
+    }
+    SkipFrames = ms / 400;
+    while (SkipFrames && ms / SkipFrames < 200) {
+	--SkipFrames;
+    }
+    ms /= SkipFrames + 1;
+
+    FrameTicks = ms / 10;
+    FrameRemainder = ms % 10;
+    DebugLevel0Fn("frames %d - %d.%dms\n", SkipFrames, ms / 10, ms % 10);
 }
 
 #endif
@@ -165,12 +189,14 @@ local void MyConnectionWatch
 **	X11 get ticks in ms.
 **
 **	@return		Game ticks in ms.
+**
+**	@todo	Use if supported RDTSC.
 */
 global unsigned long X11GetTicks(void)
 {
     struct timeval now;
     unsigned long ticks;
- 
+
     gettimeofday(&now,NULL);
 
     ticks=(now.tv_sec-X11TicksStart.tv_sec)*1000
@@ -561,10 +587,10 @@ local void X11HandleModifiers(XKeyEvent* keyevent)
 	    /* Do Nothing */;
     }
     if( mod&ControlMask ) {
-        keyevent->state&=~ControlMask;  // Hack Attack!
+        keyevent->state&=~ControlMask;	// Hack Attack!
     }
     if( mod&Mod1Mask ) {
-        keyevent->state&=~Mod1Mask;     // Hack Attack!
+        keyevent->state&=~Mod1Mask;	// Hack Attack!
     }
 }
 
@@ -689,8 +715,8 @@ local unsigned X112InternalKeycode(const KeySym code)
 	    icode='^';
 	    break;
 
-        // We need these because if you only hit a modifier key,
-        // X doesn't set its state (modifiers) field in the keyevent.
+	// We need these because if you only hit a modifier key,
+	// X doesn't set its state (modifiers) field in the keyevent.
 	case XK_Shift_L:
 	case XK_Shift_R:
 	    icode = KeyCodeShift;
@@ -860,7 +886,7 @@ local void X11DoEvent(const EventCallback* callbacks)
 	}
 	    break;
 
-	case ConfigureNotify:	// IGNORE, not useful for us yet - 
+	case ConfigureNotify:	// IGNORE, not useful for us yet -
 				// we may be able to limit hidden map draw here later
 	    break;
 
@@ -882,6 +908,8 @@ local void X11DoEvent(const EventCallback* callbacks)
 **	Returns if the time for one frame is over.
 **
 **	@param callbacks	Call backs that handle the events.
+**
+**	FIXME:	the initialition could be moved out of the loop
 */
 global void WaitEventsOneFrame(const EventCallback* callbacks)
 {
@@ -894,15 +922,36 @@ global void WaitEventsOneFrame(const EventCallback* callbacks)
     int i;
     int morex;
     int connection;
+    unsigned long ticks;
 
     connection=ConnectionNumber(TheDisplay);
 #ifdef WITH_SOUND
+    // FIXME: ugly hack, move into sound part!!!
     if( SoundFildes==-1 ) {
 	SoundOff=1;
     }
 #endif
+    if( !++FrameCounter ) {
+	// FIXME: tests with frame counter now fails :(
+	// FIXME: Should happen in 68 years :)
+	fprintf(stderr,"FIXME: *** round robin ***\n");
+	fprintf(stderr,"FIXME: *** round robin ***\n");
+	fprintf(stderr,"FIXME: *** round robin ***\n");
+	fprintf(stderr,"FIXME: *** round robin ***\n");
+    }
 
-    InputMouseTimeout(callbacks,X11GetTicks());
+    ticks=X11GetTicks();
+    if( ticks>NextFrameTicks ) {	// We are too slow :(
+	IfDebug(
+	    VideoDrawText(TheUI.MapX+10,TheUI.MapY+10,GameFont,"SLOW FRAME!!");
+	    XClearArea(TheDisplay,TheMainWindow
+		,TheUI.MapX+10,TheUI.MapX+10,13*13,13
+		,False);
+	);
+	++SlowFrameCounter;
+    }
+
+    InputMouseTimeout(callbacks,ticks);
 
     for( ;; ) {
 #ifdef SLOW_INPUT
@@ -910,7 +959,6 @@ global void WaitEventsOneFrame(const EventCallback* callbacks)
 	   X11DoEvent(callbacks);
 	}
 #endif
-
 	//
 	//	Prepare select
 	//
@@ -920,6 +968,24 @@ global void WaitEventsOneFrame(const EventCallback* callbacks)
 	FD_ZERO(&rfds);
 	FD_ZERO(&wfds);
 
+#ifndef USE_ITIMER
+	//
+	//      Time of frame over? This makes the CPU happy. :(
+	//
+	ticks=X11GetTicks();
+	if( !VideoInterrupts && ticks+11<NextFrameTicks ) {
+	    tv.tv_usec=ticks*1000;
+	}
+	while( ticks>=NextFrameTicks ) {
+	    ++VideoInterrupts;
+	    FrameFraction+=FrameRemainder;
+	    if( FrameFraction>10 ) {
+		FrameFraction-=10;
+		++NextFrameTicks;
+	    }
+	    NextFrameTicks+=FrameTicks;
+	}
+#endif
 	//
 	//	X11 how many events already in queue
 	//
@@ -967,17 +1033,15 @@ global void WaitEventsOneFrame(const EventCallback* callbacks)
 	    FD_SET(NetworkFildes,&rfds);
 	}
 
+#ifdef USE_ITIMER
 	maxfd=select(maxfd+1,&rfds,&wfds,NULL
 		,(morex|VideoInterrupts) ? &tv : NULL);
+#else
+	maxfd=select(maxfd+1,&rfds,&wfds,NULL,&tv);
+#endif
 
 	DebugLevel3Fn("%d, %d\n",morex|VideoInterrupts,maxfd);
 
-#if 0
-	if( maxfd>0 && NetworkFildes!=-1 && FD_ISSET(NetworkFildes,&rfds) ) {
-	    callbacks->NetworkEvent();
-	}
-#endif
-
 	//
 	//	X11
 	//
@@ -1009,11 +1073,21 @@ global void WaitEventsOneFrame(const EventCallback* callbacks)
 	    //
 	    //	Sound
 	    //
-	    if( !SoundOff && !SoundThreadRunning 
+	    if( !SoundOff && !SoundThreadRunning
 		    && FD_ISSET(SoundFildes,&wfds) ) {
 		callbacks->SoundReady();
 	    }
 
+#if 0
+	    // ARI: needs network packets!
+	    //
+	    //	No more input and network in sync time for frame over: return
+	    //
+	    if( !morex && NetworkInSync && VideoInterrupts ) {
+		break;
+	    }
+#endif
+
 	    //
 	    //	Network
 	    //
@@ -1021,12 +1095,6 @@ global void WaitEventsOneFrame(const EventCallback* callbacks)
 		callbacks->NetworkEvent();
 	    }
 
-	    //
-	    //	No more input and time for frame over: return
-	    //
-	    if( !morex && VideoInterrupts ) {
-		break;
-	    }
 	}
 
 	//
@@ -1041,6 +1109,12 @@ global void WaitEventsOneFrame(const EventCallback* callbacks)
     //	Prepare return, time for one frame is over.
     //
     VideoInterrupts=0;
+
+#ifndef USE_ITIMER
+    if( !SkipGameCycle-- ) {
+	SkipGameCycle=SkipFrames;
+    }
+#endif
 }
 
 /**
@@ -1053,25 +1127,16 @@ global void WaitEventsOneFrame(const EventCallback* callbacks)
 **
 **	We must handle atlast one X11 event
 **
-**	FIXME:	the initialition could be moved out of the loop
+**	@todo FIXME: Use the ::WaitEventsOneFrame().
 */
 global void WaitEventsAndKeepSync(void)
 {
     EventCallback callbacks;
-    struct timeval tv;
-    fd_set rfds;
-    fd_set wfds;
-    int maxfd;
-    int* xfd;
-    int n;
-    int i;
-    int morex;
-    int connection;
 
-    callbacks.ButtonPressed=HandleButtonDown;
-    callbacks.ButtonReleased=HandleButtonUp;
-    callbacks.MouseMoved=HandleMouseMove;
-    callbacks.MouseExit=HandleMouseExit;
+    callbacks.ButtonPressed=(void*)HandleButtonDown;
+    callbacks.ButtonReleased=(void*)HandleButtonUp;
+    callbacks.MouseMoved=(void*)HandleMouseMove;
+    callbacks.MouseExit=(void*)HandleMouseExit;
 
     callbacks.KeyPressed=HandleKeyDown;
     callbacks.KeyReleased=HandleKeyUp;
@@ -1079,158 +1144,8 @@ global void WaitEventsAndKeepSync(void)
     callbacks.NetworkEvent=NetworkEvent;
     callbacks.SoundReady=WriteSound;
 
-    connection=ConnectionNumber(TheDisplay);
-#ifdef WITH_SOUND
-    if( SoundFildes==-1 ) {
-	SoundOff=1;
-    }
-#endif
-
-    InputMouseTimeout(&callbacks,X11GetTicks());
-
-    for( ;; ) {
-#ifdef SLOW_INPUT
-	while( XPending(TheDisplay) ) {
-	   X11DoEvent(&callbacks);
-	}
-#endif
-
-	//
-	//	Prepare select
-	//
-	maxfd=0;
-	tv.tv_sec=tv.tv_usec=0;
-
-	FD_ZERO(&rfds);
-	FD_ZERO(&wfds);
-
-	//
-	//	X11 how many events already in queue
-	//
-	xfd=NULL;
-	morex=QLength(TheDisplay);
-	if( !morex ) {
-	    //
-	    //	X11 connections number
-	    //
-	    maxfd=connection;
-	    FD_SET(connection,&rfds);
-
-	    //
-	    //	Get all X11 internal connections
-	    //
-	    if( !XInternalConnectionNumbers(TheDisplay,&xfd,&n) ) {
-		DebugLevel0Fn(": out of memory\n");
-		abort();
-	    }
-	    for( i=n; i--; ) {
-		FD_SET(xfd[i],&rfds);
-		if( xfd[i]>maxfd ) {
-		    maxfd=xfd[i];
-		}
-	    }
-	}
-
-	//
-	//	Sound
-	//
-	if( !SoundOff && !SoundThreadRunning ) {
-	    if( SoundFildes>maxfd ) {
-		maxfd=SoundFildes;
-	    }
-	    FD_SET(SoundFildes,&wfds);
-	}
-
-	//
-	//	Network
-	//
-	if( NetworkFildes!=-1 ) {
-	    if( NetworkFildes>maxfd ) {
-		maxfd=NetworkFildes;
-	    }
-	    FD_SET(NetworkFildes,&rfds);
-	    if( !NetworkInSync ) {
-		NetworkRecover();	// recover network
-	    }
-	}
-
-	maxfd=select(maxfd+1,&rfds,&wfds,NULL
-		,(morex|VideoInterrupts) ? &tv : NULL);
-
-	DebugLevel3Fn("%d, %d\n",morex|VideoInterrupts,maxfd);
-
-	//
-	//	X11
-	//
-	if( maxfd>0 ) {
-	    if( !morex ) {		// look if new events
-		if (xfd) {
-		    for( i=n; i--; ) {
-			if( FD_ISSET(xfd[i],&rfds) ) {
-			    XProcessInternalConnection(TheDisplay,xfd[i]);
-			}
-		    }
-		}
-		if( FD_ISSET(connection,&rfds) ) {
-		    morex=XEventsQueued(TheDisplay,QueuedAfterReading);
-		} else {
-		    morex=QLength(TheDisplay);
-		}
-	    }
-	}
-	if( xfd) {
-	    XFree(xfd);
-	}
-
-	for( i=morex; i--; ) {		// handle new + *OLD* x11 events
-	   X11DoEvent(&callbacks);
-	}
-
-	if( maxfd>0 ) {
-	    //
-	    //	Sound
-	    //
-	    if( !SoundOff && !SoundThreadRunning 
-		    && FD_ISSET(SoundFildes,&wfds) ) {
-		callbacks.SoundReady();
-	    }
-
-	    //
-	    //	Network
-	    //
-	    if( NetworkFildes!=-1 && FD_ISSET(NetworkFildes,&rfds) ) {
-		callbacks.NetworkEvent();
-	    }
-
-	    //	ARI: ORDER IS IMPORTANT HERE - THIS HAS TO BE LAST,
-	    //	or Network/Sound events wont be handled without X activity
-	    //	which would break for example network menu code
-
-	    //
-	    //	Network in sync and time for frame over: return
-	    //
-	    if( !morex && NetworkInSync && VideoInterrupts ) {
-		break;
-	    }
-
-	}
-
-	//
-	//	Network in sync and time for frame over: return
-	//
-	if(
-#ifndef SLOW_INPUT
-	    !morex &&
-#endif
-		      NetworkInSync && VideoInterrupts ) {
-	    break;
-	}
-    }
-
-    //
-    //	Prepare return, time for one frame is over.
-    //
-    VideoInterrupts=0;
+    DebugLevel0Fn("Don't use this function\n");
+    WaitEventsOneFrame(&callbacks);
 }
 
 /**
@@ -1429,8 +1344,9 @@ global void CheckVideoInterrupts(void)
 global void RealizeVideoMemory(void)
 {
     // in X11 it does flushing the output queue
-    XFlush(TheDisplay);
-    //XSync(TheDisplay,False);
+    //XFlush(TheDisplay);
+    // in X11 wait for all commands done.
+    XSync(TheDisplay,False);
 }
 
 /**
@@ -1453,6 +1369,15 @@ global void ToggleGrabMouse(void)
     }
 }
 
+/**
+**	Toggle full screen mode.
+**
+**	@todo FIXME: not written.
+*/
+global void ToggleFullScreen(void)
+{
+}
+
 #endif	// USE_X11
 
 //@}
diff --git a/src/video/sdl.cpp b/src/video/sdl.cpp
index cc69d893a..ed28d46cd 100644
--- a/src/video/sdl.cpp
+++ b/src/video/sdl.cpp
@@ -639,7 +639,7 @@ global void WaitEventsOneFrame(const EventCallback* callbacks)
 #endif
 
 	    //
-	    //	Not more input and network in syn and time for frame over
+	    //	Not more input and network in sync and time for frame over
 	    //
 	    if( !i && NetworkInSync && VideoInterrupts ) {
 		break;