DDDR/2 - Seamless Windows Support

=Seamless Windows Support= Seamless support provides the ability to execute Windows applications in one or more windows on the OS/2 Desktop. The Windows applications run side by side with OS/2 Presentation Manager (PM) applications. To provide seamless support, you must modify both the PM and Windows display drivers.

Overview
Each seamless windows session is run as a protect-mode DOS session. The seamless session runs a copy of the Windows video display driver, modified to operate seamlessly. Seamless windows drivers write directly to the video display hardware, and must be modified to coordinate and serialize hardware access. As a result, a seamless windows driver actually "owns" a piece of the PM Desktop.

In addition to modifications to the PM and Windows drivers, the IBM Operating System/2 itself has been modified to support seamless operation. A special virtual device driver (VWIN) is used to establish limited addressability to a small set of key PM routines and data, as well as to provide services permitting hardware serialization and exchange of Windows state-change information.

Presentation Manager Display Device Driver
To enable a seamless session, the Presentation Manager display device driver must call the special virtual device driver, VWIN, using DOSRequestVDD. The PM driver passes initialization data to enable the seamless windows driver's access to necessary PM data and the shared data and code. After initialization, the PM driver again calls VWIN to define the current video mode and cursor size.

The PM display driver is responsible for initializing a fast safe RAM semaphore. (FSRamSemaphore) for synchronizing access to the video hardware. Both the PM and the seamless windows drivers must use the FSRamSemaphore to coordinate video access. The PM driver can force release of this semaphore when necessary, for example, if the seamless DOS session stops with the Windows driver owning the semaphore. To avoid forcing an inappropriate semaphore release, heartbeat processing has been incorporated into VWIN. The PM driver requests VWIN to initiate heartbeat processing when a timeout occurs on the PM driver's request for the FSRamSemaphore.

The PM Shield component, shown in the diagram in the previous figure, communicates with the PM driver, sending notifications whenever a seamless DOS session is created or destroyed. This enables the PM driver to modify its processing logic to be compatible with the seamless environment.

There are several other calls the PM video driver can issue to VWIN. At PM death and resurrection (that is, on a switch to a full-screen DOS or WIN-OS/2 session), the driver calls VWIN to broadcast suspend/resume drawing messages to the seamless DOS sessions. These messages are transmitted to the DOS session's drivers by way of INT 2Fh.

Seamless Windows Display Device Driver
The seamless windows display device driver is usually derived from the standard Windows driver by conditional compilation. It must always start in the disabled state to establish all necessary addressability and data before beginning seamless operation. To start in a disabled state, the driver must simulate an INT 2Fh, Function 4001h, at startup. The seamless windows driver hooks INT 2Fh to receive all notifications of foreground- background switching. The Win Shield component, diagrammed in the previous figure, enables the seamless windows driver, using INT 2Fh, Function 4002h, when synchronized with the Presentation Manager Desktop.

Because the virtual display driver and the Win Shield have the ability to disable video output from the Windows driver (through INT 2FhH), a cumulative disable count must be kept in the driver. When a Disable occurs (INT 2Fh, AX=4001h), the count is incremented. On the first increment (count = 1), output from the driver is disabled. Alternately, when an Enable occurs (INT 2Fh, AX=4002h), the count is decremented. When the count is 0, driver output can be enabled.

Most seamless processing and message handling is asynchronous; therefore, it is possible for multiple Disables to be received before any Enables take place. Events are posted to the DOS session and retained until a time slice occurs and they can be processed. As stated previously, a cumulative disable count is required.

At seamless windows driver startup, the driver must begin with a Disable count of 1 (in effect, forcing an INT 2Fh Disable on itself) to permit all the window shadows from the desktop to be reflected to the DOS session's Win Shield. When this process is complete, the Win Shield enables the seamless windows driver by way of INT 2Fh.

When the first Enable call is received, the seamless windows display driver must call VWIN, passing it a pointer to a WINDD_INIT structure. (The WINDD_INIT structure is described in detail later in the text.) VWIN completes this structure for the Windows driver.

It is critical for the seamless windows driver to coordinate all hardware access, using the video FSRamSemaphore, and it must always reset the video hardware to a default state upon release of the semaphore.

The seamless windows driver need not perform any cursor drawing operations, nor does it need to save and restore hardware registers when switched to the background. These functions are performed by the Presentation Manager driver. However, the seamless driver must call the PM cursor-exclusion routines when outputting to the screen, to protect the mouse pointer and prevent its erasure.

Presentation Manager Shield
The PM Shield maintains Workplace Shell windowing-state information, including that related to the seamless DOS sessions.

Upon creation of a seamless DOS session, the PM Shield notifies the virtual video device driver (for example, VVGA or VSVGA) to permit direct hardware access. Normally, the virtual video drivers control and intercept attempts to access the hardware. In a seamless DOS session, however, virtual video is modified to permit this hardware I/O.

As windowing-related events occur in the seamless DOS sessions, the PM Shield duplicates these events in the Workplace Shell to track the seamless DOS sessions. Examples of windowing events are:
 * Create
 * Destroy
 * Move
 * Resize

In addition to monitoring the seamless DOS sessions, the PM Shield registers with PMWIN to be posted whenever events occur that alter the state of the desktop. (Again, events such as Create and Destroy are monitored.) These events are forwarded to VWIN, which broadcasts them to the Win Shield in each seamless DOS session.

Windows Shield
The Windows (Win) Shield is the Windows counterpart of the PM Shield. It is both an executable file and a dynamic link library (WINSHELD.EXE and WINSMSG.DLL, respectively). Win Shield serves a complementary purpose, maintaining Workplace Shell windowing-state information for its DOS session.

Initial state information for the desktop is received when the Win Shield registers with VWIN. Subsequently, VWIN notifies the Win Shield whenever changes on the desktop occur. All the desktop information is received from the PM Shield and passed on by VWIN.

Additionally, the Win Shield registers with the Windows User component to obtain notification of internal Windows events. This information is then forwarded to VWIN to update the PM Shield.

VWIN Virtual Device Driver
VWIN is the special virtual device driver that lies at the heart of seamless operation. VWIN shares all Windows and PM Desktop windowing-state information, permits addressability to seamless data and code shared between the PM and Windows display drivers, and sends INT 2Fh notification to each seamless DOS session at PM death and resurrection, as described previously.

Interface to VWIN is achieved using the following two mechanisms:
 * PM services are requested using DOSRequestVDD.
 * Windows services are requested through INT 66h.

Note: The Windows services must be requested by an interrupt because VWIN executes at Ring 0 (the highest privilege level) and the seamless windows drivers do not.

Distributed Console Access Facility (DCAF)
Seamless Windows support is provided in DCAF for installations that require remote console functions between programmable workstations.

In OS/2 2.1, Seamless Windows is supported by allowing the Windows display driver to draw directly on the Presentation Manager (PM) screen. This means that Seamless Windows updates do not go through the PM drawing functions and, therefore, will not update the active screen change area (SCA) in the usual way. As a result, Seamless Windows requires special treatment.

Prior to drawing on the PM screen, the Seamless Windows driver calls the PM driver through an exported entry point, SeamlessExcludeCursor. This call excludes the PM cursor from the area in which the Seamless Driver is about to draw. The modified XGA display driver intercepts this call and passes the rectangle coordinates to AccumulateScreenBounds.

Under DCAF, during PM display driver initialization, Seamless Windows must be granted addressability to all data and code that it will access during the call to SeamlessExcludeCursor. In order to add the supplied rectangle to all active SCAs, which can reside anywhere in the display driver heap, there is a single, static SSA (Seamless Screen Area called scaSeamless) used for this purpose. All Seamless bounding rectangles will be accumulated in scaSeamless; then, this SCA is merged with the other active SCAs when a query is issued via GreGetScreenChangeArea.

