/*
 *      Copyright (C) 1997-2006 Andrei Los.
 *      This file is part of the lSwitcher source package.
 *      lSwitcher is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the lSwitcher main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

#define INCL_DOSMEMMGR
#define INCL_DOSMODULEMGR
#define INCL_WIN

#include "lswitch.h"
#include "taskbar.h"
#include <string.h>
#include <stdio.h>

/*
   Since the hooks may be called in the context of any thead having a message
   queue, we must use shared memory to access the globals of lSwitcher daemon.
   For the same reason the shared mem must be freed upon exiting the hook procs,
   otherwise the caller will retain a reference to it and the shared mem object
   will not be destroyed when lSwitcher exits
   IMPORTANT !!! Don't use DosGetNamedSharedMed/DosFreeMem before it's REALLY
   needed, check all other conditions first. These functions turn out to be
   expensive performance-wise

   With version 2.6 this DLL, if compiled with Watcom C, does not use C RTL
   (since Watcom RTL does not support multiple processes attached to a DLL
   with a single data segment, which we need). Because of this stack overflow
   is not checked for, and we want to reduce stack usage to a minimum
*/

HWND hwndMenuUp = NULLHANDLE, hwndTskBar = NULLHANDLE, hwndPopup = NULLHANDLE;
BOOL bWidget;

APIRET ctrpDesktopWorkareaSupported(VOID);

APIRET doshQueryProcAddr(PCSZ pcszModuleName, ULONG ulOrdinal, PFN *ppfn);

//#ifndef XWORKPLACE
/*
 * WinSetDesktopWorkArea:
 *      undocumented Warp 4 API to change the "desktop work
 *      area", which, among other things, sets the size for
 *      maximized windows.
 *
 *      This is manually imported from PMMERGE.5468.
 */

BOOL APIENTRY WinSetDesktopWorkArea(HWND hwndDesktop,
                                    PRECTL pwrcWorkArea);
typedef BOOL APIENTRY WINSETDESKTOPWORKAREA(HWND hwndDesktop,
                                            PRECTL pwrcWorkArea);
typedef WINSETDESKTOPWORKAREA *PWINSETDESKTOPWORKAREA;

/*
 * WinQueryDesktopWorkArea:
 *      the reverse to the above.
 *
 *      This is manually imported from PMMERGE.5469.
 */

BOOL APIENTRY WinQueryDesktopWorkArea(HWND hwndDesktop,
                                      PRECTL pwrcWorkArea);
typedef BOOL APIENTRY WINQUERYDESKTOPWORKAREA(HWND hwndDesktop,
                                              PRECTL pwrcWorkArea);
typedef WINQUERYDESKTOPWORKAREA *PWINQUERYDESKTOPWORKAREA;

static PWINSETDESKTOPWORKAREA   G_WinSetDesktopWorkArea = (PWINSETDESKTOPWORKAREA)-1;
static PWINQUERYDESKTOPWORKAREA G_WinQueryDesktopWorkArea = (PWINQUERYDESKTOPWORKAREA)-1;

BOOL fWorkAreaSupported;
/**
 * Check to see if PMMerge supports reducing the desktop
 * Code adapted from xworkplace by Ulrich M”ller & Paul Ratcliffe
 **/
APIRET doshQueryProcAddr(PCSZ pcszModuleName,       // in: module name (e.g. "PMMERGE")
                         ULONG ulOrdinal,           // in: proc ordinal
                         PFN *ppfn)                 // out: proc address
{
    HMODULE hmod;
    APIRET  arc;
    if (!(arc = DosQueryModuleHandle((PSZ)pcszModuleName,
                                     &hmod)))
    {
        if ((arc = DosQueryProcAddr(hmod,
                                    ulOrdinal,
                                    NULL,
                                    ppfn)))
        {
            // the CP programming guide and reference says use
            // DosLoadModule if DosQueryProcAddr fails with this error
            if (arc == ERROR_INVALID_HANDLE)
            {
                if (!(arc = DosLoadModule(NULL,
                                          0,
                                          (PSZ)pcszModuleName,
                                          &hmod)))
                {
                    arc = DosQueryProcAddr(hmod,
                                           ulOrdinal,
                                           NULL,
                                           ppfn);
                }
            }
        }
    }

    return arc;
}
/**
 * Check to see if PMMerge supports reducing the desktop
 * Code adapted from xworkplace by Ulrich M”ller & Paul Ratcliffe
 **/
