diff --git a/sc2/ChangeLog b/sc2/ChangeLog index e9f7f8b2f..ea8b783e2 100644 --- a/sc2/ChangeLog +++ b/sc2/ChangeLog @@ -1,4 +1,5 @@ Changes towards version 0.7: +- Added graphics context debugging function - SvdB - Thread down-throttling and game sleep when inactive (currently disabled), (bug #1070), from Flandry - Internal changes: GOOD_GUY/BAD_GUY ship flags retired - Alex diff --git a/sc2/doc/devel/debug b/sc2/doc/devel/debug index 82063f627..22be62890 100644 --- a/sc2/doc/devel/debug +++ b/sc2/doc/devel/debug @@ -6,11 +6,15 @@ require the game to be in a specific state. The function debugKeyPressed() in uqmdebug.c is called when the debug key is pressed. This function is a suitable place to put various debugging calls. -There is also a global variable 'debugHook', which can be set to a function -to be called the next iteration of the main game loop (which will occur -when the current activity (IP, HyperSpace, Communication, Battle) changes. -By setting this, a function can be called from the main loop, thereby -eliminating threading issues that may otherwise arrise. +There are also global variables 'debugHook' and 'doInputDebugHook', +which can be set to a function to be called from the Starcon2Main thread. +If if is set, 'debugHook' is called the next iteration of the main game loop +(which will occur when the current activity (IP, HyperSpace, Communication, +Battle) changes. The game will be in a well defined state here. +If 'doInputDebugHook' is set, the function it is set to is called from +doInput(), which is called all throughout the game. +By setting one of these hooks, a function can be called from the Starcon2Main +thread, thereby eliminating threading issues that may otherwise arrise. The debug key can be specified in user's override.cfg by adding a line with a text similar to "debug.1 = STRING:key F12". An interactive way to access various debugging code, similar to @@ -56,6 +60,13 @@ listed below: This function is not defined in sc2code/uqmdebug.h, but in libs/uio.h. This function can interactively (tty-based) display information on the state of the uio file system, and modifications can be made. +- the function dumpStrings() + This function prints all the game strings, and is useful to check whether + the various string bases, as defined in gamestr.h, are correct. +- the function debugContexts() + Prints and visually displays the various graphics contexts. + This function should only be called from doInputDebugHook(), as threading + issues would otherwise arrise. The first version of this document was created by Serge van den Boom, diff --git a/sc2/src/libs/gfxlib.h b/sc2/src/libs/gfxlib.h index 9c525bb64..e8d06dbfb 100644 --- a/sc2/src/libs/gfxlib.h +++ b/sc2/src/libs/gfxlib.h @@ -169,8 +169,11 @@ extern void UninitGraphics (void); extern CONTEXT SetContext (CONTEXT Context); extern COLOR SetContextForeGroundColor (COLOR Color); +extern COLOR GetContextForeGroundColor (void); extern COLOR SetContextBackGroundColor (COLOR Color); +extern COLOR GetContextBackGroundColor (void); extern FRAME SetContextFGFrame (FRAME Frame); +extern FRAME GetContextFGFrame (void); extern BOOLEAN SetContextClipping (BOOLEAN ClipStatus); extern BOOLEAN SetContextClipRect (RECT *pRect); extern BOOLEAN GetContextClipRect (RECT *pRect); @@ -191,7 +194,13 @@ extern void UnbatchGraphics (void); extern void FlushGraphics (void); extern void ClearBackGround (RECT *pClipRect); extern void ClearDrawable (void); -extern CONTEXT CreateContext (void); +#ifdef DEBUG +extern CONTEXT CreateContextAux (const char *name); +#define CreateContext(name) CreateContextAux((name)) +#else /* if !defined(DEBUG) */ +extern CONTEXT CreateContextAux (void); +#define CreateContext(name) CreateContextAux() +#endif /* !defined(DEBUG) */ extern BOOLEAN DestroyContext (CONTEXT ContextRef); extern DRAWABLE CreateDisplay (CREATE_FLAGS CreateFlags, SIZE *pwidth, SIZE *pheight); @@ -199,6 +208,12 @@ extern DRAWABLE CreateDrawable (CREATE_FLAGS CreateFlags, SIZE width, SIZE height, COUNT num_frames); extern BOOLEAN DestroyDrawable (DRAWABLE Drawable); extern BOOLEAN GetFrameRect (FRAME Frame, RECT *pRect); +#ifdef DEBUG +extern const char *GetContextName (CONTEXT context); +extern CONTEXT GetFirstContext (void); +extern CONTEXT GetNextContext (CONTEXT context); +extern size_t GetContextCount (void); +#endif /* DEBUG */ extern HOT_SPOT SetFrameHot (FRAME Frame, HOT_SPOT HotSpot); extern HOT_SPOT GetFrameHot (FRAME Frame); diff --git a/sc2/src/libs/graphics/context.c b/sc2/src/libs/graphics/context.c index 63c76bca7..20d55d89b 100644 --- a/sc2/src/libs/graphics/context.c +++ b/sc2/src/libs/graphics/context.c @@ -22,6 +22,14 @@ GRAPHICS_STATUS _GraphicsStatusFlags; CONTEXT _pCurContext; +#ifdef DEBUG +// We keep track of all contexts +CONTEXT firstContext; + // The first one in the list. +CONTEXT *contextEnd = &firstContext; + // Where to put the next context. +#endif + PRIMITIVE _locPrim; FONT _CurFontPtr; @@ -37,12 +45,10 @@ SetContext (CONTEXT Context) if (LastContext) { UnsetContextFlags ( - MAKE_WORD (0, GRAPHICS_ACTIVE | DRAWABLE_ACTIVE) - ); + MAKE_WORD (0, GRAPHICS_ACTIVE | DRAWABLE_ACTIVE)); SetContextFlags ( MAKE_WORD (0, _GraphicsStatusFlags - & (GRAPHICS_ACTIVE | DRAWABLE_ACTIVE)) - ); + & (GRAPHICS_ACTIVE | DRAWABLE_ACTIVE))); DeactivateContext (); } @@ -65,17 +71,29 @@ SetContext (CONTEXT Context) return (LastContext); } +#ifdef DEBUG CONTEXT -CreateContext (void) +CreateContextAux (const char *name) +#else /* if !defined(DEBUG) */ +CONTEXT +CreateContextAux (void) +#endif /* !defined(DEBUG) */ { CONTEXT NewContext; NewContext = AllocContext (); if (NewContext) { + /* initialize context */ CONTEXT OldContext; - /* initialize context */ +#ifdef DEBUG + NewContext->name = name; + NewContext->next = NULL; + *contextEnd = NewContext; + contextEnd = &NewContext->next; +#endif /* DEBUG */ + OldContext = SetContext (NewContext); SetContextForeGroundColor ( BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)); @@ -85,9 +103,25 @@ CreateContext (void) SetContext (OldContext); } - return (NewContext); + return NewContext; } +#ifdef DEBUG +// Loop through the list of context to the pointer which points to the +// specified context. This is either 'firstContext' or the address of +// the 'next' field of some other context. +static CONTEXT * +FindContextPtr (CONTEXT context) { + CONTEXT *ptr; + + for (ptr = &firstContext; *ptr != NULL; ptr = &(*ptr)->next) { + if (*ptr == context) + break; + } + return ptr; +} +#endif /* DEBUG */ + BOOLEAN DestroyContext (CONTEXT ContextRef) { @@ -97,6 +131,16 @@ DestroyContext (CONTEXT ContextRef) if (_pCurContext && _pCurContext == ContextRef) SetContext ((CONTEXT)0); +#ifdef DEBUG + // Unlink the context. + { + CONTEXT *contextPtr = FindContextPtr (ContextRef); + if (contextEnd == &ContextRef->next) + contextEnd = contextPtr; + *contextPtr = ContextRef->next; + } +#endif /* DEBUG */ + FreeContext (ContextRef); return TRUE; } @@ -109,7 +153,8 @@ SetContextForeGroundColor (COLOR Color) if (!ContextActive ()) return (BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)); - if ((oldColor = _get_context_fg_color ()) != Color) + oldColor = _get_context_fg_color (); + if (oldColor != Color) { SwitchContextForeGroundColor (Color); @@ -123,6 +168,15 @@ SetContextForeGroundColor (COLOR Color) return (oldColor); } +COLOR +GetContextForeGroundColor (void) +{ + if (!ContextActive ()) + return (BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)); + + return _get_context_fg_color (); +} + COLOR SetContextBackGroundColor (COLOR Color) { @@ -131,14 +185,22 @@ SetContextBackGroundColor (COLOR Color) if (!ContextActive ()) return (BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)); - if ((oldColor = _get_context_bg_color ()) != Color) - { + oldColor = _get_context_bg_color (); + if (oldColor != Color) SwitchContextBackGroundColor (Color); - } return (oldColor); } +COLOR +GetContextBackGroundColor (void) +{ + if (!ContextActive ()) + return (BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)); + + return _get_context_bg_color (); +} + BOOLEAN SetContextClipping (BOOLEAN ClipStatus) { @@ -249,3 +311,24 @@ FixContextFontEffect (void) _pCurContext->FontBacking = img; UnsetContextFBkFlags (FBK_DIRTY); } + +#ifdef DEBUG +const char * +GetContextName (CONTEXT context) +{ + return context->name; +} + +CONTEXT +GetFirstContext (void) +{ + return firstContext; +} + +CONTEXT +GetNextContext (CONTEXT context) +{ + return context->next; +} +#endif /* DEBUG */ + diff --git a/sc2/src/libs/graphics/context.h b/sc2/src/libs/graphics/context.h index 1046560bb..bbe058cca 100644 --- a/sc2/src/libs/graphics/context.h +++ b/sc2/src/libs/graphics/context.h @@ -40,6 +40,10 @@ struct context_desc TFB_Image *FontBacking; FBK_FLAGS BackingFlags; +#ifdef DEBUG + const char *name; + CONTEXT next; +#endif }; #define AllocContext() HCalloc (sizeof (CONTEXT_DESC)) diff --git a/sc2/src/libs/graphics/drawable.c b/sc2/src/libs/graphics/drawable.c index 2a14d991e..753639f5f 100644 --- a/sc2/src/libs/graphics/drawable.c +++ b/sc2/src/libs/graphics/drawable.c @@ -51,6 +51,12 @@ SetContextFGFrame (FRAME Frame) return (LastFrame); } +FRAME +GetContextFGFrame (void) +{ + return _CurFramePtr; +} + DRAWABLE CreateDisplay (CREATE_FLAGS CreateFlags, SIZE *pwidth, SIZE *pheight) { diff --git a/sc2/src/uqm/comm.c b/sc2/src/uqm/comm.c index 0c421c8e6..8b5ce1d7e 100644 --- a/sc2/src/uqm/comm.c +++ b/sc2/src/uqm/comm.c @@ -1266,7 +1266,7 @@ HailAlien (void) SubtitleText.align = CommData.AlienTextAlign; // init subtitle cache context - TextCacheContext = CreateContext (); + TextCacheContext = CreateContext ("TextCacheContext"); TextCacheFrame = CaptureDrawable ( CreateDrawable (WANT_PIXMAP, SIS_SCREEN_WIDTH, SIS_SCREEN_HEIGHT - SLIDER_Y - SLIDER_HEIGHT + 2, 1)); @@ -1287,7 +1287,7 @@ HailAlien (void) { RECT r; - TaskContext = CreateContext (); + TaskContext = CreateContext ("TaskContext"); SetContext (TaskContext); SetContextFGFrame (Screen); GetFrameRect (CommData.AlienFrame, &r); diff --git a/sc2/src/uqm/credits.c b/sc2/src/uqm/credits.c index 53b186e99..b74b5fe26 100644 --- a/sc2/src/uqm/credits.c +++ b/sc2/src/uqm/credits.c @@ -315,8 +315,8 @@ credit_roll_task (void *data) TextBack = BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00); TextFore = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F); - LocalContext = CreateContext (); - DrawContext = CreateContext (); + LocalContext = CreateContext ("Credits.LocalContext"); + DrawContext = CreateContext ("Credits.DrawContext"); total_h = disp_h = SCREEN_HEIGHT; diff --git a/sc2/src/uqm/gameinp.c b/sc2/src/uqm/gameinp.c index 6b93aac5e..62e929e31 100644 --- a/sc2/src/uqm/gameinp.c +++ b/sc2/src/uqm/gameinp.c @@ -28,6 +28,7 @@ #include "settings.h" #include "sounds.h" #include "tactrans.h" +#include "uqmdebug.h" #include "libs/inplib.h" #include "libs/timelib.h" #include "libs/threadlib.h" @@ -352,6 +353,19 @@ DoInput (void *pInputState, BOOLEAN resetInput) UpdateInputState (); +#ifdef DEBUG + if (doInputDebugHook != NULL) + { + void (*saveDebugHook) (void); + saveDebugHook = doInputDebugHook; + doInputDebugHook = NULL; + // No further debugHook calls unless the called + // function resets doInputDebugHook. + (*saveDebugHook) (); + continue; + } +#endif + #if DEMO_MODE || CREATE_JOURNAL if (ArrowInput != DemoInput) #endif diff --git a/sc2/src/uqm/globdata.c b/sc2/src/uqm/globdata.c index 678b15656..9b2fd6f3e 100644 --- a/sc2/src/uqm/globdata.c +++ b/sc2/src/uqm/globdata.c @@ -125,7 +125,7 @@ CreateRadar (void) RECT r; CONTEXT OldContext; - RadarContext = CreateContext (); + RadarContext = CreateContext ("RadarContext"); OldContext = SetContext (RadarContext); SetContextFGFrame (Screen); r.corner.x = RADAR_X; diff --git a/sc2/src/uqm/planets/genchmmr.c b/sc2/src/uqm/planets/genchmmr.c index 3466b72d6..df3020817 100644 --- a/sc2/src/uqm/planets/genchmmr.c +++ b/sc2/src/uqm/planets/genchmmr.c @@ -110,7 +110,7 @@ GenerateChmmr (BYTE control) CaptureStringTable ( LoadStringTable (CHMMR_BASE_STRTAB)); - ScanContext = CreateContext (); + ScanContext = CreateContext ("genchmmr.ScanContext"); SetContext (ScanContext); SetContextFGFrame (Screen); r.corner.x = (SIS_ORG_X + SIS_SCREEN_WIDTH) - MAP_WIDTH; diff --git a/sc2/src/uqm/planets/planets.c b/sc2/src/uqm/planets/planets.c index f1ca33b62..4aa989b6d 100644 --- a/sc2/src/uqm/planets/planets.c +++ b/sc2/src/uqm/planets/planets.c @@ -152,7 +152,7 @@ LoadPlanet (FRAME SurfDefFrame) StopMusic (); - TaskContext = CreateContext (); + TaskContext = CreateContext ("TaskContext"); pPlanetDesc = pSolarSysState->pOrbitalDesc; diff --git a/sc2/src/uqm/planets/scan.c b/sc2/src/uqm/planets/scan.c index a29d36f35..4bf8a3645 100644 --- a/sc2/src/uqm/planets/scan.c +++ b/sc2/src/uqm/planets/scan.c @@ -1164,7 +1164,7 @@ ScanSystem (void) (MAP_HEIGHT >> 1) << MAG_SHIFT; LockMutex (GraphicsLock); - ScanContext = CreateContext (); + ScanContext = CreateContext ("ScanContext"); SetContext (ScanContext); initPlanetLocationImage (&MenuState); diff --git a/sc2/src/uqm/setup.c b/sc2/src/uqm/setup.c index 4869aa941..814bd025f 100644 --- a/sc2/src/uqm/setup.c +++ b/sc2/src/uqm/setup.c @@ -96,7 +96,7 @@ LoadKernel (int argc, char *argv[]) InitSound (argc, argv); InitVideoPlayer (TRUE); - ScreenContext = CreateContext (); + ScreenContext = CreateContext ("ScreenContext"); if (ScreenContext == NULL) return FALSE; @@ -158,7 +158,7 @@ InitContexts (void) { RECT r; - StatusContext = CreateContext (); + StatusContext = CreateContext ("StatusContext"); if (StatusContext == NULL) return FALSE; @@ -170,11 +170,11 @@ InitContexts (void) r.extent.height = STATUS_HEIGHT; SetContextClipRect (&r); - SpaceContext = CreateContext (); + SpaceContext = CreateContext ("SpaceContext"); if (SpaceContext == NULL) return FALSE; - OffScreenContext = CreateContext (); + OffScreenContext = CreateContext ("OffScreenContext"); if (OffScreenContext == NULL) return FALSE; diff --git a/sc2/src/uqm/sis.c b/sc2/src/uqm/sis.c index e28eaf4e6..d5b9a0da7 100644 --- a/sc2/src/uqm/sis.c +++ b/sc2/src/uqm/sis.c @@ -761,7 +761,8 @@ DrawStorageBays (BOOLEAN Refresh) --i; } - r.extent.height = (4 * j + (STORAGE_BAY_CAPACITY - 1)) / STORAGE_BAY_CAPACITY; + r.extent.height = (4 * j + (STORAGE_BAY_CAPACITY - 1)) / + STORAGE_BAY_CAPACITY; if (r.extent.height) { r.corner.y += 4 - r.extent.height; @@ -876,14 +877,11 @@ DeltaSISGauges (SIZE crew_delta, SIZE fuel_delta, int resunit_delta) s.origin.y = 0; for (i = 0; i < NUM_DRIVE_SLOTS; ++i) { - BYTE which_piece; - - if ((which_piece = - GLOBAL_SIS (DriveSlots[i])) < EMPTY_SLOT) + BYTE which_piece = GLOBAL_SIS (DriveSlots[i]); + if (which_piece < EMPTY_SLOT) { s.frame = SetAbsFrameIndex ( - FlagStatFrame, which_piece + 1 + 0 - ); + FlagStatFrame, which_piece + 1 + 0); DrawStamp (&s); s.frame = IncFrameIndex (s.frame); DrawStamp (&s); @@ -894,14 +892,11 @@ DeltaSISGauges (SIZE crew_delta, SIZE fuel_delta, int resunit_delta) s.origin.y = 0; for (i = 0; i < NUM_JET_SLOTS; ++i) { - BYTE which_piece; - - if ((which_piece = - GLOBAL_SIS (JetSlots[i])) < EMPTY_SLOT) + BYTE which_piece = GLOBAL_SIS (JetSlots[i]); + if (which_piece < EMPTY_SLOT) { s.frame = SetAbsFrameIndex ( - FlagStatFrame, which_piece + 1 + 1 - ); + FlagStatFrame, which_piece + 1 + 1); DrawStamp (&s); s.frame = IncFrameIndex (s.frame); DrawStamp (&s); @@ -913,14 +908,11 @@ DeltaSISGauges (SIZE crew_delta, SIZE fuel_delta, int resunit_delta) s.origin.x = 1; // This properly centers the modules. for (i = 0; i < NUM_MODULE_SLOTS; ++i) { - BYTE which_piece; - - if ((which_piece = - GLOBAL_SIS (ModuleSlots[i])) < EMPTY_SLOT) + BYTE which_piece = GLOBAL_SIS (ModuleSlots[i]); + if (which_piece < EMPTY_SLOT) { s.frame = SetAbsFrameIndex ( - FlagStatFrame, which_piece + 1 + 2 - ); + FlagStatFrame, which_piece + 1 + 2); DrawStamp (&s); } @@ -996,22 +988,25 @@ DeltaSISGauges (SIZE crew_delta, SIZE fuel_delta, int resunit_delta) old_coarse_fuel = (COUNT)~0; else { - DWORD FuelCapacity; old_coarse_fuel = (COUNT)( - GLOBAL_SIS (FuelOnBoard) / FUEL_TANK_SCALE - ); + GLOBAL_SIS (FuelOnBoard) / FUEL_TANK_SCALE); if (fuel_delta < 0 && GLOBAL_SIS (FuelOnBoard) <= (DWORD)-fuel_delta) + { GLOBAL_SIS (FuelOnBoard) = 0; - else if ((GLOBAL_SIS (FuelOnBoard) += fuel_delta) > - (FuelCapacity = GetFTankCapacity (NULL))) - GLOBAL_SIS (FuelOnBoard) = FuelCapacity; + } + else + { + DWORD FuelCapacity = GetFTankCapacity (NULL); + GLOBAL_SIS (FuelOnBoard) += fuel_delta; + if (GLOBAL_SIS (FuelOnBoard) > FuelCapacity) + GLOBAL_SIS (FuelOnBoard) = FuelCapacity; + } } new_coarse_fuel = (COUNT)( - GLOBAL_SIS (FuelOnBoard) / FUEL_TANK_SCALE - ); + GLOBAL_SIS (FuelOnBoard) / FUEL_TANK_SCALE); if (new_coarse_fuel != old_coarse_fuel) { sprintf (buf, "%u", new_coarse_fuel); @@ -1122,8 +1117,7 @@ GetCPodCapacity (POINT *ppt) SetContextForeGroundColor (crew_rows[which_row]); else SetContextForeGroundColor ( - BUILD_COLOR (MAKE_RGB15 (0x05, 0x10, 0x05), 0x65) - ); + BUILD_COLOR (MAKE_RGB15 (0x05, 0x10, 0x05), 0x65)); } capacity += CREW_POD_CAPACITY; @@ -1169,7 +1163,8 @@ GetSBayCapacity (POINT *ppt) }; bay_remainder = GLOBAL_SIS (TotalElementMass) - capacity; - if ((which_row = bay_remainder / SBAY_MASS_PER_ROW) == 0) + which_row = bay_remainder / SBAY_MASS_PER_ROW; + if (which_row == 0) SetContextForeGroundColor (BLACK_COLOR); else SetContextForeGroundColor (color_bars[--which_row]); @@ -1184,7 +1179,7 @@ GetSBayCapacity (POINT *ppt) x -= SHIP_PIECE_OFFSET; } while (slot--); - return (capacity); + return capacity; } DWORD @@ -1229,8 +1224,7 @@ GetFTankCapacity (POINT *ppt) which_row = (COUNT)( (GLOBAL_SIS (FuelOnBoard) - capacity) - * MAX_FUEL_BARS / HEFUEL_TANK_CAPACITY - ); + * MAX_FUEL_BARS / HEFUEL_TANK_CAPACITY); ppt->x = x + 1; if (volume == FUEL_TANK_CAPACITY) ppt->y = 27 - which_row; @@ -1247,7 +1241,7 @@ GetFTankCapacity (POINT *ppt) x -= SHIP_PIECE_OFFSET; } while (slot--); - return (capacity); + return capacity; } COUNT @@ -1281,7 +1275,7 @@ CountSISPieces (BYTE piece_type) } } - return (num_pieces); + return num_pieces; } void @@ -1356,6 +1350,7 @@ DrawAutoPilotMessage (BOOLEAN Reset) Task flash_task = 0; RECT flash_rect; static FRAME flash_screen_frame = 0; + // The original contents of the flash rectangle. static int flash_changed; Mutex flash_mutex = 0; // XXX: these are currently defined in libs/graphics/sdl/3do_getbody.c @@ -1369,25 +1364,25 @@ flash_rect_func (void *data) #define NORMAL_STRENGTH 4 #define NORMAL_F_STRENGTH 0 #define CACHE_SIZE 10 - DWORD TimeIn, WaitTime; - SIZE strength, fstrength, incr; + DWORD TimeIn; + const DWORD WaitTime = ONE_SECOND / 16; + SIZE strength; RECT cached_rect; FRAME cached_screen_frame = 0; Task task = (Task)data; - int cached[CACHE_SIZE]; + bool cached[CACHE_SIZE]; STAMP cached_stamp[CACHE_SIZE]; int i; + // Init cache for (i = 0; i < CACHE_SIZE; i++) { - cached[i] = 0; + cached[i] = false; cached_stamp[i].frame = 0; } - fstrength = NORMAL_F_STRENGTH; - incr = 1; + strength = NORMAL_STRENGTH; TimeIn = GetTimeCounter (); - WaitTime = ONE_SECOND / 16; while (!Task_ReadState(task, TASK_EXIT)) { CONTEXT OldContext; @@ -1411,10 +1406,12 @@ flash_rect_func (void *data) arith_frame_blit (flash_screen_frame, &screen_rect, cached_screen_frame, NULL, 0, 0); UnlockMutex (flash_mutex); + + // Clear the cache. for (i = 0; i < CACHE_SIZE; i++) { - cached[i] = 0; - if(cached_stamp[i].frame) + cached[i] = false; + if (cached_stamp[i].frame) DestroyDrawable (ReleaseDrawable (cached_stamp[i].frame)); cached_stamp[i].frame = 0; } @@ -1436,7 +1433,7 @@ flash_rect_func (void *data) { RECT tmp_rect = cached_rect; pStamp = &cached_stamp[strength - MIN_STRENGTH]; - cached[strength - MIN_STRENGTH] = 1; + cached[strength - MIN_STRENGTH] = true; pStamp->frame = CaptureDrawable (CreateDrawable (WANT_PIXMAP, cached_rect.extent.width, cached_rect.extent.height, 1)); @@ -1451,11 +1448,12 @@ flash_rect_func (void *data) arith_frame_blit (cached_screen_frame, &tmp_rect, pStamp->frame, &tmp_rect, strength, 4); } + LockMutex (GraphicsLock); OldContext = SetContext (ScreenContext); SetContextClipRect (&cached_rect); // flash changed_can't be modified while GraphicSem is held - if (! flash_changed) + if (!flash_changed) DrawStamp (pStamp); SetContextClipRect (NULL); // this will flush whatever SetContext (OldContext); @@ -1465,6 +1463,8 @@ flash_rect_func (void *data) SleepThreadUntil (TimeIn + WaitTime); TimeIn = GetTimeCounter (); } + + // Clear cache { if (cached_screen_frame) DestroyDrawable (ReleaseDrawable (cached_screen_frame)); @@ -1480,7 +1480,7 @@ flash_rect_func (void *data) UnlockMutex (flash_mutex); FinishTask (task); - return(0); + return 0; } void @@ -1490,7 +1490,7 @@ SetFlashRect (RECT *pRect) CONTEXT OldContext; int create_flash = 0; - if (! flash_mutex) + if (!flash_mutex) flash_mutex = CreateMutex ("FlashRect Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO); @@ -1525,6 +1525,7 @@ SetFlashRect (RECT *pRect) if (pRect == 0 || pRect->extent.width == 0) { + // End the flashing. flash_rect1.extent.width = 0; if (flash_task) { @@ -1550,8 +1551,10 @@ SetFlashRect (RECT *pRect) || old_r.corner.x != flash_rect.corner.x || old_r.corner.y != flash_rect.corner.y)) { + // We had a flash rectangle, and now a different one is set. if (flash_screen_frame) { + // The screen contents may have changed; we grab a new copy. STAMP old_s; old_s.origin.x = old_r.corner.x; old_s.origin.y = old_r.corner.y; @@ -1566,6 +1569,7 @@ SetFlashRect (RECT *pRect) if (flash_rect.extent.width) { + // A new flash rectangle is set. // Copy the original contents of the rectangle from the screen. if (flash_screen_frame) DestroyDrawable (ReleaseDrawable (flash_screen_frame)); diff --git a/sc2/src/uqm/uqmdebug.c b/sc2/src/uqm/uqmdebug.c index cc968d053..ae5109e82 100644 --- a/sc2/src/uqm/uqmdebug.c +++ b/sc2/src/uqm/uqmdebug.c @@ -20,6 +20,7 @@ #include "build.h" #include "colors.h" +#include "controls.h" #include "clock.h" #include "encount.h" #include "element.h" @@ -68,6 +69,7 @@ static void dumpPlanetTypeCallback (int index, const PlanetFrame *planet, BOOLEAN instantMove = FALSE; BOOLEAN disableInteractivity = FALSE; void (* volatile debugHook) (void) = NULL; +void (* volatile doInputDebugHook) (void) = NULL; void @@ -118,6 +120,12 @@ debugKeyPressed (void) // main loop. Calling it from here would give threading // problems. + // Graphical and textual: + //doInputDebugHook = debugContexts; + // This will cause debugContexts to be called from the + // Starcon2Main thread, from DoInput(). Calling it from here + // would give threading problems. + // Interactive: // uio_debugInteractive(stdin, stdout, stderr); } @@ -755,6 +763,7 @@ dumpUniverse (FILE *out) UniverseRecurse (&universeRecurseArg); } +// Must be called from the main thread. void dumpUniverseToFile (void) { @@ -1162,6 +1171,7 @@ tallyResources (FILE *out) UniverseRecurse (&universeRecurseArg); } +// Must be called from the main thread. void tallyResourcesToFile (void) { @@ -1459,7 +1469,8 @@ depositQualityString (BYTE quality) // playerNr should be 0 or 1 STARSHIP* -findPlayerShip (SIZE playerNr) { +findPlayerShip (SIZE playerNr) +{ HELEMENT hElement, hNextElement; for (hElement = GetHeadElement (); hElement; hElement = hNextElement) @@ -1486,7 +1497,8 @@ findPlayerShip (SIZE playerNr) { //////////////////////////////////////////////////////////////////////////// void -resetCrewBattle(void) { +resetCrewBattle (void) +{ STARSHIP *StarShipPtr; COUNT delta; CONTEXT OldContext; @@ -1508,7 +1520,8 @@ resetCrewBattle(void) { } void -resetEnergyBattle(void) { +resetEnergyBattle (void) +{ STARSHIP *StarShipPtr; COUNT delta; CONTEXT OldContext; @@ -1534,7 +1547,8 @@ resetEnergyBattle(void) { // This function should help in making sure that gamestr.h matches // gamestrings.txt. void -dumpStrings(FILE *out) { +dumpStrings (FILE *out) +{ #define STRINGIZE(a) #a #define MAKE_STRING_CATEGORY(prefix) \ { \ @@ -1603,6 +1617,409 @@ dumpStrings(FILE *out) { } } +//////////////////////////////////////////////////////////////////////////// + + +static COLOR +hsvaToRgba (double hue, double sat, double val, BYTE alpha) { + assert (hue >= 0.0 && hue < 360.0); + assert (sat >= 0 && sat <= 1.0); + assert (val >= 0 && val <= 1.0); + /*fprintf(stderr, "hsva = (%.1f, %.2f, %.2f, %.2d)\n", + hue, sat, val, alpha);*/ + + unsigned int hi = (int) (hue / 60.0); + double f = (hue / 60.0) - ((int) (hue / 60.0)); + double p = val * (1.0 - sat); + double q = val * (1.0 - f * sat); + double t = val * (1.0 - (1.0 - f * sat)); + + // Convert p, q, t, and v from [0..1] to [0..255] + BYTE pb = (BYTE) (p * 255.0 + 0.5); + BYTE qb = (BYTE) (q * 255.0 + 0.5); + BYTE tb = (BYTE) (t * 255.0 + 0.5); + BYTE vb = (BYTE) (val * 255.0 + 0.5); + + assert (hi < 6); + switch (hi) { + case 0: return BUILD_COLOR_RGBA (vb, tb, pb, alpha); + case 1: return BUILD_COLOR_RGBA (qb, vb, pb, alpha); + case 2: return BUILD_COLOR_RGBA (pb, vb, tb, alpha); + case 3: return BUILD_COLOR_RGBA (pb, qb, vb, alpha); + case 4: return BUILD_COLOR_RGBA (tb, pb, vb, alpha); + case 5: return BUILD_COLOR_RGBA (vb, pb, qb, alpha); + } + + // Should not happen. + return BUILD_COLOR_RGBA (0, 0, 0, alpha); +} + +// Work-around: colors returned by BUILD_COLOR_RGBA are not usable to draw +// with. +static DWORD +fixColorRgba (FRAME frame, COLOR col) +{ +#if 0 + extern DWORD frame_mapRGBA (FRAME FramePtr, BYTE r, BYTE g, BYTE b, + BYTE a); + + BYTE r = (col & 0xff000000) >> 24; + BYTE g = (col & 0x00ff0000) >> 16; + BYTE b = (col & 0x0000ff00) >> 8; + BYTE a = (col & 0x000000ff) >> 0; + + return (DWORD) frame_mapRGBA (frame, r, g, b, a); +#endif + + BYTE r = (col & 0xff000000) >> 24; + BYTE g = (col & 0x00ff0000) >> 16; + BYTE b = (col & 0x0000ff00) >> 8; + + (void) frame; + return BUILD_COLOR (MAKE_RGB15 (r >> 3, g >> 3, b >> 3) ,0); +} + +// Workaround. DrawFilledRectangle() doesn't handle transparency. +// We use a temporary frame to achieve the same thing. +static void +DrawFilledRectangleTransparent (RECT *rect, BYTE alpha) +{ + extern void arith_frame_blit (FRAME srcFrame, const RECT *rsrc, + FRAME dstFrame, const RECT *rdst, int num, int denom); + + RECT absRect; + FRAME orgRectFrame; + COLOR fillColor; + + // Create a rectangle from 'rect', but with (0, 0) as origin. + absRect.corner.x = 0; + absRect.corner.y = 0; + absRect.extent = rect->extent; + + // Create a new temporary FRAME to store the contents of the original + // rectangle in. + orgRectFrame = CaptureDrawable (CreateDrawable ( + WANT_PIXMAP, rect->extent.width, rect->extent.height, 1)); + + // Copy the original rectangle, faded. + arith_frame_blit (GetContextFGFrame (), rect, orgRectFrame, &absRect, + 255 - alpha, 255); + + // Apply the transparency to the colour. + fillColor = GetContextForeGroundColor (); +#if 0 /* 32 bits RGBA */ + fillColor = + (((((fillColor & 0xff000000) >> 24) * alpha + 127) / 255) << 24) | + (((((fillColor & 0x00ff0000) >> 16) * alpha + 127) / 255) << 16) | + (((((fillColor & 0x0000ff00) >> 8) * alpha + 127) / 255) << 8); + */ +#endif + /* 15 bits RGB + index: */ + fillColor = + (((((fillColor & 0x007c0000) >> 18) * alpha + 15) / 31) << 18) | + (((((fillColor & 0x0003e000) >> 13) * alpha + 15) / 31) << 13) | + (((((fillColor & 0x00001f00) >> 8) * alpha + 15) / 31) << 8) | + (fillColor & 0x000000ff); + + // Fill the frame with fillColor + { + COLOR oldFgColor = SetContextForeGroundColor (fillColor); + DrawFilledRectangle (rect); + SetContextForeGroundColor (oldFgColor); + FlushGraphics (); + // Not really necessary for the Context for which this + // function is called, but if this function is ever used + // to draw directly to the screen, this will be needed + // to make sure the rectangle is drawn before the + // arith_frame_blit() call, which is immediate. + } + + // Blend in the original rectangle. + arith_frame_blit (orgRectFrame, &absRect, GetContextFGFrame (), rect, + 1, -1); + + // Destroy the temporary frame + DestroyDrawable (ReleaseDrawable (orgRectFrame)); +} + +// Returns true iff this context has a visible FRAME. +static bool +isContextVisible (CONTEXT context) +{ + FRAME contextFrame; + + // Save the original context. + CONTEXT oldContext = SetContext (context); + + // Get the frame of the specified context. + contextFrame = GetContextFGFrame (); + + // Restore the original context. + SetContext (oldContext); + + return contextFrame == Screen; +} + +static size_t +countVisibleContexts (void) +{ + size_t contextCount; + CONTEXT context; + + contextCount = 0; + for (context = GetFirstContext (); context != NULL; + context = GetNextContext (context)) + { + if (!isContextVisible (context)) + continue; + + contextCount++; + } + + return contextCount; +} + +static void +drawContext (CONTEXT context, double hue /* no pun intended */) +{ + FRAME drawFrame; + CONTEXT oldContext; + FONT oldFont; + COLOR oldFgCol; + COLOR rectCol; + COLOR lineCol; + COLOR textCol; + bool haveClippingRect; + RECT rect; + LINE line; + TEXT text; + POINT p1, p2, p3, p4; + + drawFrame = GetContextFGFrame (); + rectCol = (COLOR) fixColorRgba (drawFrame, + hsvaToRgba (hue, 1.0, 0.5, 127)); + lineCol = (COLOR) fixColorRgba (drawFrame, + hsvaToRgba (hue, 1.0, 1.0, 0)); + textCol = lineCol; + + // Save the original context. + oldContext = SetContext (context); + + // Get the clipping rectangle of the specified context. + haveClippingRect = GetContextClipRect (&rect); + + // Switch back the old context; we're going to draw in it. + (void) SetContext (oldContext); + + if (!haveClippingRect) + { + rect.corner.x = 0; + rect.corner.y = 0; + rect.extent.width = ScreenWidth; + rect.extent.height = ScreenHeight; + } + + p1 = rect.corner; + p2.x = rect.corner.x + rect.extent.width - 1; + p2.y = rect.corner.y; + p3.x = rect.corner.x; + p3.y = rect.corner.y + rect.extent.height - 1; + p4.x = rect.corner.x + rect.extent.width - 1; + p4.y = rect.corner.y + rect.extent.height - 1; + + oldFgCol = SetContextForeGroundColor (rectCol); + DrawFilledRectangleTransparent (&rect, 63); + + SetContextForeGroundColor (lineCol); + line.first = p1; line.second = p2; DrawLine (&line); + line.first = p2; line.second = p4; DrawLine (&line); + line.first = p1; line.second = p3; DrawLine (&line); + line.first = p3; line.second = p4; DrawLine (&line); + line.first = p1; line.second = p4; DrawLine (&line); + line.first = p2; line.second = p3; DrawLine (&line); + // Gimme C'99! So I can do: + // DrawLine ((LINE) { .first = p1, .second = p2 }) + + oldFont = SetContextFont (TinyFont); + SetContextForeGroundColor (textCol); + text.baseline.x = (p1.x + (p2.x + 1)) / 2; + text.baseline.y = p1.y + 8; + text.pStr = GetContextName (context); + text.align = ALIGN_CENTER; + text.CharCount = (COUNT) ~0; + font_DrawText (&text); + + (void) SetContextForeGroundColor (oldFgCol); + (void) SetContextFont (oldFont); +} + +static void +describeContext (FILE *out, const CONTEXT context) { + RECT rect; + CONTEXT oldContext = SetContext (context); + + GetContextClipRect (&rect); + fprintf(out, "Context '%s':\n" + "\tClipRect = (%d, %d)-(%d, %d) (%d x %d)\n", + GetContextName (context), + rect.corner.x, rect.corner.y, + rect.corner.x + rect.extent.width, + rect.corner.y + rect.extent.height, + rect.extent.width, rect.extent.height); + + SetContext (oldContext); +} + + +typedef struct wait_state +{ + // standard state required by DoInput + BOOLEAN (*InputFunc) (struct wait_state *self); + COUNT MenuRepeatDelay; +} WAIT_STATE; + + +// Maybe move to elsewhere, where it can be reused? +static BOOLEAN +waitForKey (struct wait_state *self) { + if (PulsedInputState.menu[KEY_MENU_SELECT] || + PulsedInputState.menu[KEY_MENU_CANCEL]) + return FALSE; + + SleepThread (ONE_SECOND / 20); + + (void) self; + return TRUE; +} + +// Maybe move to elsewhere, where it can be reused? +static FRAME +getScreen (void) +{ + CONTEXT oldContext = SetContext (ScreenContext); + FRAME savedFrame; + RECT screenRect; + + screenRect.corner.x = 0; + screenRect.corner.y = 0; + screenRect.extent.width = ScreenWidth; + screenRect.extent.height = ScreenHeight; + savedFrame = CaptureDrawable (LoadDisplayPixmap (&screenRect, (FRAME) 0)); + + (void) SetContext (oldContext); + return savedFrame; +} + +static void +putScreen (FRAME savedFrame) { + STAMP stamp; + + CONTEXT oldContext = SetContext (ScreenContext); + + stamp.origin.x = 0; + stamp.origin.y = 0; + stamp.frame = savedFrame; + DrawStamp (&stamp); + + (void) SetContext (oldContext); +} + +// Show the contexts on the screen. +// Must be called from the main thread. +void +debugContexts (void) +{ + extern void arith_frame_blit (FRAME srcFrame, const RECT *rsrc, + FRAME dstFrame, const RECT *rdst, int num, int denom); + static volatile bool inDebugContexts = false; + // Prevent this function from being called from within itself. + + CONTEXT orgContext; + CONTEXT debugDrawContext; + // We're going to use this context to draw in. + FRAME debugDrawFrame; + double hueIncrement; + size_t visibleContextI; + CONTEXT context; + size_t contextCount; + FRAME savedScreen; + + // Prevent this function from being called from within itself. + if (inDebugContexts) + return; + inDebugContexts = true; + + contextCount = countVisibleContexts (); + if (contextCount == 0) + goto out; + + LockMutex (GraphicsLock); + savedScreen = getScreen (); + //UnlockMutex (GraphicsLock); + FlushGraphics (); + // Make sure that the screen has actually been captured, + // before we use the frame. + + // Create a new frame to draw on. + debugDrawContext = CreateContext ("debugDrawContext"); + debugDrawFrame = CaptureDrawable (CreateDrawable ( + WANT_PIXMAP /*| WANT_ALPHA*/, ScreenWidth, ScreenHeight, 1)); + orgContext = SetContext (debugDrawContext); + SetContextFGFrame (debugDrawFrame); + + // Fill the new frame with a copy of the original. + arith_frame_blit (savedScreen, NULL, debugDrawFrame, NULL, 1, 1); + + hueIncrement = 360.0 / contextCount; + + //LockMutex (GraphicsLock); + visibleContextI = 0; + for (context = GetFirstContext (); context != NULL; + context = GetNextContext (context)) + { + if (context == debugDrawContext) { + // Skip our own context. + continue; + } + + if (isContextVisible (context)) + { + // Only draw the visible contexts. + drawContext (context, visibleContextI * hueIncrement); + visibleContextI++; + } + + describeContext (stderr, context); + } + + // Blit the final debugging frame to the screen. + putScreen (debugDrawFrame); + UnlockMutex (GraphicsLock); + + // Wait for a key: + { + WAIT_STATE state; + state.InputFunc = waitForKey; + DoInput(&state, TRUE); + } + + SetContext (orgContext); + + // Destroy the debugging frame and context. + DestroyContext (debugDrawContext); + // This does nothing with the drawable set with + // SetContextFGFrame(). + DestroyDrawable (ReleaseDrawable (debugDrawFrame)); + + LockMutex (GraphicsLock); + putScreen (savedScreen); + UnlockMutex (GraphicsLock); + + DestroyDrawable (ReleaseDrawable (savedScreen)); + +out: + inDebugContexts = false; +} + #endif /* DEBUG */ - diff --git a/sc2/src/uqm/uqmdebug.h b/sc2/src/uqm/uqmdebug.h index 173985cfc..d1996c7d6 100644 --- a/sc2/src/uqm/uqmdebug.h +++ b/sc2/src/uqm/uqmdebug.h @@ -29,9 +29,14 @@ // functions are a no-op. extern BOOLEAN disableInteractivity; -// If a function is assigned to this, it will be called from the main loop. +// If a function is assigned to this, it will be called from the +// Starcon2Main thread, in the main game loop. extern void (* volatile debugHook) (void); +// If a function is assigned to this, it will be called from the +// Starcon2Main thread, in doInput(). +extern void (* volatile doInputDebugHook) (void); + // Called when the debug key (symbol 'Debug' in the keys.cfg) is pressed. void debugKeyPressed (void); @@ -180,6 +185,11 @@ extern BOOLEAN instantMove; void dumpStrings(FILE *out); +// Graphically and textually show all the contexts. +// Should be called from debugHook. +void debugContexts (void); + + // To add some day: // - a function to fast forward the game clock to a specifiable time.