As a result, at initialization (in InitializeSeamless), the Seamless Windows driver is given access to AccumulateScreenBounds, to scaSeamless and to the DDT display driver control block, in order to retrieve the screen dimension at seamless time. The addresses of this data are stored in the SeamlessAddresses control block, owned by the Windows driver.

Before writing to the PM screen, the Seamless Windows driver will call SeamlessExcludeCursor. The exclusion rectangle will be passed in the following registers in so-called AI coordinates (i.e., 16-bit inclusive; 0, 0 is top left of screen):
 * Left cx
 * Top dx
 * Right si
 * Bottom di

The display driver will determine whether the new bounds accumulation is needed by checking the value of the pStartSCA pointer and, in SeamlessExcludeCursor32, will call AccumulateSeamlessBounds to initiate the bounds accumulation. AccumulateSeamlessBounds converts the passed rectangle to EXCLUSIVE SCREEN coordinates, clips the rectangle to the screen dimensions and calls AccumulateScreenBounds. This causes the rectangle supplied to SeamlessExcludeCursor to be added to only scaSeamless. When an SCA is queried using GreGetScreenChangeArea, the Seamless SCA is merged with the active SCAs and then reset to NULL.

Seamless Design Issues
There are multiple design issues in the seamless environment. The critical issues (those generating the most questions), are documented in the following sections:
 * Determination of code and data to be shared between the PM and Windows display drivers
 * Coordination of off-screen video RAM
 * Save/restore processing of the video hardware registers/environment
 * Implementation of semaphore and heartbeat processing
 * Conversion of Windows cursors and icons in the PM environment
 * Palette handling
 * Sharing of cursor exclusion and Enable/Disable code, including problems to be avoided
 * Setup and sharing of a pointer to video RAM.

Shared Data and Code between PM Drivers and Windows Drivers
Each design of seamless PM and Windows driver pairs requires its own shared data/code structure, because each driver implementation and its underlying video hardware are unique. The contents of the structure varies across implementations. The shared data/code structure for seamless XGA is included in the following example: PMDD_IO_STRUC struc LevelID                dd  ? ; 4 bytes to sync interface to PMDD.

; The following variables are in the PM DD's default data segment. ulMemRegBase         dd  ? ; 16:16 addr of memory mapped XGA regs usIORegBase          dw  ? ; Base of IO XGA regs ulVramBase           dd  ? ; 32-bit physical memory addr of VRAM ulVramSize           dd  ? ; Size of VRAM in bytes bfUseExternalPolling db  ? ; TRUE if external polling is supported

usScreenWidthMM      dw  ? usScreenHeightMM     dw  ? usScreenWidthPels    dw  ? usScreenHeightPels   dw  ? bBitsPerPel          db  ?

pSemaphore           dd  ? ; Flat pointer to FSRamSemaphore pHeartBeat           dd  ? ; Flat pointer to heartbeat counter

pNumDefaultColors    dd  ? ; Flat ptr to # of default colors in use

pCursorStatus        dd  ? ; Byte ptr: bit 0x80 set if sfw cursor pExcludeCursor       dd  ? ; 16:16 addr of exclude cursor function pDisableCursor       dd  ? ; 16:16 addr of disable cursor function pEnableCursor        dd  ? ; 16:16 addr of enable cursor function

WindowsPrivateArea   db WINDOWS_PRIVATE_AREA_SIZE dup (?) ;-- ; Information shared among the XGA Windows' drivers. ; Multiple Windows drivers can exist, running in multiple DOS sessions. ; SecondaryFontStart    dd  ? ; SecondaryFontEnd      dd  ? ; ; SystemFontTable        dd  256 dup (?) ; SecondaryFontTable    dd  256 dup (?) ; ; SystemFontHeight       dw  ? ; SystemFontPoints      dw  ? ; SystemFontWidth       dw  ? ; SystemFontCached      db  0 ; ; SecondaryFontName      db  0 ;                       db  32 dup (?) ; Max facename length=32. ; ; SecondaryFontNameLen   dw  1 ; SecondaryFontHeight   dw  0 ; SecondaryFontPoints   dw  0 ; SecondaryFontWidth    dw  0 ; ; SecondaryFontHdr       db  32 dup(0) ; SystemFontHdr         dw  16 dup(0) ; PaddedField           db  32 dup(0) ; Remaining reserved. ;-- ; Total 2200 bytes reserved by the PM driver for Windows' drivers.

bfPaletteIsFixed     db  ;  TRUE if palette manager not enabled ulLastPalUpdate      dd  ;  Timestamp of last foreground realize pHWPalette           dd  ;  16:16 ptr to HWPalette structure PMKWSPhysicalBase    dw  ;  Physical addr of 64KB kernel workspace for ;  bus-mastering into/out of system memory PMKWSLinearBAse      dd  ;  Linear addr of 64KB workspace

PMDD_IO_STRUC ends The initial entry in this shared structure must be a driver signature. For XGA, the signature is XG01. For VGA, the signature is VG01. This field must be checked by the Windows driver at initialization. If an incorrect entry is found, the Windows driver must report initialization failure to the GDI. This mechanism prohibits the running of non-compatible seamless driver pairs (for example, using a VGA seamless windows driver with a Presentation Manager XGA driver).

Coordination of Off-Screen Video RAM
A static division of off-screen video memory is typically employed to permit access to this resource for both the Presentation Manager and seamless windows drivers. Alternatively, dynamic allocation or total dedication of the memory to one driver is used. However, these alternatives could decrease performance.

At a minimum, both the PM and seamless windows driver's character caches are maintained in off-screen video memory for the sake of performance.

Save/Restore Processing of Video Registers/Hardware
A straightforward approach to save/restore processing is to force both the PM and seamless windows drivers to explicitly set video hardware registers anytime the registers are required. Since the video FSRamSemaphore must be used to coordinate hardware access, this semaphore can be obtained, the necessary register(s) set and used, and the semaphore released with no problem.

An alternative approach is to design the necessary save/restore processing and mark the associated registers and resources with handles (such as SessionIDs).

Implementation of Semaphore Processing
Most 16-bit presentation drivers use the FSRamSemaphore mechanism to serialize output. This same mechanism is used by the PM and seamless windows drivers in the 32-bit environment. FSRamSemaphores use the driver's data area to store the semaphore. Therefore, the Presentation Manager driver must establish the semaphore and provide its address to the seamless windows driver in the shared data/code structure. This logic flow is true for both the 16-bit and 32-bit environments.

In the 32-bit environment, there are new FSRamSemaphore entry points, and 16-bit drivers now have alternative entry points. The new entry points have been optimized for efficiency. The following is a list of both the new and alternative semaphore entry points: The semaphore entry points all have one parameter-a pointer to a FSRamSemaphore structure. The structure contains the following fields:
 * Note: The usClient field is the only field that, for normal processing, the presentation driver should access directly. This field is initialized as 0 when the semaphore is first acquired and thereafter is controlled by the driver. The intention is to give a DC some way of flagging a semaphore to notify the driver's ExitList processing routine of any special action it needs to take when releasing the semaphores.

The RAMSEMREQUESTxx functions return 0 if successful, and a nonzero error code otherwise.

The RAMSEMCLEARxx functions have no return information, and are void functions.

To support FSRamSemaphores, 32-bit Presentation Manager drivers must link with the OS2386 library, and 16-bit drivers must link with the OS2286 library.

To use FSRamSemaphores for coordinating hardware access, the seamless windows drivers must implement request_semaphore and clear_semaphore code logic. The purpose of request_semaphore is to own the semaphore; clear_semaphore relinquishes ownership.

When requesting the semaphore (if it is already owned by the current process), the semaphore's usage count must be incremented. If the semaphore is available, it is marked as owned by the current process. However, if the semaphore is owned by a different process, a call is made (indirectly) to the OS/2 kernel to block the thread until the semaphore is released. The mechanism for calling the OS/2 block_semaphore function is to issue an INT 66h to VWIN with some parameters passed through registers. VWIN then calls OS/2 block_semaphore on behalf of the seamless windows driver to wait on the specified FSRamSemaphore. The following code section illustrates how to acquire the FSRamSemaphore: mov    ax, _DATA mov    ds, ax     assumes ds, Data


 * Get our process ID and thread ID
 * Get our process ID and thread ID