APIRET ctrpDesktopWorkareaSupported(VOID)
{
    APIRET  arc = ERROR_INVALID_ORDINAL;

    if ((ULONG)G_WinSetDesktopWorkArea == -1)
    {
        // first call:
        if (    (!(arc = doshQueryProcAddr("PMMERGE",
                                           5468,
                                           (PFN*)&G_WinSetDesktopWorkArea)))
             && (!(arc = doshQueryProcAddr("PMMERGE",
                                           5469,
                                           (PFN*)&G_WinQueryDesktopWorkArea)))
           )
        {
            //lstInit(&G_llWorkAreaViews, FALSE);
            fWorkAreaSupported = TRUE;
        }
    }

    if (arc)
    {
        G_WinSetDesktopWorkArea =   NULL;
        G_WinQueryDesktopWorkArea = NULL;
    }

    return arc;
}

//simple and dirty string to integer conversion
/*ULONG __atol(UCHAR * s)
{
    SHORT k;
    ULONG r, base;

    for (k = 0; s[k] != '\0'; k++) ;
    for (--k, base = 1, r = 0; k >= 0; k--, base *= 10)
	r += (s[k] - 48) * base;

    return r;
} */

BOOL IsWindowClass(HWND hwnd, UCHAR * pszClassName)
{
    static UCHAR ucBuf[32];	//reduce stack usage to minimum since we don't use stack probes
    USHORT k;
    BOOL bEqual;

    WinQueryClassName(hwnd, sizeof(ucBuf) - 1, ucBuf);
/* equivalent of strcmp, but we don't want to use the RTL */
    for (k = 0, bEqual = TRUE; ucBuf[k] != '\0' && pszClassName[k] != '\0';
	 k++)
	if (ucBuf[k] != pszClassName[k]) {
	    bEqual = FALSE;
	    break;
	}

    return (bEqual && ucBuf[k] == '\0' && pszClassName[k] == '\0');
}

BOOL IsDesktop(HWND hwnd)
{
    return (hwnd == WinQueryWindow(HWND_DESKTOP, QW_BOTTOM) &&
	    (IsWindowClass(hwnd, "wpFolder window")));
}

BOOL GetSharedMem(LSWDATA ** plswData)
{
    return
	(DosGetNamedSharedMem
	 ((PVOID *) plswData, SHAREMEM_NAME, PAG_READ | PAG_WRITE) == 0
	 && plswData != NULL);
}

VOID SetPrty(ULONG pid, USHORT usPrty)
{
    SHORT sPrtyC, sPrtyD;
    BOOL bDesc;

    bDesc = (usPrty & 0x8000);
    usPrty &= ~0x8000;
    sPrtyC = (usPrty + PRTYD_MAXIMUM) >> 8;
    sPrtyD = usPrty - (sPrtyC << 8);
    DosSetPriority(bDesc ? PRTYS_PROCESSTREE : PRTYS_PROCESS, sPrtyC, sPrtyD,
		   pid);
}

BOOL EXPENTRY lswInputHook(HAB hab, PQMSG pQmsg, ULONG fs)
{
    LSWDATA *plswData;
    POINTL ptl;
    static BOOL bOnTop = FALSE;
    static USHORT usOldY = 0xFFFF, cyScreen = 0, cyTskBar = 0;	// this and the previous lines need to be static
    static RECTL rcl;		//reduce stack usage to minimum since we don't use stack probes
    static HWND hwndBtn = 0;
    PPIB ppib;

    DosGetInfoBlocks(NULL, &ppib);
    if (ppib->pib_flstatus & PS_EXITLIST)
	return FALSE;

    if (cyScreen == 0 || pQmsg->msg == WM_SYSVALUECHANGED)
	cyScreen = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);

