home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
VSCPPv8.zip
/
VACPP
/
IBMCPP
/
samples
/
TOOLKIT
/
MM
/
CAPDLL
/
CCDLL.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-11-17
|
63KB
|
1,272 lines
/******************************************************************************
* File Name : CCDLL.C
*
* Description : This file contains the C source code required for the
* caption DLL.
*
* Concepts : This caption DLL is part of MMPM/2 sample closed captioning
* system. Sample captioning system is composed of three
* parts: Caption Creation Utility, Caption DLL (this DLL),
* and Caption Sample Application. The Caption Creation
* Utility creates a "captioned" file. This is a text file
* with timing information relating to it's associated audio
* file. The The Caption DLL (this DLL) provides 3 API's that
* drive the display and management of a "caption window" in a
* PM application. The Caption Sample Application illustrates
* how an application uses the 3 API's provided by the caption
* DLL (this DLL) to take advantage of it's services.
*
* This sample captioning system is provided to illustrate one
* of the many ways that captioning may be implemented using
* MMPM/2. As with all MMPM/2 samples, this may be used as it
* is provided, or it may be modified in any way you prefer.
* Please refer to the Application Programmers Reference for
* more information on this sample captioning system.
*
* MMPM/2 API's: List of all MMPM/2 API's that are used in
* this module
*
* mciSendString
* MCI_SET_POSITION_ADVISE
*
* Required
* Files : ccdll.c Source Code.
* captions.h Include file.
* makefile Make file.
* cap.def Linker definition file.
*
* Copyright (C) IBM 1993
******************************************************************************/
#define INCL_GPILCIDS
#define INCL_GPIPRIMITIVES
#define INCL_GPIBITMAPS
#define INCL_WIN
#define INCL_DOSPROCESS
#define INCL_OS2MM /* required for MCI and MMIO headers */
#define INCL_WINWINDOWMGR
#define INCL_WINMESSAGEMGR
#define INCL_WINDIALOGS
#define INCL_DOSMODULEMGR
#include <stdlib.h> /*---------------------------------------*/
#include <string.h> /* Include standard c-library files */
#include <stdio.h> /*---------------------------------------*/
#include <os2.h> /*----- OS/2 system header files -------*/
#include <os2me.h> /*-- Headers for multimedia extensions --*/
#include "captions.h" /*--- General closed-captioning header --*/
/*------------------------- Local definitions --------------------------------*/
#define CC_TIMER_ID 1110
#define CC_TIMER_FREQ 0
#define CC_DEFAULT_TEXT_LINES 3 /* Default window height in text lines */
#define CC_DEFAULT_CHARS_WIDE 35 /* Default window width in characters */
#define MAX_TEXT_LINES 500
#define CHAR_NULL 0 /*------------------------------------------*/
#define CHAR_RETURN 13 /* Control characters present in ASCII files*/
#define CHAR_EOF 26 /*------------------------------------------*/
#define CC_FILE "CC_FILE"
/*------------ Values for ulFlags in CCINSTANCE strucuture -------------------*/
#define CC_TIMER_RUNNING 0x02 /* Set if the timer is running */
#define CC_CAPTIONS_ACTIVE 0x04 /* Set when we are actually doing captioning*/
#define CC_DRAG_MODE 0x08 /* Set when the cap. win. is being dragged */
#define CC_USER_POSITION 0x10 /* Set when user has set win pos */
/*------- This is the instance data for the closed-caption window ------------*/
typedef _Packed struct _CCINSTANCE
{
HAB hab; /* Anchor block for this window */
HWND hwndCaption; /* Handle of the caption window */
HWND hwndParent; /* Window in which caption window is displayed */
HWND hwndPosition; /* Send position messages here */
PFNWP pfnOldWinProc; /* Old window procedure of the parent window */
RECTL rclWin; /* Latest dimensions of caption window */
RECTL rclText; /* Size of the backup bitmap for the cap. win. */
HPS hpsText; /* Pres. space handle for the backup bitmap */
HBITMAP hbmText; /* Bitmap handle for the backup bitmap */
HDC hdcText; /* Device context for the backup bitmap */
PVOID pvLinedataTable; /* ^ to table of strings and times to display */
PVOID pvText; /* Pointer to the caption file text */
LONG lScrollOffset; /* Offset if scrolling new line into window */
LONG lScrollInc; /* Scroll step size */
LONG lLineSpacing; /* Y-pixels for each line */
LONG lLeftMargin; /* X-pixel start for each line */
LONG lBottomLineY; /* Y-coordinate of bottom-most line in cap. win*/
LONG lWindowColor; /* Background color of window */
LONG lTextColor; /* Color of text in window */
LONG lWindowXcoord; /* Cap. win. position if CC_USER_POSITION set */
LONG lWindowYcoord; /* Cap. win. position if CC_USER_POSITION set */
USHORT usWindowLines; /* Number of lines to display in window */
USHORT usWindowChars; /* Width of window, in text characters */
USHORT usNextlineReq; /* Number of queued line requests */
USHORT usDisplayLine; /* Index of BOTTOM-most line in capaption win */
USHORT usLineCount; /* Number of lines in caption file */
ULONG ulTimePrev; /* Time of previous position change message */
MPARAM mpMouseCoords; /* Mouse coords at start of drag */
HPOINTER hptrMove; /* Handle of the SPTR_MOVE pointer */
ULONG ulFlags; /* Flags for TRUE/FALSE data */
}
CCINSTANCE;
typedef CCINSTANCE FAR *PCCINSTANCE;
/*------------- Line data structure --------*/
/* */
typedef _Packed struct _LINEDATA /* There is one for each caption line */
{ /* */
PSZ szText; /* <-Pointer to a NULL-terminated text str. */
LONG lTextLen; /* <-Number of characters in the string */
ULONG ulTime; /* <-Time (MM units) to display the string */
} /* */
LINEDATA; /* An array of structures is maintained */
/* in a table pointed to by pvLinedataTable.*/
typedef LINEDATA FAR *PLINEDATA; /* */
/*------------------------------------------*/
/*----------------- Function prototype statements ----------------------------*/
MRESULT EXPENTRY CaptionWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
static APIRET StartCaptioning( PCCINSTANCE pid, CHAR szFilename[],
HWND hwndPosition, CHAR szDeviceName[] );
static VOID FormatTextBitmap( PCCINSTANCE pid, USHORT usLastLine );
static APIRET CreateBitmap( PCCINSTANCE pid );
static APIRET LoadCaptionFile( PCCINSTANCE pid, CHAR szCaptionFile[] );
static VOID StopCaptioning( PCCINSTANCE pid );
static void PositionCaptionWindow( PCCINSTANCE pid );
#define CC_WINCLASS "CapClass"
/*------------------------------- Global data area ---------------------------*/
HMODULE hmodule; /* The module handle of the captioning DLL */
/******************************************************************************
* Name : ccInitialize
*
* Description : This API creates a captioning window and returns the handle
* to the application. Call this function once to associate a
* captioning window with a user window.
*
* Parameters : hwndParent - The handle of the user window in where the
* captioning window will appear.
*
* Return : ULONG - The handle of the captioning window. If the
* function was unsuccessful, this will be NULL.
*
******************************************************************************/
ULONG APIENTRY ccInitialize ( HWND hwndParent )
{
HAB hab; /* Parent window's anchor block */
PFNWP pfnCaptionWindowProc; /* Pointer to DLL entry */
HWND hwndCaption; /* Handle of the caption window */
if ( hwndParent == (HWND)NULL )
return( CCERR_INVALID_WINDOW_HANDLE );
/*---------------- Create the captioning window ---------------------------*/
/* */
/* The captioning window is created as a child of the specified user */
/* window. It does not become visible until the user issues a CC_START */
/* message. Note that the captioning window may be overpainted by the */
/* user's parent window unless the user's window has the WS_CLIPCHILDREN */
/* style set. */
/*-------------------------------------------------------------------------*/
hab = WinQueryAnchorBlock ( (HWND) hwndParent );
WinRegisterClass ( hab, CC_WINCLASS, CaptionWindowProc,
CS_SYNCPAINT | CS_SIZEREDRAW, 4 );
hwndCaption = WinCreateWindow ( (HWND) hwndParent, CC_WINCLASS, NULL,
0, 0, 0, 1, 1, (HWND) hwndParent,
HWND_TOP, CC_WINDOW_ID, NULL, NULL );
return (ULONG) hwndCaption;
}
/******************************************************************************
* Name : ccSendCommand
*
* Description : This API is used to control the captioning window once it
* has been created.
*
* Parameters : usMsg - Captioning command.
* mp1 - Window handle of caption window.
* mp2 - Varies depending on command. See commands below.
*
* Return : ULONG - Captioning DLL's response to the message.
*
******************************************************************************/
ULONG APIENTRY ccSendCommand ( USHORT usMsg, MPARAM mp1, MPARAM mp2 )
{
USHORT msg;
switch ( usMsg )
{ /*---------------------- CC_START ----------------*/
case CC_START: /* mp1 - Reserved. Must be set to zero. */
msg = UMCC_START; /* mp2 - Pointer to a CC_START_PARMS structure */
break; /*------------------------------------------------*/
/*---------------------- CC_STOP -----------------*/
case CC_STOP: /* mp1 - Reserved. Must be set to zero. */
msg = UMCC_STOP; /* mp2 - Reserved. Must be set to zero. */
break; /*------------------------------------------------*/
/*----------------------- CC_SET -----------------*/
case CC_SET: /* mp1 - Reserved. Must be set to zero. */
msg = UMCC_SET; /* mp2 - Pointer to a CC_SET_PARMS structure. */
break; /*------------------------------------------------*/
/*------------- CC_STATUS ------------------------*/
case CC_STATUS: /* mp1 - Reserved. Must be set to zero. */
msg = UMCC_STATUS; /* mp2 - Pointer to a CC_STATUS_PARMS structure. */
break; /*------------------------------------------------*/
default:
msg = UMCC_ERROR; /* mp2 - Pointer to a CC_STATUS_PARMS structure. */
break;
}
/*
* After assigning the appropriate value to message, send it over to the
* window procedure to be processed. Obtain the handle of the caption
* window by extracting it from message parameter 1, where it was passed in.
*/
return (ULONG) WinSendMsg ( HWNDFROMMP(mp1), msg, mp1, mp2 );
}
/******************************************************************************
* Name : ccTerminate
*
* Description : This API closes the captioning system and releases any
* resources. Call this API once at the end of the appliaction.
*
* Parameters : hwndCaption - window handle of caption window.
*
* Return : None.
*
******************************************************************************/
VOID APIENTRY ccTerminate (HWND hwndCaption)
{
WinDestroyWindow ( hwndCaption );
DosFreeModule ( hmodule );
}
/******************************************************************************
* Name : CaptionWindowProc
*
* Description : This function controls the caption window. It will handle
* received messages from the user application.
*
* Concepts : - How to handle notification messages.
*
* MMPM/2 API's :
*
* Parameters : hwnd - Handle for the text window
* msg - Message received by the text dialog box.
* mp1 - Parameter 1 for the just received message.
* mp2 - Parameter 2 for the just received message.
*
* Return :
*
******************************************************************************/
MRESULT EXPENTRY CaptionWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
PCCINSTANCE pid; /* Pointer to the instance data for this win.*/
PLINEDATA pld; /* Pointer used to scan Line Data Table */
PCREATESTRUCT pcs; /* Pointer to window creation structure */
PCC_START_PARMS psp; /* Pointer to CC_START_PARMS */
PCC_SET_PARMS pSetParms; /* Pointer to CC_SET_PARMS structure */
PCC_STATUS_PARMS pStatusParms;/* Pointer to CC_STATUS_PARMS structure */
HPS hpsPaint; /*-------------------------------------------*/
RECTL rclPaint; /* Variables used in painting caption win. */
POINTL aptlPaint[4];/*-------------------------------------------*/
USHORT usOutputLine;/*-------- These variables are used -------*/
ULONG ulTime; /* when calculating which */
USHORT usTestLine; /*-------- caption line to show next -------*/
ULONG ulMemreq; /* Bytes of memory needed for instance data. */
RECTL rclParent; /* Updated dimensions of parent window */
LONG lXcoord, lYcoord;/* Updated origin of captioning window */
APIRET apiret; /* Value recturned with UMCC_START message */
SWP swp;
/*----------------------*/
if ( msg != WM_CREATE ) /* Get a pointer to the */
pid = (PCCINSTANCE) WinQueryWindowPtr(hwnd, 0); /* window instance data */
/*----------------------*/
switch ( msg )
{
case WM_CREATE:
DosAllocMem ( (PPVOID) &pid, sizeof(CCINSTANCE), fALLOC );
WinSetWindowPtr ( hwnd, 0, pid );
pcs = (PCREATESTRUCT) PVOIDFROMMP(mp2);
memset ( pid, 0, (size_t) sizeof(CCINSTANCE) ); /*------------------*/
pid->hab = WinQueryAnchorBlock( hwnd );/* */
pid->hwndCaption = hwnd; /* */
pid->hwndParent = pcs->hwndParent; /* */
pid->usWindowLines = CC_DEFAULT_TEXT_LINES; /* During WM_CREATE */
pid->usWindowChars = CC_DEFAULT_CHARS_WIDE; /* processing, we */
pid->lWindowColor = CLR_WHITE; /* allocate and */
pid->lTextColor = CLR_DARKBLUE; /* initialize the */
pid->lLeftMargin = 10; /* window instance */
pid->lScrollInc = 3; /* data. */
pid->rclWin.xRight = pcs->cx; /* */
pid->rclWin.yTop = pcs->cy; /*------------------*/
pid->hptrMove =
WinQuerySysPointer( HWND_DESKTOP, SPTR_MOVE, FALSE );
break;
case WM_SIZE: /*--------------------------*/
WinQueryWindowRect( hwnd, &pid->rclWin );/* Update the instance data */
break; /* every time the window is */
/* resized. */
/*--------------------------*/
/*---------------- Dragging the caption window -------------------------*/
/* */
/* The next three messages (WM_BUTTON1DOWN, WM_MOUSEMOVE, WM_BUTTON1UP)*/
/* are used for dragging the caption window. */
/* */
/* (1) When we receive a WM_BUTTON1DOWN message, we store the */
/* coordinates of the pointer (relative to the Caption Window) in */
/* the window instance data. */
/* */
/* (2) When we receive a WM_MOUSEMOVE message, we check to see if the */
/* pointer is still at the same relative position in the Caption */
/* Window, and... */
/* */
/* (3) If the pointer has moved relative to the window, we move the */
/* window enough to restore the original pointer position. */
/* */
/* (4) When the user releases the left mouse button, we end drag mode */
/* and... */
/* */
/* (5) Save the new window position for future captioning requests */
/* */
/*----------------------------------------------------------------------*/
case WM_BUTTON1DOWN:
WinSetCapture ( HWND_DESKTOP, hwnd );
WinSetPointer ( HWND_DESKTOP, pid->hptrMove );
pid->ulFlags |= CC_DRAG_MODE;
pid->mpMouseCoords = mp1; /* (1) Save pointer coords */
return 0;
case WM_MOUSEMOVE:
if ( pid->ulFlags & CC_DRAG_MODE )
{
if ( mp1 != pid->mpMouseCoords ) /* (2) Check for alignment */
{
WinQueryWindowPos ( hwnd, &swp );
swp.x += ( SHORT1FROMMP(mp1) - SHORT1FROMMP(pid->mpMouseCoords));
swp.y += ( SHORT2FROMMP(mp1) - SHORT2FROMMP(pid->mpMouseCoords));
WinSetWindowPos(hwnd, 0, swp.x, swp.y, 0, 0, SWP_MOVE );/*-(3)-*/
}
WinSetPointer ( HWND_DESKTOP, pid->hptrMove );
return 0;
}
break;
case WM_BUTTON1UP:
if ( pid->ulFlags & CC_DRAG_MODE )
{
pid->ulFlags &= ~CC_DRAG_MODE; /* (4) End drag mode */
WinSetCapture ( HWND_DESKTOP, 0 );
WinQueryWindowPos ( hwnd, &swp ); /* (5) Store position for */
pid->lWindowXcoord = swp.x; /* future requests. */
pid->lWindowYcoord = swp.y;
pid->ulFlags |= CC_USER_POSITION;
}
break;
/*----------------------------------------------------------------------------*/
/* */
/* When a WM_PAINT message is received, update the caption window from the */
/* text bitmap. In calculating the SOURCE blit rectangle, it is necessary to*/
/* take out account: */
/* */
/* (a) The window may not be the same size as the bitmap ( horiz & vert ) */
/* (b) The window may be scrolled with respect to the bitmap ( vert only ) */
/* */
/* Note: In this iteration of the program, the window and the backup bitmap */
/* always copy pixel-for-pixel. However, in future versions this */
/* might be so. Therefore I have put in scaling logic to accommodate */
/* any future version which might need it. */
/* */
/*----------------------------------------------------------------------------*/
case WM_PAINT:
hpsPaint = WinBeginPaint ( hwnd, 0, &rclPaint );
aptlPaint[0].x = rclPaint.xLeft; /*---------------------------------*/
aptlPaint[0].y = rclPaint.yBottom;/* The TARGET blit rect. is always */
aptlPaint[1].x = rclPaint.xRight; /* the same as the paint rectangle */
aptlPaint[1].y = rclPaint.yTop; /*---------------------------------*/
aptlPaint[2].x =
rclPaint.xLeft * pid->rclText.xRight / pid->rclWin.xRight;
aptlPaint[2].y =
rclPaint.yBottom * pid->rclText.yTop / pid->rclWin.yTop
+ pid->lScrollOffset;
aptlPaint[3].x =
rclPaint.xRight * pid->rclText.xRight / pid->rclWin.xRight;
aptlPaint[3].y =
rclPaint.yTop * pid->rclText.yTop / pid->rclWin.yTop
+ pid->lScrollOffset;
GpiSetColor ( hpsPaint, pid->lWindowColor );/* Normal Window color*/
GpiSetBackColor( hpsPaint, pid->lTextColor );/* Normal Text color */
GpiBitBlt ( hpsPaint, pid->hpsText, 4, aptlPaint,
ROP_SRCCOPY, BBO_IGNORE );
WinEndPaint ( hpsPaint );
return (MRESULT) 0;
/*----------------------------------------------------------------------------*/
/* We get a WM_TIMER message every time it is necessary to scroll the */
/* captions. The scroll increment is specified by lScrollInc in the window */
/* instance data. The caption window is repainted as part of the (1) */
/* WinScrollWindow operation, because of its CS_SYNCPAINT class style. */
/* After each scroll operation, we (2) check to see if we have scrolled */
/* one entire text line. If this is so, stop the timer and (3) check */
/* usNextlineReq to see if there are any more lines queued up to be scrolled. */
/*----------------------------------------------------------------------------*/
case WM_TIMER:
pid->lScrollOffset -= pid->lScrollInc;
WinScrollWindow( hwnd, 0, pid->lScrollInc, NULL, /* (1) Scroll window*/
NULL, 0, NULL, SW_INVALIDATERGN );
if ( ! pid->lScrollOffset ) /* (2) Text line done?*/
{
WinStopTimer ( pid->hab, hwnd, CC_TIMER_ID );
pid->ulFlags &= ~CC_TIMER_RUNNING;
if ( pid->usNextlineReq ) pid->usNextlineReq--;
if ( pid->usNextlineReq ) /* (3) Check for */
WinPostMsg ( hwnd, UMCC_NEXTLINE, 0, 0 ); /* queued lines.*/
}
break;
/*----------------------------------------------------------------------------*/
/* */
/* There are two cases in processing an MM_MCIPOSITIONCHANGE message: */
/* */
/* (1) The time in the message is LATER than the previous message (saved */
/* in the window instance data ). In this case, check to see if it's */
/* time to scroll another line into the caption window. */
/* */
/* (2) The time in the message is EARLIER than the previous message. This */
/* means that the user has rewound or backed up the audio file. Stop */
/* any current scrolling operation; reposition the usDisplayLine */
/* pointer, and show the repositioned captions. */
/* */
/* In any case, check to see if the user has requested us to forward the */
/* position messages to his window. */
/* */
/*----------------------------------------------------------------------------*/
case MM_MCIPOSITIONCHANGE:
if ( pid->ulFlags & CC_CAPTIONS_ACTIVE )
{
ulTime = (ULONG) LONGFROMMP(mp2);
if ( ulTime >= pid->ulTimePrev ) /********* CASE 1 **********/
{
usTestLine = pid->usDisplayLine + pid->usNextlineReq + 1;
if ( usTestLine >= (pid->usLineCount + 5) ) break;
pld = (PLINEDATA) pid->pvLinedataTable + usTestLine;
while ((usTestLine<pid->usLineCount) && (pld->ulTime<=ulTime))
{
pid->usNextlineReq++;
if ( pid->usNextlineReq == 1 )
WinPostMsg(hwnd,UMCC_NEXTLINE,MPFROMLONG((LONG)ulTime), 0);
pld++;
usTestLine++;
}
}
else
{ /********* CASE 2 **********/
if ( pid->ulFlags & CC_TIMER_RUNNING )
{
WinStopTimer ( pid->hab, hwnd, CC_TIMER_ID );
pid->ulFlags &= ~CC_TIMER_RUNNING;
}
pld = (PLINEDATA) pid->pvLinedataTable;
usTestLine = 0;
while ((usTestLine<pid->usLineCount) && (pld->ulTime<=ulTime))
{
usTestLine++;
pld++;
}
FormatTextBitmap ( pid, usTestLine );
pid->usNextlineReq = 0;
pid->usDisplayLine = usTestLine;
WinInvalidateRect ( hwnd, 0, FALSE );
}
pid->ulTimePrev = ulTime; /* Forward msg if requested*/
if ( pid->hwndPosition != 0 )
return WinSendMsg ( pid->hwndPosition, msg, mp1, mp2 );
}
return 0;
/*-------------- User request to start captioning ----------------------*/
case UMCC_START:
psp = (PCC_START_PARMS) PVOIDFROMMP(mp2);
apiret = StartCaptioning( pid, (PCHAR)psp->pszCaptionFile,
(HWND)psp->hwndOwner, (PCHAR)psp->pszDeviceName );
return (MRESULT) apiret;
/*--------------- User request to stop captioning ----------------------*/
case UMCC_STOP:
StopCaptioning ( pid );
return 0;
/*--------------- When we receive an invalid status request.------------*/
case UMCC_ERROR:
return (MRESULT) CCERR_INVALID_COMMAND;
/*----------------------------------------------------------------------*/
/* When we receive a status request, move the requested information */
/* from the window instance data to the ulReturn field of the status */
/* parameters. */
/*----------------------------------------------------------------------*/
case UMCC_STATUS:
pStatusParms = (PCC_STATUS_PARMS) PVOIDFROMMP(mp2);
switch ( pStatusParms->ulItem )
{
case CC_STATUS_TEXT_COLUMNS:
pStatusParms->ulReturn = (ULONG) pid->usWindowChars;
break;
case CC_STATUS_TEXT_ROWS:
pStatusParms->ulReturn = (ULONG) pid->usWindowLines;
break;
case CC_STATUS_TEXT_COLOR:
pStatusParms->ulReturn = (ULONG) pid->lTextColor;
break;
case CC_STATUS_BACKGROUND_COLOR:
pStatusParms->ulReturn = (ULONG) pid->lWindowColor;
break;
case CC_STATUS_X_POSITION:
pStatusParms->ulReturn = (ULONG) pid->lWindowXcoord;
break;
case CC_STATUS_Y_POSITION:
pStatusParms->ulReturn = (ULONG) pid->lWindowYcoord;
default:
pStatusParms->ulReturn = (ULONG) CCERR_INVALID_STATUS_REQUEST;
}
return 0;
/*--------------- Setting captioning parameters ------------------------*/
/* */
/* There are two phases involved in setting captioning parameters: */
/* */
/* (1) Move the data from the UMCC_SET message to the appropriate */
/* area(s) in the instance data for the captioning window. */
/* */
/* (2) If captioning is currently in progress, update the captioning */
/* window to reflect these changes... */
/* */
/* (2a) When changing window colors, force a repaint of the window.*/
/* */
/* (2b) When changing the number of columns or rows, we have to */
/* destroy the old backup text bitmap and create a new one. */
/* The captioning window must be hidden during this operation */
/* so it doesn't get any WM_PAINT message while the backup */
/* bitmap is in limbo. For the same reason, scrolling */
/* operations and new line requests must be shut off. Once */
/* the new bitmap is made, reposition and show the resized */
/* captioning window. */
/* */
/* (2c) When changing the window position, move the captioning */
/* window */
/* */
/*----------------------------------------------------------------------*/
case UMCC_SET:
pSetParms = (PCC_SET_PARMS) PVOIDFROMMP(mp2);
pid->usWindowChars = (USHORT) pSetParms->ulColumns;
pid->usWindowLines = (USHORT) pSetParms->ulRows;
pid->lTextColor = (LONG) pSetParms->ulTextColor;
pid->lWindowColor = (LONG) pSetParms->ulBackgroundColor;
pid->ulFlags |= CC_USER_POSITION;
pid->lWindowXcoord = (LONG) pSetParms->ulXposition;
pid->ulFlags |= CC_USER_POSITION;
pid->lWindowYcoord = (LONG) pSetParms->ulYposition;
if (pid->ulFlags & CC_CAPTIONS_ACTIVE) /*(2)Update caption window, if*/
{
WinInvalidateRect ( hwnd, NULL, FALSE );
WinSetWindowPos ( hwnd, 0, 0, 0, 0, 0, SWP_HIDE );
if ( pid->ulFlags & CC_TIMER_RUNNING )
{
WinStopTimer ( pid->hab, hwnd, CC_TIMER_ID );
pid->ulFlags &= ~CC_TIMER_RUNNING;
}
pid->lScrollOffset = 0;
pid->usNextlineReq = 0;
apiret = CreateBitmap ( pid );
if (apiret)
return (MRESULT) apiret;
FormatTextBitmap ( pid, pid->usDisplayLine );
PositionCaptionWindow ( pid );
WinSetWindowPos ( hwnd, 0, 0, 0, 0, 0, SWP_SHOW );
PositionCaptionWindow ( pid );
}
return 0;
/*----------------------------------------------------------------------*/
/* We receive this message when it is time to scroll to the next line. */
/* First make sure that (1) the audio file has not been backspaced */
/* since the messagewas issued. If the file has not been backspaced, */
/* go ahead and (2) start the scroll operation. */
/*----------------------------------------------------------------------*/
case UMCC_NEXTLINE:
if ( pid->ulFlags & CC_CAPTIONS_ACTIVE )
{
ulTime = (ULONG) LONGFROMMP(mp1);
if (ulTime <= pid->ulTimePrev) /*(1)Check to see if audio backspcd*/
{
pid->usDisplayLine++;
if ( pid->usDisplayLine < pid->usLineCount )
{
FormatTextBitmap(pid, pid->usDisplayLine);/*(2)Initiate scrl*/
pid->lScrollOffset = pid->lLineSpacing;
WinStartTimer ( pid->hab, hwnd, CC_TIMER_ID, CC_TIMER_FREQ );
pid->ulFlags |= CC_TIMER_RUNNING;
}
}
}
return 0;
case WM_DESTROY:
StopCaptioning ( pid );
DosFreeMem ( pid );
break;
}
return WinDefWindowProc ( hwnd, msg, mp1, mp2 );
}
/******************************************************************************
* Name : StartCaptioning
*
* Description : This procedure will initiate captioning and loads the
* caption file. Initializes internal data structures and the
* caption window.
*
* MMPM/2 API's : mciSendString
*
* Parameters : pid Pointer to Instance Data for caption window
*
* szFilename Caption file name (NULL terminated)
*
* hwndOwner If you want the captioning system to pass
* position advise messages to one of your
* windows, pass the handle of that window
* here. Otherwise pass NULL.
*
* szDeviceName Alias/Device name of the audio device. Pass
* this so that the caption system can request
* MM_MCIPOSITIONCHANGE messages from this
* device.
*
* Return : 0 - if the operation was initiated without error.
* apiret - if an error occurred.
*
******************************************************************************/
static APIRET StartCaptioning ( PCCINSTANCE pid, CHAR szFilename[],
HWND hwndOwner, CHAR szDeviceName[] )
{
APIRET apiret; /* Values returned from DOS operations */
ULONG ulBytesReq; /* Bytes requested for DosAllocMem calls*/
CHAR szCommandString[CCHMAXPATH] =
"setpositionadvise ";
LONG lSendStringRC = 0;
/*---------- Check for device ID. Return error if not found.------------------*/
if (strlen(szDeviceName) == 0)
return (APIRET) CCERR_NO_DEVICE_NAME;
/*---------- Stop any captioning that is currently underway ------------------*/
if ( pid->ulFlags & CC_CAPTIONS_ACTIVE )
StopCaptioning ( pid );
apiret = CreateBitmap( pid ); /* Create the backup bitmap for painting */
/* captions. */
if (apiret)
return (APIRET) apiret;
/*----- Load the caption file, creating Text and Line Data Tables ------------*/
ulBytesReq = (ULONG) ( MAX_TEXT_LINES * sizeof(LINEDATA) );
DosAllocMem ( &pid->pvLinedataTable, ulBytesReq, fALLOC );
apiret = LoadCaptionFile ( pid, szFilename );
if ( apiret )
{ /*---------------------------------*/
if ( pid->pvText ) /* */
{ /* If we were unable to load the */
DosFreeMem ( pid->pvText ); /* caption file, then clean up */
pid->pvText = (PVOID) NULL; /* any resources we allocated, and*/
} /* return an error code. */
DosFreeMem ( pid->pvLinedataTable ); /* */
pid->pvLinedataTable = (PVOID) NULL; /*---------------------------------*/
return apiret;
}
/*---------------- Format the first few lines of text ------------------------*/
FormatTextBitmap ( pid, 0 ); /* Write the first line to text to the bitmap.*/
/*---------------- Set instance data to initial values -----------------------*/
pid->lScrollOffset = 0;
pid->usDisplayLine = 0;/* Index of line currently at bottom of the cap. win*/
pid->usNextlineReq = 0;
pid->ulTimePrev = 0;
pid->hwndPosition = hwndOwner;
/*--- Ask MMPM/2 for position advise messages on the device ------------------*/
strcat( szCommandString, szDeviceName);
strcat( szCommandString, " ");
strcat( szCommandString, "on every 1500 notify");
lSendStringRC =
mciSendString( (PSZ)szCommandString,
(PSZ)NULL,
CCHMAXPATH,
pid->hwndCaption,
0 );
if (lSendStringRC != 0)
return (APIRET) lSendStringRC;
PositionCaptionWindow ( pid );
pid->ulFlags |= CC_CAPTIONS_ACTIVE;
WinSetWindowPos ( pid->hwndCaption, 0, 0, 0, 0, 0, SWP_SHOW );
return (APIRET) 0;
}
/******************************************************************************
* Name : PositionCaptionWindow
*
* Description : This procedure will position the caption window in its
* parent window and display it.
*
* Parameters : pid Pointer to Instance Data for caption window
*
* Return : none
*
******************************************************************************/
static void PositionCaptionWindow ( PCCINSTANCE pid )
{
RECTL rclParent; /* Receives dimensions of the parent of the caption win */
LONG lXcoord; /* Computed position of caption window - X coordinate */
LONG lYcoord; /* Computed position of caption window - Y coordinate */
/*------------------------ Locic summary -------------------------------------*/
/* */
/* (1) Calculate horizontal position. If the user has specified a position, */
/* then use that position. If not, then center the Captioning Window */
/* horizontally in its parent window. */
/* */
/* (2) Calculate the vertical position. If the user has specified a position,*/
/* then use that position. If not, then place the captioning window near*/
/* the bottom of the parent window. */
/* */
/*----------------------------------------------------------------------------*/
WinQueryWindowRect ( pid->hwndParent, &rclParent );
if ( pid->ulFlags & CC_USER_POSITION ) /* (1) Horizontal position */
lXcoord = pid->lWindowXcoord;
else
{
lXcoord = ( rclParent.xRight - pid->rclText.xRight ) / 2;
pid->lWindowXcoord = lXcoord;
}
if ( pid->ulFlags & CC_USER_POSITION ) /* (2) Vertical position */
lYcoord = pid->lWindowYcoord;
else
{
lYcoord = rclParent.yTop / 12;
pid->lWindowYcoord = lYcoord;
}
pid->rclWin = pid->rclText;
WinSetWindowPos( pid->hwndCaption, 0, lXcoord, lYcoord, pid->rclText.xRight,
pid->rclText.yTop, SWP_MOVE | SWP_SIZE );
}
/******************************************************************************
* Name : CreateBitmap
*
* Description : This procedure will create a backup text bitmap for the
* captioning window. Data from the bitmap will be blitted
* to the caption window whenever it receives a WM_PAINT
* message.
*
* Parameters : pid - Pointer to Instance Data for caption window
*
* Return : none
*
******************************************************************************/
static APIRET CreateBitmap ( PCCINSTANCE pid )
{
FONTMETRICS fmText; /* Font metrics for backup bitmap font */
SIZEL sizelText; /* Needed to initialize the bitmap */
BITMAPINFOHEADER2 bmh2; /* Needed to initialize the bitmap */
LONG lModulo;
APIRET apiret;
/*----------------------- Logic overview ----------------------------------*/
/* */
/* (1) If a backup bitmap already exists for this window, destroy it */
/* before creating the new bitmap. (We can't just use the old bitmap */
/* because the user might have decided to resize the window.) */
/* */
/* (2) Open a new device context and create a new presentation space for */
/* the backup bitmap. */
/* */
/* (3) Get the font metrics for the default font for this presentation */
/* space. Use this data in conjunction with the window instance data */
/* (usWindowLines, usWindowChars) to determine the dimensions of the */
/* bitmap. The bitmap will be taller than the window by the height */
/* of one text line. This is so the caption window can be repainted */
/* correctly while it is scrolling. */
/* */
/* (4) Create the backup bitamp. This is a monochrome bitmap; the colors */
/* of the text and background are determined during the WM_PAINT */
/* message for the caption window. */
/* */
/*-------------------------------------------------------------------------*/
if ( pid->hpsText )
{
GpiDestroyPS ( pid->hpsText );/*--------------------------------------*/
DevCloseDC ( pid->hdcText );/* (1)Destroy any existing backup bitmap*/
GpiDeleteBitmap( pid->hbmText );/*--------------------------------------*/
}
/*--------- (2) Open device context and create presentation space ---------*/
pid->hdcText = DevOpenDC ( pid->hab, OD_MEMORY, "*", 0, NULL, 0 );
sizelText.cx = 0;
sizelText.cy = 0;
pid->hpsText = GpiCreatePS( pid->hab, pid->hdcText, &sizelText,
PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC);
/*--------- (3) Use the font metrics to determine size of bitmap ----------*/
GpiQueryFontMetrics ( pid->hpsText, (LONG) sizeof(fmText), &fmText );
pid->lLineSpacing = fmText.lMaxBaselineExt /*-----------------------*/
+ fmText.lExternalLeading /* Force the line spacing*/
+ pid->lScrollInc; /* to be a multiple of */
if (lModulo = pid->lLineSpacing % pid->lScrollInc)/* the scroll increment. */
pid->lLineSpacing += pid->lScrollInc - lModulo;/*-----------------------*/
pid->rclText.yTop = pid->lLineSpacing /* Height of caption window*/
* (LONG) pid->usWindowLines /* ( The bitmap is one text*/
+ pid->lScrollInc; /* line greater in height.)*/
pid->rclText.xRight = fmText.lAveCharWidth * pid->usWindowChars;
pid->lBottomLineY = pid->rclText.yTop -
( pid->lLineSpacing * pid->usWindowLines ) +
fmText.lMaxDescender;
/*--------------------- (4) Create the bitmap -----------------------------*/
bmh2.cbFix = sizeof (BITMAPINFOHEADER2);
bmh2.cx = pid->rclText.xRight;
bmh2.cy = pid->rclText.yTop+pid->lLineSpacing;/* Bitmap height*/
bmh2.cPlanes = 1;
bmh2.cBitCount = 1; /* Monochrome */
bmh2.ulCompression = BCA_UNCOMP;
bmh2.cbImage = 0;
bmh2.cxResolution = 30000;
bmh2.cyResolution = 30000;
bmh2.cclrUsed = 2;
bmh2.cclrImportant = 2;
bmh2.usUnits = BRU_METRIC;
bmh2.usReserved = 0;
bmh2.usRecording = BRA_BOTTOMUP;
bmh2.usRendering = BRH_NOTHALFTONED;
bmh2.cSize1 = 0;
bmh2.cSize2 = 0;
bmh2.ulColorEncoding = BCE_RGB;
bmh2.ulIdentifier = 0;
pid->hbmText = GpiCreateBitmap(pid->hpsText, &bmh2, 0L, 0L, NULL);
if (pid->hbmText == 0)
return (APIRET) CCERR_CANNOT_CREATE_BITMAP;
GpiSetBitmap ( pid->hpsText, pid->hbmText );
GpiErase ( pid->hpsText );
return(APIRET) 0;
}
/******************************************************************************
* Name : FormatTextBitmap
*
* Description : This procedure will write text to the backup bitmap beginning
* with the bottom line and working up.
*
* Parameters : pid - Pointer to Instance Data for caption window
* usLastLine - Index of bottom-most text line in bitmap.
*
* Return : none
*
******************************************************************************/
static VOID FormatTextBitmap ( PCCINSTANCE pid, USHORT usLastLine )
{
PLINEDATA pld; /* Pointer to LINEDATA structure for current line */
POINTL ptlText; /* Coordinate of text */
USHORT usCurrLine; /* Index of line currently being written to bitmap */
USHORT usLineCount;/* Counter for number of lines written to text bitmap*/
GpiErase ( pid->hpsText );
pld = (PLINEDATA) pid->pvLinedataTable + usLastLine;
ptlText.x = pid->lLeftMargin;
ptlText.y = pid->lBottomLineY;
usCurrLine = usLastLine;
for ( usLineCount = 0; usLineCount <= pid->usWindowLines; usLineCount++ )
{
GpiCharStringAt(pid->hpsText, &ptlText, pld->lTextLen, (PCH)pld->szText);
if ( usCurrLine == 0 ) break;
ptlText.y += pid->lLineSpacing;
pld--;
usCurrLine--;
}
}
/******************************************************************************
* Name : LoadCaptionFile
*
* Description : This procedure will load a caption file for processing.
*
* Parameters : pid - Pointer to Instance Data for caption window
* szCaptionFile - NULL-terminated caption file name.
*
* Return : 0 - if the operation was initiated without error.
* apiret - if an error occurred.
*
******************************************************************************/
static APIRET LoadCaptionFile( PCCINSTANCE pid, CHAR szCaptionFile[] )
{
APIRET apiret; /* DOS return code */
HFILE hfile; /* Text file handle */
ULONG ulAction; /* Parameter returned by DOS API functions */
ULONG ulFileSize; /* Expected file size, used as a read check */
ULONG ulBytesRemaining;/* Bytes in file remaining to be scanned */
PLINEDATA pld; /* Points to "current" line in Line Data Table */
UCHAR ucEndChar; /* Used to save char immed. following MM time */
PSZ psz; /* Pointer to "current line" in caption file */
PVOID pvPositions; /* Points to table used by GpiQueryCharPosAt */
PPOINTL pptl; /* (Same as above, but cast to a useful type) */
POINTL ptlStart; /* Set to co-or of leftmost char in caption win */
PLINEDATA pldNew; /* Points to "remainder" of line after word-wrap*/
SHORT sIndex; /* Used to scan line for a logical breaking point.*/
PSZ pszEnd; /* Points to first memory position after text data*/
/*----------------------------------------------------------------------------*/
if ( pid->pvText ) /*-----------------------------------------*/
{ /* */
DosFreeMem ( pid->pvText ); /* Free any text that is currently loaded. */
pid->pvText = NULL; /* */
} /*-----------------------------------------*/
/*----------------------- Open the text file ---------------------------------*/
apiret = DosOpen ( (PSZ) szCaptionFile, &hfile, &ulAction, 0,
FILE_NORMAL | FILE_READONLY, FILE_OPEN,
OPEN_ACCESS_READONLY | OPEN_SHARE_DENYREADWRITE, NULL );
if ( apiret ) return apiret;
/*------------ Read the entire file into a memory area -----------------------*/
DosSetFilePtr ( hfile, 0, FILE_END, &ulFileSize ); /* Determine file size. */
apiret =
DosAllocMem( &pid->pvText, ulFileSize, fALLOC );/* Alloc memory for it. */
if ( apiret)
{
DosClose ( hfile );
return apiret;
}
DosSetFilePtr ( hfile, 0, FILE_BEGIN, &ulAction );/* Read file image into */
apiret = DosRead ( hfile, pid->pvText, ulFileSize, &ulAction );/* buffer. */
DosClose ( hfile );
if ( ! apiret && ( ulFileSize != ulAction ) ) apiret = CCERR_UNEXPECTED_EOF;
pszEnd = (PSZ) pid->pvText + ulFileSize;
if ( apiret ) return apiret;
/*----------------------------------------------------------------------------*/
/* Make sure the file is in the correct format, by checking the first line */
/* for the constant "CC_FILE". */
/*----------------------------------------------------------------------------*/
if ( strncmp( (char *) pid->pvText, "CC_FILE", 7 ) )/* Check for format ID */
return(APIRET) CCERR_FILE_FORMAT; /* Return error if not */
/* found. */
psz = (PSZ) memchr ( pid->pvText, CHAR_RETURN, (size_t) ulFileSize );
if ( psz == NULL ) return(APIRET) CCERR_FILE_FORMAT;/* Bump pointer psz */
psz += 2; /* to the start of the */
if ( ( *psz == CHAR_EOF ) || ( psz >= pszEnd ) ) /* first line of data. */
return (APIRET) CCERR_FILE_FORMAT;
/*----------------------------------------------------------------------------*/
/* Allocate memory to hold a character-position table. This table will be */
/* used to determine if and when it will be necessary to split a line to fit */
/* into the captioning window. */
/*----------------------------------------------------------------------------*/
DosAllocMem ( &pvPositions, 256 * sizeof(POINTL), fALLOC );
pptl = (PPOINTL) pvPositions;
ptlStart.x = pid->lLeftMargin; /* Get memory for char */
ptlStart.y = pid->lBottomLineY; /* position table. */
/*----------------------------------------------------------------------------*/
/* */
/* Now loop through every line in the caption file. For each line build an */
/* entry in the Line Data Table. There will be AT LEAST as many entries in */
/* the Line Data Table as there are lines in the caption file. */
/* */
/* (1) The first eight characters of the line is an ASCII number representing*/
/* the time ( in MM units ) when the caption is to be scrolled into the */
/* window. The text (if any) begins in column ten. */
/* */
/* (2) The caption text runs from column ten to the next carriage return. */
/* Determine its length and place that in pld->lTextLen. */
/* */
/* (3) Wrap text lines that are wider than the caption window. Create extra */
/* entries in the Line Data Table for the extra lines that are needed. */
/* */
/*----------------------------------------------------------------------------*/
pld = (PLINEDATA) pid->pvLinedataTable;
for ( pid->usLineCount = 1; pid->usLineCount < MAX_TEXT_LINES;
pid->usLineCount++ )
{
ucEndChar = psz[8]; /*----------------------------*/
psz[8] = CHAR_NULL; /* (1) Convert the time to a */
pld->ulTime = (ULONG) atol((PCHAR) psz);/* long integer and setup a */
pld->szText = psz + 9; /* default text len of zero */
pld->lTextLen = 0; /*----------------------------*/
if ( ucEndChar != CHAR_RETURN ) /* (2) If text present, find its */
{ /* length. */
ulBytesRemaining = (ULONG) ( (ULONG) pszEnd - (ULONG) psz );
psz =
(PSZ) memchr( pld->szText, CHAR_RETURN, (size_t) ulBytesRemaining );
if ( psz == NULL ) break;
*psz = CHAR_NULL;
pld->lTextLen = (LONG) strlen ( pld->szText );
psz += 2;
/*-------------------------------------------------------------------*/
/* */
/* (3) Wrap text lines that are too wide to fit into the caption */
/* window. The overall strategy of this logic is as follows: If */
/* the line istoo wide, divide it into two parts--a part that */
/* will fit and a part which contains everything else; then */
/* apply this same strategy to the second part of the line. Keep */
/* repeating this logic until we are left a line which will fit */
/* without any left-over parts. */
/* */
/* In detail: */
/* */
/* (3a) Determine the width of the current line. If it fits */
/* in the caption window, we are done! Otherwise... */
/* */
/* (3b) We will have to divide the line. Make a new entry in */
/* the Line Data Table. We will use this for the second */
/* part of the line. */
/* */
/* (3c) Loop backwards through the Character Position Table */
/* until we find the last space character that will fit */
/* in the window. (If there are no blanks in the first */
/* portion, just chop the line off at the window width */
/* for a "Procrustean" fit.) */
/* */
/* (3d) Make the second portion of the original line the new */
/* "current" line and repeat the operation. */
/* */
/*-------------------------------------------------------------------*/
for ( ; ; )
{ /* (3a) Does it fit yet?*/
GpiQueryCharStringPosAt( pid->hpsText, &ptlStart, 0, pld->lTextLen,
(PCH) pld->szText, NULL, pptl );
if ( pptl[pld->lTextLen].x <= pid->rclText.xRight )
break;
pid->usLineCount++; /*--------------------------*/
if (pid->usLineCount>=MAX_TEXT_LINES) /* (3b) Make sure there is */
{ /* enough room in the Line */
DosFreeMem ( pvPositions ); /* Data Table for new line. */
return(APIRET)CCERR_TOO_MANY_LINES;/* If so, use the existing */
} /* Line Data entry as the */
pldNew = pld + 1; /* basis for the new one. */
*pldNew = *pld; /*--------------------------*/
for ( sIndex = pld->lTextLen; sIndex > 0; sIndex-- )
if ( pptl[sIndex].x <= pid->rclText.xRight ) break;
pld->lTextLen = (LONG) sIndex;
sIndex--; /*--------------------------*/
while ( sIndex > 0 ) /* (3c) Truncate existing */
{ /* line at the last space */
if ( pld->szText[sIndex] == ' ' ) /* that will fit. */
break; /*--------------------------*/
sIndex--;
}
if ( sIndex ) pld->lTextLen = (LONG) sIndex + 1;
/*-----------------------------------------------*/
/* (3d) Begin new line where old line ends. */
/*-----------------------------------------------*/
pldNew->lTextLen -= pld->lTextLen;
pldNew->szText = pld->szText + pld->lTextLen;
pld = pldNew;
}
}
else
psz += 10; /* (Take this branch of the if in case there */
/* is no caption text) */
/*------------ Break out if the loop when we reach end of file ---------------*/
if ( ( *psz == CHAR_EOF ) || ( psz >= pszEnd ) )
break;
pld++;
}
DosFreeMem( pvPositions );/* Free memory used for the Char. Position Table */
/*---------------------------- Make some final checks ------------------------*/
/* */
/* (1) Make sure the file ends in a standard EOF character. */
/* (2) See if we had enough room in the Line Data Table to hold the file. */
/*----------------------------------------------------------------------------*/
if ( psz == NULL )
return (APIRET) CCERR_FILE_FORMAT;
if ( pid->usLineCount == MAX_TEXT_LINES )
return (APIRET) CCERR_TOO_MANY_LINES;
return 0;
}
/******************************************************************************
* Name : StopCaptioning
*
* Description : This procedure will stop the current captioning operation.
*
* Parameters : pid - Pointer to Instance Data for caption window
*
* Return : none
*
******************************************************************************/
static VOID StopCaptioning ( PCCINSTANCE pid )
{
pid->ulFlags &= ~CC_CAPTIONS_ACTIVE;
WinSetWindowPos ( pid->hwndCaption, 0, 0, 0, 0, 0, SWP_HIDE );
if ( pid->ulFlags & CC_TIMER_RUNNING )
{
WinStopTimer ( pid->hab, pid->hwndCaption, CC_TIMER_ID );
pid->ulFlags &= ~CC_TIMER_RUNNING;
}
if ( pid->hpsText )
{
GpiDestroyPS ( pid->hpsText ); /*------------------------------------*/
DevCloseDC ( pid->hdcText ); /* Destroy any existing backup bitmap */
GpiDeleteBitmap ( pid->hbmText ); /*------------------------------------*/
pid->hpsText = (HPS) 0;
}
if ( pid->pvText )
{
DosFreeMem ( pid->pvText );
pid->pvText = (PVOID) NULL;
}
if ( pid->pvLinedataTable )
{
DosFreeMem ( pid->pvLinedataTable );
pid->pvLinedataTable = (PVOID) NULL;
}
}