mov    cx, init_struct.thread_id       ; Get the current thread ID      mov     dx, init_struct.proc_id         ;  and current process


 * Get addressability to the hardware semaphore structure
 * Get addressability to the hardware semaphore structure

mov    di, PMDD_IO_SEL                 ; PM selector mov    fs, di     mov     edi, init_struct.pmdd_io        ; FS:EDI -> pmdd_io struct mov    edi, fs:[edi].pSemaphore        ; FS:EDI -> pSemaphore

sem_retry: mov    ax, word ptr fs:[edi].fs_ProcID ; Touch the page cli mov    ax, word ptr fs:[edi].fs_ProcID ; Is the semaphore owned? or     ax, ax     jnz     SHORT sem_owned                 ; Yes, see if it is caller?


 * Here, if the semaphore is currently unowned, marked as owned by the
 * current process ID and thread ID; set the usage count to 1 and mark
 * the RAM semaphore as SET.
 * the RAM semaphore as SET.

mov    WORD PTR fs:[edi].fs_ProcID, dx     mov     WORD PTR fs:[edi].fs_ThrdID, cx     mov     ax, 1 mov    fs:[edi].fs_Usage, ax             ; Set usage count to 1. mov    fs:[edi].fs_RAMSem.RamSemOwner,al ; Set RAM semaphore. sti jmp    SHORT request_semaphore_exit

sem_owned: cmp    ax, dx                            ; Is the semaphore owned by caller? ; Essentially, pidOwner == pidCaller? jne    SHORT sem_owned2                  ; No, go wait.
 * Here, if the semaphore is currently owned. Check to see if current
 * owner is the caller.
 * owner is the caller.


 * If the semaphore is already owned by the current process ID and
 * thread ID, then nothing to do but increment the usage count.
 * thread ID, then nothing to do but increment the usage count.

inc    fs:[edi].fs_Usage sti jmp    SHORT request_semaphore_exit


 * If the semaphore is already owned by some other process ID and
 * thread ID, then perform an INT 66h to request the VDD (VWIN)
 * to call the CPDOS RAM Semaphore mechanism on our behalf. When
 * it returns, jump back to the beginning of this function to try
 * to get the semaphore again.
 * to get the semaphore again.

sem_owned2: sti


 * Block this thread until the semaphore is available.
 * Block this thread until the semaphore is available.

xor    ax, ax     mov     ah, VWIN_SEM_BLOCK                ; Block for semaphore function
 * AH = 82H ( Function Code for VWIN )

mov    ecx, fs:[edi].fs_Timeout
 * ECX -> Semaphore timeout

;Thunk the high word of the 32-bit offset in esi into 16:16 addr in ebx ;ie, multiply high word by 8 and set the LS 3 bits on. lea    esi, fs:[edi].fs_RAMSem shld   ebx, esi, 19 or     bx, 7 shl    ebx, 16 mov    bx, si
 * EBX -> CPDOS RAM Semaphore

mov    si, OFFSET vwin_id
 * Set DS:SI to "VWIN"

pushf call    DWORD PTR init_struct.pfvwin
 * Block on the semaphore
 * int    66h

jmp    sem_retry                         ; Go back and try to get it

request_semaphore_exit: mov    edi, Heartbeat inc    DWORD PTR fs:[edi]                ; Increment the heartbeat counter ..... When clearing the semaphore, the seamless windows driver should decrement the FSRamSemaphore's usage count. When this usage count reaches 0 (after decrementing), the semaphore's RamSemFlag field is saved, and then the entire semaphore structure is set as not owned. The saved RamSemFlag is checked (TRUE or FALSE) to determine whether any processes that are being blocked by the semaphore need to be unblocked. To unblock a requesting process, an INT 66h is issued to VWIN with the parameters passed in the appropriate registers. VWIN then invokes the OS/2 kernel wake_up function. The following code fragment shows how to clear the FSRamSemaphore: mov    ax, _DATA mov    ds, ax     assumes ds, Data


 * Get our process ID and thread ID
 * Get our process ID and thread ID

mov    cx, init_struct.thread_id          ; Get current thread ID     mov     dx, init_struct.proc_id            ; and current process ID


 * Get addressability to the Hardware Semaphore structure
 * Get addressability to the Hardware Semaphore structure

mov    di, PMDD_IO_SEL                    ; PM selector mov    fs, di     mov     edi, Init_struct.pmdd_io           ; FS:DI -> pmdd_io struct mov    edi, fs:[edi].pSemaphore           ; FS:DI -> pSemaphore

mov    ax, fs:[edi].fs_ProcID             ; Touch the page cli


 * See if semaphore is owned by calling process/thread
 * See if semaphore is owned by calling process/thread

cmp    fs:[edi].fs_Usage, 0               ; See if owned je     SHORT sem_leavebad                 ; No, declare an error

mov    ax, fs:[edi].fs_ProcID cmp    dx, ax                             ; pidOwner == pidCaller? jne    SHORT sem_leavebad                 ; No, declare an error jmp    SHORT sem_leaveok                  ; Yes, proceed

sem_leavebad:

sti jmp    SHORT exit_clear_semaphore

sem_leaveok:


 * Decrement the usage count
 * Decrement the usage count

dec    fs:[edi].fs_Usage                  ; Decrement the usage count jz     SHORT release_sem sti jmp    exit_clear_semaphore


 * Release the semaphore by setting the process ID and thread ID
 * fields to 0, clearing the RAM semaphore and remembering if any
 * other threads were waiting on the RAM semaphore. If not, re-enable
 * interrupts and return.
 * interrupts and return.

release_sem:

xor    dx, dx     mov     word ptr fs:[edi].fs_ProcID, dx     ; Clear pid mov    word ptr fs:[edi].fs_ThrdID, dx     ; Clear tid mov    fs:[edi].fs_Client, dx              ; Clear client mov    fs:[edi].fs_RAMSem.RamSemOwner, dl  ; Clear ramsemowner xor    cx, cx                              ; Clear the cx register xchg   fs:[edi].fs_RAMSem.RamSemFlag, cl   ; and set it to the sem flag or     cx, cx                              ; Is the sem being requested? jnz    SHORT sem_waiting                   ; Yes, call wakeup

sti jmp    SHORT exit_clear_semaphore


 * Some other process/thread is waiting for this semaphore. Perform an
 * INT 66h with the appropriate function, which will call the CPDOS
 * wakeup code (done through the VDD) to unblock all of the waiting
 * threads. Non-deterministic as to which one will actually get the
 * semaphore first. The others will have to wait again.
 * semaphore first. The others will have to wait again.

sem_waiting: sti


 * Issue the INT 66h call to wake up any threads that are blocked on
 * the semaphore.
 * the semaphore.

xor    ax, ax     mov     ah, VWIN_SEM_WAKE                   ; Wake function
 * AH = 83H ( Function Code for VWIN )

mov    si, OFFSET vwin_id
 * Set DI:SI to "VWIN"

; Thunk the high word of the 32-bit offset in edit into 16:16 addr in ebx ; ie, multiply the high-word by 8 and set the LS 3 bits ON. lea    edi, fs:[edi].fs_RAMSem shld   ebx, edi, 19 or     bx, 7 shl    ebx, 16 mov    bx, di
 * EBX -> CPDOS RAM Semaphore

pushf call    DWORD PTR init_struct.pfvwin
 * Block on the semaphore. CX should contain the old RamSemFlag value.
 * int      66H