// get taskbar hwnd in case it changed
    if (pQmsg->msg == WM_NULL && pQmsg->hwnd == 0 && GetSharedMem(&plswData)) {
	hwndTskBar = plswData->hwndTaskBarClient;
	DosFreeMem(plswData);
    }

    if (pQmsg->msg == WM_MOUSEMOVE) {
	if (pQmsg->hwnd != hwndBtn) {
	    if (WinQueryWindow(pQmsg->hwnd, QW_PARENT) == hwndTskBar
		&& IsWindowClass(pQmsg->hwnd, "#3")) {
		hwndBtn = pQmsg->hwnd;
		WinPostMsg(hwndTskBar, LSWM_MOUSEOVERBTN, MPFROMHWND(hwndBtn),
			   0);
	    }
	    else if (hwndBtn != 0) {
		hwndBtn = 0;
		WinPostMsg(hwndTskBar, LSWM_MOUSEOVERBTN, MPFROMHWND(hwndBtn),
			   0);
	    }
	}

	if (!bWidget && hwndMenuUp == NULLHANDLE
	    && (WinQueryPointerPos(HWND_DESKTOP, &ptl), ptl.y != usOldY)) {
	    usOldY = ptl.y;
	    if ((!bOnTop && (ptl.y == 0 || ptl.y == cyScreen - 1)) ||
		(bOnTop && ptl.y > cyTskBar
		 && ptl.y < cyScreen - cyTskBar)) {

		if (!GetSharedMem(&plswData))
                    return FALSE;
                if ((!bOnTop
		     && plswData->Settings.bTaskBarTopScr ^ ptl.y == 0)
                    || (bOnTop && !WinIsWindowVisible(plswData->hwndMenu))) {
                    if (!bOnTop) {
                        WinQueryWindowRect(plswData->hwndTaskBar, &rcl);
                        cyTskBar = rcl.yTop;
                    }
                    WinSetWindowPos(plswData->hwndTaskBar,
                                    HWND_TOP, 0, 0, 0,
                                    0, SWP_ZORDER | SWP_SHOW);
                    bOnTop ^= TRUE;
                }
                DosFreeMem(plswData);
            }
            /*else {
                DebugHereIAm();
                WinPostMsg(hwndTskBar, LSWM_MOUSEOVERBTN, MPFROMHWND(hwndBtn), 0);
            } */
	}
    }
    else if (pQmsg->msg ==
	     WinFindAtom(WinQuerySystemAtomTable(), LSWM_GETPROCCMD)
	     && GetSharedMem(&plswData)) {
	UCHAR *ucHandle;
	USHORT usHandle = 0;

	strncpy(plswData->buf,
		&ppib->pib_pchcmd[strlen(ppib->pib_pchcmd) + 1],
		sizeof plswData->buf);
	if (DosScanEnv("WP_OBJHANDLE", &ucHandle) == 0 && ucHandle != NULL)
	    strncpy(&plswData->buf[strlen(plswData->buf) + 1], ucHandle,
		    sizeof plswData->buf - strlen(plswData->buf) - 2);

	DosFreeMem(plswData);
	WinPostMsg(hwndTskBar, LSWM_SWITCHLISTCHANGED, MPFROMLONG(4),
		   pQmsg->mp2);
	//don't understand why we need to pass this over, but there is some system instability otherwise
    }
    else if (pQmsg->msg ==
	     WinFindAtom(WinQuerySystemAtomTable(), LSWM_SETPRIORITY)) {
	SetPrty(LONGFROMMP(pQmsg->mp2), SHORT1FROMMP(pQmsg->mp1));
	return TRUE;
    }
    return FALSE;
}

/* we install this function as a pre-accelerator hook (undocumented). This hook
 is called even before an application accelerator table is translated, which helps
 to avoid compatibility problems with Ctrl-Tab hotkey */
BOOL EXPENTRY lswPreAccHook(HAB hab, PQMSG pQmsg, ULONG fs)
{
    USHORT usFlags;
    UCHAR ucScan;
    LSWDATA *plswData;
    BOOL bRet;
    PPIB ppib;

    DosGetInfoBlocks(NULL, &ppib);
    if (ppib->pib_flstatus & PS_EXITLIST)
	return FALSE;

    if (pQmsg->msg != WM_CHAR
	|| (usFlags = SHORT1FROMMP(pQmsg->mp1), ucScan =
	    CHAR4FROMMP(pQmsg->mp1), (usFlags & KC_SCANCODE)) == 0)
	return FALSE;

    bRet = FALSE;
/* tab w/ left Ctrl down. Trap the key either it's pressed or released,
 so that it does not reach other applications and trigger something*/
/* Alt-Tab might work in alternative shells */
    if (ucScan == SCAN_TAB
	&& ((usFlags & KC_CTRL) != 0 ^ (usFlags & KC_ALT) != 0)
	&& GetSharedMem(&plswData)) {
	if ((plswData->Settings.ucPopupHotKey == 0 && (usFlags & KC_ALT))
	    || (plswData->Settings.ucPopupHotKey == 1 && (usFlags & KC_CTRL))) {
	    if (usFlags & KC_KEYUP)
		WinPostMsg(plswData->hwndPopClient, WM_COMMAND, MPFROMSHORT
			   ((usFlags & KC_SHIFT) ? CMD_SCROLLLEFT :
			    CMD_SCROLLRIGHT), MPFROM2SHORT(CMDSRC_OTHER,
							   FALSE));
	    bRet = TRUE;
	}
	DosFreeMem(plswData);
    }

/* Show settings */
    if (!(usFlags & KC_KEYUP) && (usFlags & KC_ALT) && (usFlags & KC_CTRL)
	&& GetSharedMem(&plswData)) {
	if (ucScan == plswData->Settings.ucSettingsScan) {
	    WinPostMsg(plswData->hwndPopClient, WM_COMMAND,
		       MPFROMSHORT(CMD_SHOWSETTINGS),
		       MPFROM2SHORT(CMDSRC_OTHER, FALSE));
	    bRet = TRUE;
	}
	DosFreeMem(plswData);
    }

    return bRet;
}