exit _clear _semaphore : ..... At times, the PM driver must free the video semaphore to continue processing. This might be required in the case of a DOS session failure when the seamless windows driver is the semaphore owner. Sample semaphore release processing is included in the following code fragment. It is important to note that, in freeing the semaphore, the PM driver's process ID and thread ID (PID/TID) must be placed in the semaphore structure. VOID SeamlessForceSemRelease(VOID) {    PTIB pThreadInfo; PPIB pProcessInfo;

/***********************************************************/    /* Set the semaphore owner to be us. The changing of the */ /* semaphore owner must be atomic to avoid letting in any */ /* other threads. */    /***********************************************************/      DosGetInfoBlocks(&pThreadInfo,                       &pProcessInfo); // FSRDriverSem.ProcID = (USHORT) pProcessInfo->pib_ulpid; // FSRDriverSem.ThrdID = (USHORT) pThreadInfo->tib_ptib2->tib2_ultid;

*((PULONG)&FSRDriverSem.ProcID) = ((pThreadInfo->tib_ptib2->tib2_ultid) << 16) | (pProcessInfo->pib_ulpid);

/****************************************************************/ /* Free the semaphore. */ /****************************************************************/     while (FSRDriverSem.Usage != 0) { ReleaseDriverSemaphore; } }

Implementation of Heartbeat Processing
For seamless windows drivers, the only heartbeat processing required is to increment the heartbeat counter in the PM and Windows shared data/code structure. This counter must be incremented after obtaining the video hardware semaphore and prior to accessing the hardware.

The counter is checked by the PM driver as part of its semaphore timeout logic. Upon a second timeout, if the semaphore owner is unchanged and the heartbeat counter also is unchanged, the Presentation Manager driver will assume that the DOS session is hung and try to force release of the video semaphore. The following code sequence demonstrates this processing logic: /********************************************************************/ /* This function carries out PM Heartbeat Processing. */ /* It detects when the Windows driver has stopped while             */ /* holding the semaphore and tidies up if need be. */ /********************************************************************/  VOID SeamlessHeartbeat(VOID) {   HVDD  hSeamlessVDD; ULONG ulReturnCode;

/******************************************************************/   /* If seamless windows is not enabled, we do nothing. */   /******************************************************************/    if (fSeamlessEnabled) {       /**************************************************************/        /* See if we are timing out for the second time with the same */ /* PID, TID, and heartbeat. */       /**************************************************************/        if (   (usHeartBeatPID == FSRDriverSem.ProcID)            && (usHeartBeatTID == FSRDriverSem.ThrdID)            && (ulHeartBeat == 0)) {           /**********************************************************/            /* This is the second time we have timed out with the     */ /* same PID and TID owning the semaphore and the heart   */ /* beat has not changed. We must call VWIN to see if a  */ /* clean up is required. */           /**********************************************************/            /**********************************************************/            /* Open the seamless VDD ("VWIN"). */           /**********************************************************/            ulReturnCode = DosOpenVDD("VWIN", &hSeamlessVDD);

if (ulReturnCode != 0) {               /******************************************************/                /* The DosOpenVDD call encountered an error of some   */ /* sort! */               /******************************************************/                LogDosError(ulReturnCode); return; }

/**********************************************************/           /* Send VWIN the timeout on semaphore signal. */           /**********************************************************/            ulReturnCode = DosRequestVDD(hSeamlessVDD,                                NULL,                                VWIN_HEARTBEAT,                                FSRDriverSem.ProcID,                                NULL,                                FSRDriverSem.ThrdID,                                NULL);

if ((ulReturnCode == 0) || (ulReturnCode == 2)) {

/******************************************************/               /* The return code indicates that we should clean up. */               /******************************************************/                SeamlessForceSemRelease;

/******************************************************/               /* Clean up other heartbeat stuff. */               /******************************************************/                usHeartBeatPID = 0; usHeartBeatTID = 0; ulHeartBeat = 0; }

/**********************************************************/           /* Close the handle to VWIN. */           /**********************************************************/            DosCloseVDD(hSeamlessVDD); }       else {           /**********************************************************/            /* Just record the PID and TID of the semaphore owner and */ /* reset the heartbeat counter. */           /**********************************************************/            usHeartBeatPID = FSRDriverSem.ProcID; usHeartBeatTID = FSRDriverSem.ThrdID; ulHeartBeat = 0; }   }  }

Windows Cursors and Icons in the PM Environment
The Windows standard cursors (crossbar, arrow, etc.) have been converted to PM resources as part of the seamless VGA and XGA development. These resources are used, unchanged, by the PM driver. It is important to notice that when the cursor is over a seamless windows application, Windows cursors should appear, not Presentation Manager resources. The converted standard Windows cursors are provided as PM resource files.

Non-standard Windows cursors and icons are passed by the Win Shield component to the PM Shield and, ultimately, to the PM driver, for conversion when they are created by an application.

Palette Handling
Seamless palette management is achieved using the existing seamless components, hardware synchronization, and event-flow mechanisms. Currently, the Windows GDI component handles all palette issues, calling the Windows driver only to actually set the hardware registers. This processing must be modified for seamless palette management. to permit only palette realizations by PM, using the PM driver palette logic.


 * Note: PM Palette Management is actually a superset of Windows Palette Management in that PM must coordinate logical color table processing (default palette) as well as permit palette changes by way of the Palette Management interface. There is so much similarity in the PM and Windows palette application calls and processing that permitting PM to process a Windows palette realization is quite straightforward.

The Windows GDI has been modified to request palette entries and changes through normal OS/2 GPI/GRE processing. Also, the color mapping of the 20 Windows system colors must be supported in the PM driver palette. This requires a remapping of the PM palette, moving the 20 Windows colors to the top 10 slots and bottom 10 slots of the physical palette. Remapping the PM palette involves moving entries such that the closest entry to the Windows default color is at the specified location. Later, a translation table is used by PM when the driver selects a "closest fit" color, using its algorithms. The result of the algorithms is run through the translation table to determine the selected color's actual palette location. This has a negligible effect (fewer than 50 clock cycles) on performance.

The key item is that seamless palette management can exist because the Windows and PM palette APIs, and associated functionality are so similar. In both Windows and PM, an application creates, selects, and realizes a palette (in that order). In addition, there is a concept of palette animation in both Windows and PM. Animation permits a palette entry's color to be changed quickly, without affecting the rest of the palette or redrawing the image. Therefore, all Windows palette manipulation can be requested through normal GPI/PMWIN APIs, with the exception of animation. Animation is even simpler. It can be handled directly by the Windows GDI once PM has defined the animateable palette slot and returned this information to GDI.

The most straightforward way to describe the design of seamless palette management is to look at the processing flow:
 * 1.At PM seamless startup, a PM and Windows shared memory area is established and initialized. Several entries are added to this shared area for seamless palette management, as follows:
 * FixedPMPalette (Boolean, indicating a non-Palette Management PM Driver)
 * -If the PM Driver is not palette-aware, the GDI will operate, assuming a fixed palette. This assumption is accounted for in the GDI code, since Windows drivers exist for fixed palette hardware.
 * -For XGA and SVGA, because the drivers incorporate palette management, the flag is always FALSE.
 * TimeLastPalUpdate (timestamp of the last reorganization of the palette due to focus change and resultant realize request)
 * -When the focus changes and the application now in focus realizes, the entire palette is reshuffled. This invalidates the current GDI palette entries. The time stamp is a mechanism for the GDI to be instantly aware of this change.
 * Note: For simplicity, this could also be implemented as a counter.
 * PtrPhysicalPalette (Pointer to a shadow of the physical hardware palette)
 * 2.After Windows driver initialization, the GDI issues a Driver Control call requesting the address of the data described above. (A Control call is really the end result of an application device escape. The GDI is responsible for translating Escape calls to Windows Driver Control functions.) After this call, the GDI has addressability to all the necessary PM palette information.
 * The format of the Control function is:

rc = Control ( lpDevice, SEAMLESS_PAL_INIT, lpInDate, lpOutData)

where

lpDevice = a long pointer to a data structure of type PDEVICE, the destination device bit map