VOID EXPENTRY lswSendHook(HAB hab, PSMHSTRUCT psmh, BOOL fInterTask)
{
    LSWDATA *plswData;
    HSWITCH hsw;
    static SWCNTRL swctl;	//reduce stack usage to a minimum since we don't use stack probes
    static SWP swp;
    ULONG ulFlags;
    BOOL bPosChange, bActiveChange;
    PPIB ppib;

    if (psmh->msg == SH_SWITCHLIST && IsWindowClass(psmh->hwnd, "WindowList") &&	//check msg target to avoid multiple messages
	(((LONG) psmh->mp1 >= 1 && (LONG) psmh->mp1 <= 3)
	 || (LONG) psmh->mp1 == 0x10001) && GetSharedMem(&plswData)) {
	if (plswData->bNowActive)
	    WinPostMsg(plswData->hwndPopClient, LSWM_SWITCHLISTCHANGED,
		       psmh->mp1, psmh->mp2);
	if (plswData->Settings.bTaskBarOn)
	    WinPostMsg(plswData->hwndTaskBarClient, LSWM_SWITCHLISTCHANGED,
		       psmh->mp1, psmh->mp2);
	if (WinIsWindow(hab, plswData->hwndParamDlg))
	    WinPostMsg(plswData->hwndParamDlg, LSWM_SWITCHLISTCHANGED,
		       psmh->mp1, psmh->mp2);
	DosFreeMem(plswData);
	return;
    }

    DosGetInfoBlocks(NULL, &ppib);
    if (ppib->pib_flstatus & PS_EXITLIST)
	return;

    if (psmh->msg == WM_COMMAND && IsWindowClass(psmh->hwnd, "AltTabWindow") && (WinGetPhysKeyState(HWND_DESKTOP, 0x5e) & 0x8000) == 0 &&	// right Alt up
	GetSharedMem(&plswData)) {
	if (plswData->Settings.ucPopupHotKey == 0
	    && plswData->Settings.bPMSwitcher) {
	    BOOL bShift = (WinGetPhysKeyState(HWND_DESKTOP, 0x2a) & 0x8000) ||	// either Shift down
		(WinGetPhysKeyState(HWND_DESKTOP, 0x36) & 0x8000);

	    WinPostMsg(plswData->hwndPopClient, WM_COMMAND,
		       MPFROMSHORT(bShift ? CMD_SCROLLLEFT : CMD_SCROLLRIGHT),
		       MPFROM2SHORT(CMDSRC_OTHER, FALSE));
	    psmh->msg = WM_NULL;
	}
	DosFreeMem(plswData);
	return;
    }

// make sure the taskbar does not show up and cover desktop menu
    if (psmh->msg == WM_INITMENU && IsDesktop(psmh->hwnd))
	hwndMenuUp = HWNDFROMMP(psmh->mp2);
    if (psmh->msg == WM_MENUEND && HWNDFROMMP(psmh->mp2) == hwndMenuUp)
	hwndMenuUp = NULLHANDLE;

    if (psmh->msg == WM_SETICON && WinQuerySwitchHandle(psmh->hwnd, 0) != 0 &&
	GetSharedMem(&plswData)) {
	if (plswData->bNowActive)
	    WinPostMsg(plswData->hwndPopClient, LSWM_WNDICONCHANGED,
		       psmh->mp1, MPFROMHWND(psmh->hwnd));
	if (plswData->Settings.bTaskBarOn)
	    WinPostMsg(plswData->hwndTaskBarClient, LSWM_WNDICONCHANGED,
		       psmh->mp1, MPFROMHWND(psmh->hwnd));
	DosFreeMem(plswData);
	return;
    }

    /* make just minimized or hidden window appear after the current one in
       the switch list if the corresponding option is on in settings */
    if (psmh->msg == WM_ADJUSTFRAMEPOS &&
	(((PSWP) PVOIDFROMMP(psmh->mp1))->fl & (SWP_HIDE | SWP_MINIMIZE)) &&
	!(WinGetPhysKeyState(HWND_DESKTOP, 0x38) & 0x8000) &&
	WinQuerySwitchHandle(psmh->hwnd, 0) != 0
	&& IsWindowClass(psmh->hwnd, "#1") && GetSharedMem(&plswData)) {

	if (plswData->Settings.bChangeZOrder)
	    ((PSWP) PVOIDFROMMP(psmh->mp1))->hwndInsertBehind = HWND_TOP;
	DosFreeMem(plswData);
	return;
    }

    if (psmh->msg == WM_DESTROY
	&& WinQueryWindow(psmh->hwnd, QW_PARENT) == WinQueryDesktopWindow(0,
									  0)
	&& GetSharedMem(&plswData)) {
	USHORT k;

//the minimized window list holds the virtual desktop positions the windows were minimized from
	for (k = 0; k < MINMAXITEMS; k++)
	    if (psmh->hwnd == plswData->MinMaxWnd[2 * k]) {
		plswData->MinMaxWnd[2 * k] = 0;
		break;
	    }
	DosFreeMem(plswData);
	return;
    }

    if (psmh->msg == WM_MINMAXFRAME
	&& WinQueryWindow(psmh->hwnd, QW_PARENT) == WinQueryDesktopWindow(0,
									  0)
	&& GetSharedMem(&plswData)) {

	USHORT k;

	ulFlags = ((PSWP) PVOIDFROMMP(psmh->mp1))->fl;
	WinQueryWindowPos(psmh->hwnd, &swp);

//the minimized window list holds the virtual desktop positions the windows were minimized from
	for (k = 0; k < MINMAXITEMS; k++)
	    if ((ulFlags & SWP_MAXIMIZE)
		&& psmh->hwnd == plswData->MinMaxWnd[2 * k]) {
		((PSWP) PVOIDFROMMP(psmh->mp1))->x +=
		    (WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN) +
		     8) * (((plswData->MinMaxWnd[2 * k + 1] & 0xFF00) >> 8) -
			   ((plswData->lCurrDesktop & 0xFF00) >> 8));
		((PSWP) PVOIDFROMMP(psmh->mp1))->y +=
		    (WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) +
		     8) * ((plswData->MinMaxWnd[2 * k + 1] & 0xFF) -
			   (plswData->lCurrDesktop & 0xFF));
		plswData->MinMaxWnd[2 * k] = 0;
		break;
	    }
	    else if ((ulFlags & SWP_MINIMIZE) && (swp.fl & SWP_MAXIMIZE)
		     && plswData->MinMaxWnd[2 * k] == 0) {
		plswData->MinMaxWnd[2 * k] = psmh->hwnd;
		plswData->MinMaxWnd[2 * k + 1] = plswData->lCurrDesktop;
		break;
            }
        DosFreeMem(plswData);
	return;
    }

/* can't call WinPostMsg in WM_ACTIVATE hook because of some random crashes
  if (!bWidget && psmh->msg==WM_ACTIVATE) {
    WinPostMsg(hwndTskBar,LSWM_ACTIVEWNDCHANGED,0,0);
    return;
  }
*/

    if (psmh->msg == WM_WINDOWPOSCHANGED &&
	(ulFlags = ((PSWP) PVOIDFROMMP(psmh->mp1))->fl,
	 bPosChange =
	 (ulFlags &
	  (SWP_HIDE | SWP_SHOW | SWP_MINIMIZE | SWP_MAXIMIZE | SWP_RESTORE)),
	 bActiveChange =
	 (ulFlags &
	  (SWP_ACTIVATE | SWP_DEACTIVATE | SWP_FOCUSACTIVATE |
	   SWP_FOCUSDEACTIVATE)), (bPosChange
				   && (hsw =
				       WinQuerySwitchHandle(psmh->hwnd,
							    0)) != 0
				   && (WinQuerySwitchEntry(hsw, &swctl),
				       swctl.hwnd == psmh->hwnd))
	 || bActiveChange) && GetSharedMem(&plswData)) {

//notify the lswitcher app of window state changes
	if (plswData->Settings.bTaskBarOn && bActiveChange)
	    WinPostMsg(plswData->hwndTaskBarClient, LSWM_ACTIVEWNDCHANGED,
		       MPFROMHWND(psmh->hwnd), psmh->mp1);
	if (bPosChange) {
	    WinPostMsg(plswData->hwndPopClient, LSWM_WNDSTATECHANGED,
		       MPFROM2SHORT(hsw & 0xFFFF, (SHORT) (ulFlags & 0xFFFF)),
		       MPFROMHWND(psmh->hwnd));
	    if (plswData->Settings.bTaskBarOn)
		WinPostMsg(plswData->hwndTaskBarClient, LSWM_WNDSTATECHANGED,
			   MPFROM2SHORT(hsw & 0xFFFF,
					(SHORT) (ulFlags & 0xFFFF)),
			   MPFROMHWND(psmh->hwnd));
	}
	DosFreeMem(plswData);
	return;
    }
}