SEAMLESS_PAL_INIT = a word of value 6010 lpInData = NULL lpOut Data = a long pointer to a structure that contains:

bfPaletteIsfixed ulLastPalUpdate pHWPalette

(These are the palette-related entries in the PM/Windows shared    data/code area).
 * 3. In the course of normal seamless processing, if the GDI requires a hardware palette change, the GDI requests this change through PM's GPI/GRE, using the Windows and PM Shields. However, the GDI also issues a standard call to OEMSetPalette in the seamless windows driver. In this call, a null pointer is passed, instead of a pointer to a valid palette, to notify the seamless windows driver that a palette change has occurred through PM. In this case, the seamless windows driver updates only its private image copy of the palette, using the data pointed to by the 16:16 hardware palette address (pHWPalette) in the PM and Windows shared data/code area.
 * Note: The first 10 entries and last 10 entries of the palette in the shared data/code area are not exact matches to the static Windows** colors set forth by Microsoft**. They are the closest fit colors available in the PM default palette. Therefore, these 20 entries must never be modified in the seamless windows driver private image.
 * When the palette address pointer in OEMSetPalette is not NULL (that is, when a valid color table is passed), the seamless windows driver must update the video hardware palette as usual, as well as update its private image copy of the palette. This only occurs in seamless processing when a Windows application is animating the palette. It is important that the seamless windows driver first requests the video semaphore and then clears the semaphore after updating the hardware palette.
 * 4. The Win Shield and PM Shield are responsible for forwarding all RealizePalette requests to the Windows DOS sessions and Presentation Manager applications. These requests are received and processed as usual.
 * 5. On a palette realization for GDI, the PM Shield returns the logical-to- physical mapping of the requested color table to the DOS session's Windows GDI. This information is obtained from the PM driver using a GRE call that is hooked by the PM driver - GREQueryPaletteRealization. When the PM Shield calls this function and passes a device context and a pointer to an array of ULONGS, the PM driver returns the mapping from the logical color table to the hardware palette in the ULONG array. Seamless operation requires no special flags or different processing.
 * 6. There is one additional requirement in seamless Presentation Manager. In the Windows OEMUpdateColors function, the seamless windows driver must add the hardware serialization code to its processing logic before performing any video hardware access. The video FSRamSemaphore must be requested, and cleared upon exit of this routine. If this is not done, screen and font damage can occur.

As always, before requesting the video fSRamSemaphore, the seamless windows driver should check the hardware access state. If the driver is not enabled, the Windows driver should call Win Shield, requesting a repaint of the Windows Desktop.

Sharing Cursor Exclusion and Enable/Disable Code
Cursor exclusion or disable code is always required when software cursors are in use. The seamless windows driver must use the PM cursor routines. Even when hardware cursors are used, it is recommended that the PM cursor routines be called by the seamless windows driver. This enables the PM driver to accumulate bounding rectangles of modified areas of the screen to support applications such as remote help-desk screen access.

The example of a PM and Windows shared data/code area given previously for XGA demonstrates how cursor exclusion and enable/disable can be implemented.

That example includes the following entries in the shared structure: pCursorStatus       dd  ? ; Byte ptr: bit 0x80 set if sfw cursor pExcludeCursor      dd  ? ; 16:16 addr of exclude cursor function pDisableCursor      dd  ? ; 16:16 addr of disable cursor function pEnableCursor       dd  ? ; 16:16 addr of enable cursor function In this example, the seamless windows driver has the option of calling the PM cursor routines, but only when a software cursor is in use (by checking the CursorStatus byte). However, in the current XGA implementation, the seamless windows driver calls the PM routines whenever it requests the video FSRamSemaphore and the destination is the screen. (This enables the XGA PM driver to accumulate bounding rectangles of all screen windows.) The seamless windows driver calls the PM ExcludeCursor routine when a hardware cursor is in use, DisableCursor when a software cursor is in use, and EnableCursor whenever the video FSRamSemaphore is cleared and the destination is the screen. For XGA, the cursor exclusion rectangle coordinates are passed on the stack.

The PM cursor functions are accessed by a 16:16 address (entry point) that is exported by the PM driver. At this address is the PM thunk code and the call to the actual PM 32-bit cursor function. The thunk is not compiled but done by hand in the code itself. Sample code from the XGA PM driver is included in the following code fragment: ;--- ;  ; SeamlessExcludeCursor is the thunked entry point to eddm_ExcludeCursor. ; The exclusion rectangle is passed in the following registers in AI coords ; (that is, 0,0 is top left of screen). ; ;       left    top     right   bottom ;      cx      dx      si      di  ; ;---       align   4 _SeamlessExcludeCursor  proc far

; If we are not using a software cursor, we can go for a       ; fast exit. We know that the Windows driver always has ; fs set up to be the flat selector, so we use it here to       ; access our 32-bit data.

mov    eax, offset FLAT:_cursor_data.cd_cursor_status test   byte ptr fs:[eax], CURSOR_SOFTWARE jz     short SEC_exit

; Save ds and es on the 16-bit stack.

push   ds        push    es

; Get the FLAT selector into ds and es.

mov    ax, SEG FLAT:_DATA mov    ds, ax        mov     es, ax

; Before we start using esp and eip, we must disable interrupts. ; An interrupt can damage the top half of the registers ; because it thinks we are 16-bit. Touch all the pages we might ; access before disabling interrupts (a page fault is non-maskable       ; and will re-enable interrupts).

call   SeamlessTouchPages cli

; Save ss:sp (that is, 16-bit stack) in ebx.

mov    bx, ss        rol     ebx, 16 mov    bx, sp

; Set up the 32-bit stack.

mov    ax, SEG FLAT:_DATA mov    ss, ax        mov     esp, offset FLAT:_SeamlessStack+SEAMLESS_STACK_SIZE

; Now on 32-bit stack, ; save the 16-bit ss:sp on the 32-bit stack.

push   ebx

; Jump into the 32-bit code, which will call the 32-bit exclusion code.

jmp    far ptr FLAT:SeamlessExcludeCursor32

ret_from_exclude32: pop    eax mov    dx,ax rol    eax,16 mov    ss,ax movzx  esp,dx

; Now on 16-bit stack

sti

pop    es        pop     ds

SEC_exit: retf _SeamlessExcludeCursor endp
 * Note: Interrupts must be disabled before setting and using esp and eip. This is done to prevent entering 32-bit interrupt handling code when the current task, the DOS session, is known to be 16-bit. In this case, the ip and sp pointers would be set for a 16-bit environment, not 32-bit.

All code pages that might be required by the execution of the shared seamless functions must be present in memory before disabling interrupts. A page fault cannot be taken while in the seamless shared code.

Care must be taken that an STI instruction is not included in the PM cursor routines when executed by a DOS session. Interrupts must remain disabled (when executed by a DOS session) until returning through the thunk logic and being enabled.

Care also must be taken not to cause any ring transitions (from Ring 3) within the PM code executed from a DOS session. Therefore, there cannot be any operating system calls, etc. executed.

Set Up and Sharing of a Pointer to Video RAM
A shared pointer to video memory might be required in a seamless environment.

To establish the pointer in a 32-bit environment, the PM display driver calls PMDD.SYS (using DOSDevIOCtl) at initialization. The PM driver passes in the physical address of video RAM and its size, and gets back a Ring 2- or Ring 3-accessible linear address for the video memory. This address is then included and initialized in the seamless shared data/code area. VWIN handles the sharing requests necessary to access the pointer from the seamless windows driver.

To support the allocation of the global pointer to VRAM, the function AllocGlobalVRAM has been added to PMDD.SYS: AllocGlobalVRAM - (Function 0x7E) Called via: DosDevIOCtl( pData, pParms, 0x7E, 3, hDev )

where:

pData points to a ULONG in which the flat address to        VRAM will be returned. pParms points to a structure of the following type: struct _PARMS { ULONG PhysicalAddress; ULONG Length; ULONG Flags; } PARMS; where: PhysicalAddress is the beginning of the video RAM. Length is the length of video RAM in bytes. Flags is a field of flags of which only the least significant bit is currently defined. If the first bit is set, the pointer returned will be shareable and can be used to access VRAM at any privilege level. If the first bit is                        0, the pointer returned will be global and only accessible from Ring 0. All other bits are reserved and should be 0. (For the seamless environment, Flags should be 1.) hDev is the handle returned from a DosOpen call of the SINGLEQ$ device. Returns: 0 if successful, or a non-zero error code on failure. As mentioned above, all addressability to the video memory for the seamless windows drivers is handled by VWIN. However, if additional PM processes also require addressability, they must issue an AccessGlobalVRAM call to PMDD.SYS. (Please note that the process that allocates the pointer does not require this AccessGlobal call. The AllocGlobalVRAM function performs all necessary processing to allow the calling process' access of video memory.) AccessGlobalVRAM - (Function 0x7F) Called via: DosDevIOCtl( NULL, pParms, 0x7F, 3, hDev ) Where: pParms points to a structure of the following type: struct _PARMS { ULONG FlatAddress; ULONG Length; } PARMS; where: FlatAddress is a pointer that was returned from a previous call to                         AllocGlobalVRAM. Length is the length of Video Memory in                         bytes. hDev is the handle returned from a DosOpen call of the SINGLEQ$ device. Returns: 0 if successful, or a non-zero error code on failure.

PM Driver Calls to VWIN - Accessed via DOSRequestVDD
HVDD = Handle of VDD (returned from DosOpenVDD) SGID = NULL ULONG = 0x000000C0 ULONG = Number of array elements PVOID = Pointer to array of SM_PMDISP structures ULONG = 0 PVOID = NULL
 * Function = Pass initialization structure

typedef struct _SM_PMDISP {      ULONG  flOptions;/* BIT1 BIT0=xx ring level if BIT3=1  */ /* BIT2=0 for DATA   BIT2=1 for CODE */ /* BIT3=0 for 16:16  BIT3=1 for 0:32 */ /* BIT4=0 for mapping BIT4=1 for     */ /* passthru; if passthru, VWIN does  */ /* not touch                         */ /* BIT5=1 to force linear addresses  */ /* to match otherwise, 16:16 addrs   */ are remapped in the DOS session   */ /* BIT6 - BIT31 reserved (must be 0) */

ULONG ulLength; /* Length of the data in bytes        */

union           /* Pointer to the beginning address   */ /* of shared memory structure        */ {        PVOID  p16;    /* for 16:16 (BIT3=0)                 */ ULONG p32;    /* for  0:32 (BIT3=1)                 */ } Start;

} SM_PMDISP; HVDD = Handle of VDD (returned from DosOpenVDD) SGID = NULL ULONG = 0x000000C1 ULONG = PID to test/kill PVOID = NULL ULONG = TID to test/kill PVOID = NULL
 * Function = Signal timeout of the hardware semaphore

Returns 0 for "valid Seamless VDM PID/TID was killed" 1 for "valid PID/TID was not killed" (held by a PM driver) 2 for "non-valid PID/TID" (went away or is going away) HVDD = Handle of VDD (returned from DosOpenVDD) SGID = -1 indicates broadcast; the PM display driver always broadcasts ULONG = 0x000000C2 ULONG = Function code to use for the interrupt ( 0x4001           or 0x4002 ) PVOID = NULL ULONG = Force Repaint ( 0 = NO, 1 = YES ) PVOID = NULL Sends VWIN a DOS session Dirty notification. This is used at PM death and resurrection to indicate to the DOS sessions to suspend drawing or resume redrawing and repainting. HVDD = Handle of VDD (returned from DosOpenVDD) SGID = NULL ULONG = 0x000000C4 ULONG = sizeof(VMSSIZE structure) PVOID = pointer to VMSSIZE structure ULONG = NULL PVOID = NULL
 * Function = DOS session Dirty notification (Send)
 * Function = Set VMOUSE Display Resolution

typedef struct vmss_s { ULONG vmss_nb;             // Size of structure, in bytes (36) LONG  vmss_lMode;          // Video mode (eg, 00h-13h, or -1) ULONG vmss_ulWidth;        // Width of screen, in pels ULONG vmss_ulHeight;       // Height of screen, in pels ULONG vmss_ulCellWidth;    // Width of screen cells, in pels ULONG vmss_ulCellHeight;   // Height of screen cells, in pels ULONG vmss_ulPtrWidth;     // Width of ptr drawing size, pels ULONG vmss_ulPtrHeight;    // Height of ptr drawing size, pels ULONG vmss_ulPtrUnitWidth; // Width of ptr drawing unit, pels } VMSSIZE;

Windows Driver Calls to VWIN - Accessed via INT 66h
Input: AH   = 0x81 - VWIN_INITIALIZE DS:SI = "VWIN" CX   = SIZEOF windd_init EBX  = windd_init struc
 * Windows DD - Initialization

WINDD_INIT struc proc_id     dw  0   ; (out) WinDD's process id        thread_id    dw  0   ; (out) WinDD's thread id        pfvwin       dd  ? ; (out) VWIN's interrupt handler pmdd_io     dd  0   ; (out) pointer to pmdd_io structure WINDD_INIT ends
 * Note: After this initialization call, instead of issuing INT 66H, the seamless windows driver can call directly into VWIN's interrupt handler using the pfvwin function pointer, passed in the WINDD_INIT structure. If this mechanism is used, the seamless windows driver must first perform a "pushf" before calling VWIN's handler.

The pmdd_io field is a pointer passed directly from the PM display driver. This pointer addresses a structure of values that are shared between the PM and seamless windows display drivers. The definition of this structure is set by the display driver pair. However, as noted above, the first four bytes of the structure must contain some type of signature value agreed on by the display driver pair. Output: AX   = 0... success n... failure code defined by VWIN Input: AH   = 0x82 - VWIN_SEM_BLOCK EBX  = CPDOS RAM Semaphore ECX  = Timeout value DS:SI = "VWIN"
 * Windows DD - Block on FSRamSemaphore

Output: AX   = 0... success n... failure Note: Most of the driver semaphore handling is at Ring 3. However, this exception (interrupt) is necessary because the seamless windows driver DOS session cannot make a kernel call. When the video hardware semaphore is in use, the Driver must block, and this requires a call to the kernel through VWIN. Input: AH   = 0x83 - VWIN_SEM_WAKE EBX  = CPDOS RAM Semaphore CX   = old RamSemFlag value DS:SI = "VWIN" Note: When freeing and resetting the FSRamSemaphore, the seamless windows driver first checks whether RamSemFlag in the semaphore structure is 0. If the flag is nonzero, the seamless windows driver must call INT 66h, VWIN_ SEM_WAKE. The RamSemFlag value before being reset is the "old RamSemFlag value".
 * Windows DD - Wakeup FSRamSemaphore request

Output: AX   = 0. . . success (call cannot fail) Input: AH   = 0x84 - VWIN_REQUEST_VVIDEO DS:SI = "VWIN"
 * Windows DD - Request hardware

Output: AX   = 0. . . success (call cannot fail; call will block                 until hardware is available) Note: In seamless processing, three components share the video hardware. These are:
 * VVIDEO
 * PM
 * Windows

PM and Windows coordinate access to the hardware via a FSRamSemaphore. However, virtual video drivers do not have this mechanism available. A virtual driver is never sure when a DOS application is finished accessing the screen; therefore, this interrupt exists to signal to VWIN (which forwards the request to the virtual video driver) that the seamless windows driver needs hardware access. Before making the call, the driver must obtain the hardware semaphore. Upon return, the seamless windows driver is guaranteed exclusive use of the video. (This mechanism of obtaining the semaphore and checking with VVIDEO already exists in the PM Driver code and is not unique to seamless operation.)

Input: AH   = 0x85 - VWIN_NOTIFY_VVIDEO DS:SI = "VWIN"
 * Windows DD - Notify that hardware is available.

Output: AX   = 0. . . success (call cannot fail) Input: AH   = 0x86 - VWIN_WINDD_ENTERCRITSEC DS:SI = "VWIN"
 * Windows DD - Critical section processing

Output: AX   = 0. . . success (call cannot fail)

Input: AH   = 0x87 - VWIN_WINDD_EXITCRITSEC DS:SI = "VWIN"

Output: AX   = 0. . . success (call cannot fail) Note: The enter/exit critical section calls can be nested and, therefore, must come in pairs. This interrupt usually is issued as part of INT 2Fh processing, when nonreentrant code is being executed. It requests VWIN to temporarily stop sending interrupts to the DOS session.

Device Escapes from Win Shield and GDI to Seamless Windows Driver
hDC      = GetDC( hWnd ) nEscape  = 6000 - SEAMLESS_ESC_INIT nCount   = 0 lpInData = Pointer to Win Shield "call-back" entry point lpOutData = NULL
 * rc = Escape( hDC, SEAMLESS_ESC_INIT, nCount, lpInData, lpOutData )

On Exit: rc = negative......Error rc = positive......Success The SEAMLESS_ESC_INIT call is made by Win Shield at initialization time. On input, Win Shield provides the address of a call-back entry point. The seamless windows driver uses this entry point, in addition to passing a clip region, to cause an area of the Windows Desktop to be repainted. This function is used whenever the seamless windows driver is disabled but must write to the screen. Win Shield is responsible for accumulating the clip regions passed to it.

Note:
 * 1) This call is made before the screen is enabled by the Win Shield via INT 2Fh.
 * 2) The seamless driver must always invalidate the Windows Desktop when OEMUpdateColorsis called and the driver is in the disabled state. Miscellaneous translation table and color-mapping problems can otherwise occur.