LONG EXPENTRY lswHookInit(LSWDATA * plswData)
{
    bWidget = plswData->bWidget;

    hwndTskBar = plswData->hwndTaskBarClient;
    hwndPopup = plswData->hwndPopClient;

    if (DosQueryModuleHandle(DLL_NAME, &plswData->hmodHook))
	return -1;

    if (!WinSetHook
	(plswData->hab, NULLHANDLE, HK_SENDMSG, (PFN) lswSendHook,
	 plswData->hmodHook))
	return -2;
    if (!WinSetHook
	(plswData->hab, NULLHANDLE, HK_PREACCEL, (PFN) lswPreAccHook,
	 plswData->hmodHook))
	return -3;
    if (!WinSetHook
	(plswData->hab, NULLHANDLE, HK_INPUT, (PFN) lswInputHook,
	 plswData->hmodHook))
	return -4;

    WinPostMsg(0, WM_NULL, 0, 0);	//reset the hook

    return MAKEULONG(MAKEUSHORT(VERSIONMAJOR, VERSIONMINOR),
		     MAKEUSHORT(REVISION, 0));
}

VOID EXPENTRY lswHookTerm(LSWDATA * plswData)
{
    WinReleaseHook(plswData->hab, NULLHANDLE, HK_INPUT, (PFN) lswInputHook,
		   plswData->hmodHook);
    WinReleaseHook(plswData->hab, NULLHANDLE, HK_PREACCEL,
		   (PFN) lswPreAccHook, plswData->hmodHook);
    WinReleaseHook(plswData->hab, NULLHANDLE, HK_SENDMSG, (PFN) lswSendHook,
		   plswData->hmodHook);
}

ULONG EXPENTRY lswHookGetVersion(void)
{
    return MAKEULONG(MAKEUSHORT(VERSIONMAJOR, VERSIONMINOR),
		     MAKEUSHORT(REVISION, 0));
}
/**
 * Update the desktop area as appropriate
 * Code adapted from xworkplace by Ulrich M”ller & Paul Ratcliffe
 **/
BOOL EXPENTRY UpdateDesktopWorkarea(LSWDATA * plswData,
                                  BOOL fForceFullSize)   // in: if TRUE, desktop made full size
{
    BOOL brc = FALSE;
    static BOOL didOnce = FALSE;

    if (!didOnce) {
        ctrpDesktopWorkareaSupported();
        didOnce = TRUE;
    }
    // workareas supported on this system?
    if (fWorkAreaSupported)
    {
        /*
         *  (2)  Calculate new desktop workarea:
         *
         */
        RECTL       rclCurrent, rclNew;
        ULONG   ulCutBottom = 0, ulCutTop = 0,
        // take frame sizing border into account 
        cyFrame = WinQuerySysValue(HWND_DESKTOP, SV_CYSIZEBORDER);
        // get current first
        G_WinQueryDesktopWorkArea(HWND_DESKTOP, &rclCurrent);
        // compose new workarea; start out with screen size
        rclNew.xLeft = 0;
        rclNew.yBottom = 0;
        rclNew.xRight = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
        rclNew.yTop = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
            // the workarea APIs want inclusive rectangles...
            // I tested this, I get 1024 and 768 for the top right.

        if (!fForceFullSize) {
             if (!plswData->Settings.bTaskBarTopScr)
             {
                 // on bottom:
                 if (plswData->Settings.sTskBarCY + cyFrame > ulCutBottom)
                     ulCutBottom = plswData->Settings.sTskBarCY + cyFrame;
             }
             else
                 // on top:
                 if (plswData->Settings.sTskBarCY + cyFrame - 1 > ulCutTop)
                     ulCutTop = plswData->Settings.sTskBarCY + cyFrame - 1;
                    rclNew.yBottom += ulCutBottom;
                    rclNew.yTop -= ulCutTop;
                    }

        if (memcmp(&rclCurrent, &rclNew, sizeof(RECTL)) != 0)
        {
            // rectangle has changed:

            /*
             *  (3)  Set new desktop workarea:
             *
             */

            G_WinSetDesktopWorkArea(HWND_DESKTOP, &rclNew);

            brc = TRUE;
        }

    } // end if (fWorkAreaSupported)

    return brc;
}