hDC      = GetDC( hWnd ) nEscape  = 6002 - SEAMLESS_ESC_ENABLE nCount   = 0 lpInData = NULL lpOutData = Pointer to hw_access
 * rc = Escape( hDC, SEAMLESS_ESC_ENABLE, nCount, lpInData, lpOutData )

On Exit: rc = negative......Error rc = positive......Success SEAMLESS_ESC_ENABLE permits a Windows program to determine the current hardware state. The escape returns the address of the driver's internal variable, hw_access, which reflects the hardware state at any time. hDC      = GetDC( hWnd ) nEscape  = 6001 - SEAMLESS_ESC_EXIT nCount   = 0 lpInData = NULL lpOutData = NULL
 * rc = Escape( hDC, SEAMLESS_ESC_EXIT, nCount, lpInData, lpOutData )

On Exit: rc = negative......Error rc = positive......Success Win Shield issues this call, requesting the seamless windows driver to reset its Win Shield call-back entry point. This is performed when the seamless windows DOS session is closing.

hDC      = GetDC( hWnd ) nEscape  = 6004 - SEAMLESS_ESC_REQSEM nCount   = 0 lpInData = NULL lpOutData = NULL
 * rc = Escape( hDC, SEAMLESS_ESC_REQSEM, nCount, lpInData, lpOutData )

On Exit: rc = negative......Error rc = positive......Success SEAMLESS_ESC_REQSEM requests the seamless windows driver to obtain the FSRamSemaphore on behalf of Win Shield.

hDC      = GetDC( hWnd ) nEscape  = 6005 - SEAMLESS_ESC_CLRSEM nCount   = 0 lpInData = NULL lpOutData = NULL
 * rc = Escape( hDC, SEAMLESS_ESC_CLRSEM, nCount, lpInData, lpOutData )

On Exit: rc = negative......Error rc = positive......Success SEAMLESS_ESC_CLRSEM requests the seamless windows driver to clear the FSRamSemaphore on behalf of Win Shield.

hDC      = GetDC( hWnd ) nEscape  = 6010 - SEAMLESS_ESC_PALINIT nCount   = 0 lpInData = NULL lpOutData = Pointer to the start of the palette-related data in the PM and Windows shared data/code area
 * rc = Escape( hDC, SEAMLESS_ESC_PALINIT, nCount, lpInData, lpOutData )

On Exit: rc = negative......Error rc = positive......Success SEAMLESS_ESCAPE_PALINIT permits the Windows GDI component to obtain the address of the palette-related data in the PM and Windows shared data/code area. There are three palette-related entries in this shared area, arranged in the following sequence: bfPaletteIsFixed  db  ? ; TRUE if Palette Manager not enabled ulLastPalUpdate   dd  ? ; Timestamp of last foreground realize pHWPalette        dd  ? ; 16:16 ptr to HWPalette structure

Windows INT 2Fh Support
AX   = 4001H CX   = Seamless identifier "SM" DX   = Seamless sub-function
 * Windows DD - Notify Background Switch

0100H....disable hardware Write access 0101H....disable hardware Read/Write access AX   = 4002H CX   = Seamless identifier "SM" DX   = Seamless sub-function
 * Windows DD - Notify Foreground Switch

0200H....enable hardware Write access 0201H....enable hardware Read/Write access 0202H....enable hardware Read/Write access and force repaint

Miscellaneous Windows Driver Information
In addition to the enabled and disabled states, the Windows driver also can be in a Read-only state, for example, when the Windows driver is disabled, yet can legitimately transfer bit-map data from video memory to system memory.

These various states are normally used to optimize execution speed, based on the needs of the calling application.

When processing INT 2Fh/disable from the PM Display Driver, it is important for the Windows driver to remain disabled until notified (again, via INT 2Fh) by the PM driver. The PM driver is notifying of its death and resurrection. Alternately, if enabled by the PM driver but otherwise INT 2Fh disabled, then the Windows driver can optimize processing and enter the read-only state.

The seamless windows driver must always check the hardware state before requesting the video FSRamSemaphore. If the hardware is disabled or in the "read-only" state, and the output destination is the screen, the driver should call Win Shield, requesting Win Shield to accumulate the screen areas (rectangles) requiring update.

There are two options when calling Win Shield for a repaint: SEAMLESS_FORCEPAINT To cause the entire Windows desktop to be repainted. SEAMLESS_POSTPAINT  To repaint the accumulated area collected by Win Shield. Normally, when a Windows session is switched to the background, its output is disabled. Then, when switched to the foreground, a repaint is forced, even if no modifications were made to the current screen. This is acceptable when running a full-screen Windows session. However, when running seamlessly, the PM Driver automatically saves the contents of the screen when it is switched to the background.

When Presentation Manager returns to foreground, the whole screen is restored (including the seamless windows). As a result, no refreshes from the seamless windows will be necessary unless the Windows program attempted a repaint while output was disabled. As a performance improvement, a repaint flag is implemented in Win Shield, which is set initially to FALSE. When the seamless windows driver is in the background, this flag is set by Win Shield to TRUE if any refresh of the screen is attempted by the driver (for example, if the driver calls-back to the Win Shield, passing a clip region). When the seamless driver is switched to the foreground, a repaint is forced only if the flag is TRUE.

Errors have been observed in seamless windows due to the Windows driver's failure to account for being in the Disabled state. For example, damage to the Min/Max buttons for a seamless window was observed. This occurred because the Min/Max button conversion was performed in the DIBtoScreen function. In some scenarios, when this function was invoked, the seamless windows driver was disabled. In this case, the Windows driver could not process the DIBtoScreen and ignored it. However, when finally enabled, the driver was told only to do a block logical transfer (Blt) and not a DIBtoScreen. The solution was for the seamless windows driver to store a flag indicating that the button conversion had not been performed, and to process it at a later time, when enabled.

The SaveScreenBitmap entry in the seamless windows driver should be disabled because the driver does not have full information of the Desktop when running seamlessly.

The seamless windows driver must not issue any INT 10h calls.

Previous seamless windows design assumed that the FS and GS registers were not modified once loaded. Under Windows 3.1 this is no longer a valid assumption. The seamless windows driver must save the FS and GS contents to global variables when FS and GS are initially loaded. Then, before these registers are used, the seamless driver should compare the contents to the global variables. If the contents have been modified, the registers must be reloaded.

It is important to modify the virtual video device driver, as well as the PM and Windows drivers, to operate seamlessly. The virtual video driver, which normally traps all hardware I/O, must be modified to permit the seamless windows driver to access the hardware. If the virtual video changes are not complete, you will see the following error message box: Error Message

System does not support this session's video mode in  a window. The program cannot continue running in a  window.

Seamless Testing
The essence of seamless windows testing is to run both OS/2 Presentation Manager and Windows applications together on the OS/2 Desktop. The tests should execute as many different Windows functions and options as possible, to stress the seamless drivers and environment.

The following situations and scenarios should be considered for seamless testing:
 * Execute standard Presentation Manager and Windows functionality - move, resize, minimize, maximize and overlay windows of both PM and Windows applications. When minimizing, minimize to the OS/2 Desktop, to the Minimized Window Viewer and hide the applications. Open and close several PM and Windows applications.
 * When closing applications, exercise various mechanisms (close from the Window List, double click in the window's upper left corner, etc.). For the Windows applications, test within the same DOS session and in different DOS sessions.
 * Use the Window List, mouse pointer and hot-key sequences (for example, Alt+Esc) to change window focus. Include full-screen, and DOS and OS/2 windowed sessions in some test scenarios.
 * Verify application functionality - pull-down and pop-up menus, control keys, radio buttons, and so on. Are text font and positioning correct? Are valid colors displayed?
 * Actual Windows and PM applications should be executed to exercise seamless modifications.
 * A verification of Windows driver functionality can be obtained using the driver test routines from the Microsoft Windows Device Driver Kit. These tests should execute seamlessly.
 * Drag both PM and Windows icons and move the cursor over the application windows. Test both hardware and software cursors if available. Exercise various bit map formats - device dependent (DDB), device independent (DIB) and run length encoded (RLE).
 * Transfer data between Presentation Manager and Windows applications using the Clipboard and DDE.
 * Exercise all supported hardware resolutions and bits per pels. A different set of problems may be detected at different resolutions and numbers of colors.

Three possible test scenarios are outlined below:
 * Scenario A (Initial seamless verification):
 * Two passes:
 * -Pass One uses a single instance of Windows Clock on the OS/2 Desktop
 * -Pass Two has multiple instances
 * Active Sessions:
 * -Windows Clock
 * Move, resize, attempt both analog and digital display, overlay OS/2 folder icon view, clip by icon view, minimize, maximize.
 * Scenario B:
 * Two passes:
 * -Pass One with one DOS session for both Windows applications
 * -Pass Two uses separate DOS sessions.
 * Exercise the outlined functionality by performing some tasks within an application, then change focus to the next application and continue to test, ultimately returning to the first application.
 * Active Sessions:
 * -PM - Jigsaw
 * Minimize, maximize, move, resize, overlay Windows applications, clip by Windows applications, play the game.
 * -Windows - Excel
 * Change fonts, copy data to Clipboard, resize, display chart, minimize, maximize, move, close, cut/paste, overlay PM and Windows applications, clipped by Presentation Manager and Windows applications, change height and width of cells, change colors in cells.
 * -Windows - Paintbrush
 * Select color, draw, fill, cut/paste, rotate, edit palette, view bit map, minimize, maximize, move, resize, clipped by and overlay other windows.
 * Scenario C:
 * This scenario includes multiple focus changes between the various windows, and exercises hardware palette updates and switching to/from a full-screen session.
 * The functionality discussed (minimize, maximize, resize, clipping, etc.) should be exercised while changing focus and performing the session switches.
 * Active Sessions:
 * -PM - PalDisp
 * Display hardware palette for verification.
 * -PM - PalTest
 * Display bit map using PM Palette Management, and verify palette update.
 * -PM - Solitaire
 * Continuous play.
 * -Windows - ShowDIB
 * Display bit map using seamless windows Palette Management, and verify palette update.
 * -Windows - Clock
 * Analog and digital display.
 * -DOS Window - batch file in loop, text mode
 * Run batch file printing text messages, coded in an endless loop.
 * -DOS Full-Screen - graphics application
 * Run graphics application program.

Checklist for Palette Management Support
To enable current Windows driver palette management support in a seamless display driver, follow the steps below:
 * 1.Make sure that the following structure is part of the PM-SL share data, that is, the PMDD_IO_STRUC structure defined in IPC.INC.

SL_PaletteData struc bfPaletteIsFixed    db  ? ;TRUE if palette manager not enabled. ulLastPalUpdate     dd  ? ;Time stamp of last foreground realize. pHWPalette          dd  ? ;16-bit pointer to HWPalette structure. SL_PaletteData ends
 * 2.Add SEAMLESS_ESC_PALINIT function support in Windows entry Control ( lpDevice, Function, lpInData, lpOutData), do the following:
 * a.Claim the support of SEAMLESS_ESC_PALINIT when this function is being queried.
 * b.When Function = SEAMLESS_ESC_PALINIT, return the address of init_Struct- >pmdd_io.SL_PaletteData in the buffer whose address is in lpOutData.
 * This address, &(init_Struct->pmdd_io.SL_PaletteData), must be in 16-bit sel :offset format.
 * Note:
 * If init_Struct->pmdd_io is a 32-bit address of the PM-SL share area, you have to thunk the &(init_Struct->pmdd_io.SL_PaletteData) 32-bit address to 16-bit sel:off format, using the following algorithm:
 * If this function is not implemented, or not correctly implemented, a General Protection Fault will occur.
 * 3. Change Windows entry SetPalette as follows:
 * 3. Change Windows entry SetPalette as follows:

if( lpPalette != NULL ) { a) Request_Semaphore(..)    b) Update the hardware palette in exactly the same way as you do in        regular Windows driver. c) Clear_Semaphore(..)  } else {     Update the private copy of the hardware palette image if there is one being     kept in the data segment.   }
 * 4. Change Windows entry UpdateColors as follows:
 * Call check_hw_access for HW_FORCEREADWRITE hardware state before any screen update.
 * If check_hw_access returns Success, call request_semaphore as you do before all hardware access and clear_semaphore after you are done updating the screen.
 * 5. Change check_hw_access in IPC.ASM as follows:
 * Add HW_FORCEREADWRITE as one of the possible values in AL when this call is made. Here is an example of code needed to add to check_hw_access:

check_hw_access PROC FAR .....        cmp     al, HW_READWRITE        ;Request for HW_READWRITE access? jnz    check_forcereadwrite    ;No, go check for HW_FORCEREADWRITE ;access. .....  check_forcereadwrite: cmp    al, HW_FORCEREADWRITE   ;Request for HW_FORCEREADWRITE access? jnz    check_write_access      ;No, go check for HW_WRITEONLY access.

VWIN_ENTER_CRITSEC             ;Turn off interrupts from VWIN.

mov    hw_virgin, HW_DOREPAINT ;Repaint when HW_READWRITE allowed. mov    ax, SEAMLESS_FORCEPAINT ;Paint the whole VDM. call   winshield_callback      ;

VWIN_EXIT_CRITSEC              ;Turn on interrupts from VWIN.

jmp    check_exit_bad          ;We are done. Exit. ....  check_hw_access ENDP
 * 6. Remarks
 * Values for symbolic names used above are as follows:

SEAMLESS_ESC_PALINIT   equ  6010    ;Pass back address of 1st entry of the ;palette-related data in PM-SL share ;area.

HW_FORCEREADWRITE      equ   'F'    ;Request R/W hardware state. If not ;available, ask winshield to do force ;repaint when the driver is once again ;allowed to draw the screen.