#include "precomp.h" // // WB.CPP // Whiteboard Services // // Copyright(c) Microsoft 1997- // #include #define MLZ_FILE_ZONE ZONE_WB // // Constructor // BOOL WbClient::WbInit(PUT_CLIENT putTask, UTEVENT_PROC eventProc) { BOOL rc = FALSE; DebugEntry(WbInit); // // Fill in the fields // m_state = STATE_EMPTY; m_subState = STATE_EMPTY; m_hLoadFile = INVALID_HANDLE_VALUE; wbClientReset(); // // Set the current state to the start of registration state. // Store the UT handle - we will need this in any calls to the UT API. // Set the ObMan handle to NULL to show that we have not registered. // m_state = STATE_STARTING; m_subState = STATE_START_START; m_putTask = putTask; m_pomClient = NULL; UT_RegisterEvent(putTask, eventProc, NULL, UT_PRIORITY_NORMAL); TRACE_OUT(("Initialized state to STATE_STARTING")); // // Register an event handler to trap events from ObMan. The third // parameter is data that will be passed to the event handler. We give // the client data pointer so that we can access the correct data for // each message. // UT_RegisterEvent(putTask, wbCoreEventHandler, this, UT_PRIORITY_NORMAL); // // Register as a Call Manager Secondary. This is required to query the // Call Manager personID to insert into the WB_PERSON structure. // if (!CMS_Register(putTask, CMTASK_WB, &(m_pcmClient))) { ERROR_OUT(("CMS_Register failed")); DC_QUIT; } // // Update the state // m_subState = STATE_START_REGISTERED_EVENT; TRACE_OUT(("Moved to substate STATE_START_REGISTERED_EVENT")); // // Register with ObMan as a client // if (OM_Register(putTask, OMCLI_WB, &(m_pomClient)) != 0) { ERROR_OUT(("OM_Register failed")); DC_QUIT; } // // Update the state // m_subState = STATE_START_REGISTERED_OM; TRACE_OUT(("Moved to substate STATE_START_REGISTERED_OM")); // // Register an exit handler. This has to be done after registering with // ObMan so that it gets called before the exit procedure registered by // ObMan. // UT_RegisterExit(putTask, wbCoreExitHandler, this); // // Update the state // m_state = STATE_STARTED; m_subState = STATE_STARTED_START; rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(WbInit, rc); return(rc); } // // CreateWBObject() // BOOL WINAPI CreateWBObject ( UTEVENT_PROC eventProc, IWbClient** ppwbClient ) { BOOL rc = FALSE; WbClient* pwbClient = NULL; PUT_CLIENT putTask = NULL; DebugEntry(CreateWBObject); // // Initialize the WB task // if (!UT_InitTask(UTTASK_WB, &putTask)) { ERROR_OUT(("Can't register WB task")); DC_QUIT; } // // Allocate the WB client object // pwbClient = new WbClient(); if (!pwbClient) { ERROR_OUT(("Couldn't allocate WbClient object")); UT_TermTask(&putTask); DC_QUIT; } else { rc = pwbClient->WbInit(putTask, eventProc); if (!rc) { pwbClient->WBP_Stop(eventProc); pwbClient = NULL; } } DC_EXIT_POINT: *ppwbClient = (IWbClient *)pwbClient; DebugExitBOOL(CreateWBObject, rc); return(rc); } // // WBP_Stop() // STDMETHODIMP_(void) WbClient::WBP_Stop(UTEVENT_PROC eventProc) { PUT_CLIENT putTask; DebugEntry(WBP_Stop); // // UT_TermTask() will call our exit handler and cause cleanup. // putTask = m_putTask; UT_DeregisterEvent(putTask, eventProc, NULL); // NOTE: // UT_TermTask() will put NULL into the pointer you pass in after it // has finished. But part of its job is to call your exit proc. Our // exit handler will call 'delete this' to kill us off. So when it // winds back to UT_TermTask(), the UT_CLIENT* pointer will be invalid. // That's why we use a temp. variable. // UT_TermTask(&putTask); DebugExitVOID(WBP_Stop); } // // WBP_PostEvent() // // Post an event back to the WB applet after a delay // STDMETHODIMP_(void) WbClient::WBP_PostEvent ( UINT delay, UINT event, UINT_PTR param1, UINT_PTR param2 ) { DebugEntry(WBP_PostEvent); UT_PostEvent(m_putTask, m_putTask, delay, event, param1, param2); DebugExitVOID(WBP_PostEvent); } // // WBP_JoinCall // STDMETHODIMP_(UINT) WbClient::WBP_JoinCall ( BOOL bContentsKeep, UINT callID ) { UINT result = 0; DebugEntry(WBP_JoinCall); TRACE_OUT(("Keep contents = %s", (bContentsKeep) ? "TRUE" : "FALSE")); TRACE_OUT(("Call ID = %d", callID)); // // If we are to keep the existing contents, just move our workset group // into the specified call. // if (bContentsKeep) { result = OM_WSGroupMoveReq(m_pomClient, m_hWSGroup, callID, &(m_wsgroupCorrelator)); if (result != 0) { ERROR_OUT(("OM_WSGroupMoveReq failed")); DC_QUIT; } // // The move request was successful, change the state to show that we // are waiting for a move request to complete. // m_state = STATE_REGISTERING; m_subState = STATE_REG_PENDING_WSGROUP_MOVE; TRACE_OUT(("Moved to substate STATE_REG_PENDING_WSGROUP_MOVE")); DC_QUIT; } // // Leave the current call. This returns the client state to what it // should be after a wbStart call. // wbLeaveCall(); // // Register with the workset group // result = OM_WSGroupRegisterPReq(m_pomClient, callID, OMFP_WB, OMWSG_WB, &(m_wsgroupCorrelator)); if (result != 0) { ERROR_OUT(("OM_WSGroupRegisterReq failed, result = %d", result)); DC_QUIT; } // // Update the state // m_state = STATE_REGISTERING; m_subState = STATE_REG_PENDING_WSGROUP_CON; TRACE_OUT(("Moved to state STATE_REGISTERING")); TRACE_OUT(("Moved to substate STATE_REG_PENDING_WSGROUP_CON")); DC_EXIT_POINT: DebugExitDWORD(WBP_JoinCall, result); return(result); } // // WBP_ContentsDelete // STDMETHODIMP_(UINT) WbClient::WBP_ContentsDelete(void) { UINT result = 0; DebugEntry(WBP_ContentsDelete); // // Make sure that we have the Page Order Lock // QUIT_NOT_GOT_PAGE_ORDER_LOCK(result); // // Request the lock // wbContentsDelete(RESET_CHANGED_FLAG); // // Reset the flag indicating that the contents have changed // m_changed = FALSE; DC_EXIT_POINT: DebugExitDWORD(WBP_ContentsDelete, result); return(result); } // // WBP_ContentsLoad // STDMETHODIMP_(UINT) WbClient::WBP_ContentsLoad(LPCSTR pFileName) { UINT result = 0; HANDLE hFile; DebugEntry(WBP_ContentsLoad); // // Check that we have the lock // QUIT_NOT_GOT_PAGE_ORDER_LOCK(result); // // Check the load state // if (m_loadState != LOAD_STATE_EMPTY) { result = WB_RC_ALREADY_LOADING; DC_QUIT; } // // Validate the file, and get a handle to it. // If there is an error, then no file handle is returned. // result = WBP_ValidateFile(pFileName, &hFile); if (result != 0) { ERROR_OUT(("Bad file header")); DC_QUIT; } // // Save the file handle for the rest of the load process // m_hLoadFile = hFile; // // We now need to make sure that the contents are deleted before we start // adding the new objects. // wbContentsDelete(DONT_RESET_CHANGED_FLAG); // // Update the load state to show that we are waiting for the contents // delete to complete. // m_loadState = LOAD_STATE_PENDING_CLEAR; TRACE_OUT(("Moved load state to LOAD_STATE_PENDING_CLEAR")); DC_EXIT_POINT: DebugExitDWORD(WBP_ContentsLoad, result); return(result); } // // WBP_ContentsSave // STDMETHODIMP_(UINT) WbClient::WBP_ContentsSave(LPCSTR pFileName) { UINT result = 0; UINT index; HANDLE hFile; PWB_PAGE_ORDER pPageOrder = &(m_pageOrder); WB_FILE_HEADER fileHeader; WB_END_OF_FILE endOfFile; DebugEntry(WBP_ContentsSave); // // Open the file // hFile = CreateFile(pFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == INVALID_HANDLE_VALUE) { result = WB_RC_CREATE_FAILED; ERROR_OUT(("Error creating file, win32 err=%d", GetLastError())); DC_QUIT; } // // Write the file header. This contains the name of the function profile // that wrote the file and allows the file type to be tested when it is // loaded. // ZeroMemory(&fileHeader, sizeof(fileHeader)); fileHeader.length = sizeof(fileHeader); fileHeader.type = TYPE_FILE_HEADER; lstrcpy(fileHeader.functionProfile, WB_FP_NAME); result = wbObjectSave(hFile, (LPBYTE) &fileHeader, sizeof(fileHeader)); if (result != 0) { ERROR_OUT(("Error writing end-of-page = %d", result)); DC_QUIT; } // // Loop through the pages, saving each as we go // for (index = 0; index < pPageOrder->countPages; index++) { // // Save the page // result = wbPageSave((WB_PAGE_HANDLE)pPageOrder->pages[index], hFile); if (result != 0) { ERROR_OUT(("Error saving page = %d", result)); DC_QUIT; } } // // If we have successfully written the contents, we write an end-of-page // marker to the file. // ZeroMemory(&endOfFile, sizeof(endOfFile)); endOfFile.length = sizeof(endOfFile); endOfFile.type = TYPE_END_OF_FILE; // // Write the end-of-file object // result = wbObjectSave(hFile, (LPBYTE) &endOfFile, sizeof(endOfFile)); if (result != 0) { ERROR_OUT(("Error writing end-of-page = %d", result)); DC_QUIT; } // // Success! // TRACE_OUT(("Resetting changed flag")); m_changed = FALSE; DC_EXIT_POINT: // // Close the file // if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); } // // If an error occurred in saving the contents to file, and the file was // opened, then delete it. // if (result != 0) { // // If the file was opened successfully, close it // if (hFile != INVALID_HANDLE_VALUE) { DeleteFile(pFileName); } } DebugExitDWORD(WBP_ContentsSave, result); return(result); } // // WBP_ContentsChanged // STDMETHODIMP_(BOOL) WbClient::WBP_ContentsChanged(void) { BOOL changed = FALSE; UINT result; WB_PAGE_HANDLE hPage; POM_OBJECT pObj; DebugEntry(WBP_ContentsChanged); TRACE_OUT(("changed %d", m_changed)); if (m_changed) { // // The whiteboard may have been changed, but if the change was to // empty it then don't bother. This is because we cannot detect that // the New operation was a local New or a remote clear and so we would // always prompt after New. Assuming that the user never needs to // always prompt after New. Assuming that the user will manually save // an workset he really wants to empty solves this problem. // // // Scan all objects looking to see what is there - get handle to first // page // result = wbPageHandle(WB_PAGE_HANDLE_NULL, PAGE_FIRST, &hPage); while (result == 0) { // // Get the handle of the first object in the page workset. // result = OM_ObjectH(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, 0, &pObj, FIRST); if (result != OM_RC_NO_SUCH_OBJECT) { changed = TRUE; break; } // // Try the next page for an object // result = wbPageHandle(hPage, PAGE_AFTER, &hPage); } } DebugExitBOOL(WBP_ContentsChanged, changed); return(changed); } // // WBP_ContentsLock // STDMETHODIMP_(void) WbClient::WBP_ContentsLock(void) { UINT result; DebugEntry(WBP_ContentsLock); // // Check that there is no lock currently // QUIT_LOCKED(result); QUIT_IF_CANCELLING_LOCK(result, WB_RC_BUSY); // // Request the lock // result = wbLock(WB_LOCK_TYPE_CONTENTS); if (result != 0) { WBP_PostEvent(0, WBP_EVENT_LOCK_FAILED, result, 0); } DC_EXIT_POINT: DebugExitVOID(WBP_ContentsLock); } // // WBP_PageOrderLock // STDMETHODIMP_(void) WbClient::WBP_PageOrderLock(void) { UINT result = 0; DebugEntry(WBP_PageOrderLock); // // Check that there is no lock currently // QUIT_LOCKED(result); // // Check that we are not in the process of cancelling a lock request // QUIT_IF_CANCELLING_LOCK(result, WB_RC_BUSY); // // Request the lock // result = wbLock(WB_LOCK_TYPE_PAGE_ORDER); if (result != 0) { WBP_PostEvent(0, WBP_EVENT_LOCK_FAILED, result, 0); } DC_EXIT_POINT: DebugExitVOID(WBP_PageOrderLock); } // // WBP_Unlock // STDMETHODIMP_(void) WbClient::WBP_Unlock(void) { UINT result = 0; DebugEntry(WBP_Unlock); // // If we are currently procesing a lock cancel request, leave the // function - it should unlock soon anyway. // QUIT_IF_CANCELLING_LOCK(result, 0); // // Check that we are currently processing a lock - the lock need not // necessarily have completed as we allow an application to call // WBP_Unlock any time after it has called WBP_Lock, effectively // cancelling a lock request. // QUIT_NOT_PROCESSING_LOCK(result); // // If we have completed the last lock request, simply do the unlock: // // The lock is not yet released, but will be when the // OM_OBJECT_DELETE_IND is received. // // if (m_lockState == LOCK_STATE_GOT_LOCK) { TRACE_OUT(("Unlock")); wbUnlock(); } else { // // Otherwise we are part way through processing the last lock and need // to cancel the lock on the next OM/lock event. e.g. when we receive // the OM_WS_LOCK indication, we should abandon lock processing and // unlock the WS. // TRACE_OUT(( "Part way through last lock set state to LOCK_STATE_CANCEL_LOCK")); m_lockState = LOCK_STATE_CANCEL_LOCK; } DC_EXIT_POINT: DebugExitVOID(WBP_Unlock); } // // WBP_LockStatus // STDMETHODIMP_(WB_LOCK_TYPE) WbClient::WBP_LockStatus(POM_OBJECT *ppObjPersonLock) { DebugEntry(WBP_LockStatus); *ppObjPersonLock = m_pObjPersonLock; DebugExitDWORD(WBP_LockStatus, m_lockType); return(m_lockType); } // // WBP_ContentsCountPages // STDMETHODIMP_(UINT) WbClient::WBP_ContentsCountPages(void) { UINT countPages; DebugEntry(WBP_ContentsCountPages); countPages = (m_pageOrder).countPages; DebugExitDWORD(WBP_ContentsCountPages, countPages); return(countPages); } // // WBP_PageClear // STDMETHODIMP_(UINT) WbClient::WBP_PageClear ( WB_PAGE_HANDLE hPage ) { UINT result = 0; DebugEntry(WBP_PageClear); QUIT_CONTENTS_LOCKED(result); result = wbPageClear(hPage, RESET_CHANGED_FLAG); DC_EXIT_POINT: DebugExitDWORD(WBP_PageClear, result); return(result); } // // WBP_PageClearConfirm // STDMETHODIMP_(void) WbClient::WBP_PageClearConfirm ( WB_PAGE_HANDLE hPage ) { DebugEntry(WBP_PageClearConfirm); wbPageClearConfirm(hPage); DebugExitVOID(WBP_PageClearConfirm); } // // WBP_PageAddBefore - See wb.h // STDMETHODIMP_(UINT) WbClient::WBP_PageAddBefore ( WB_PAGE_HANDLE hRefPage, PWB_PAGE_HANDLE phPage ) { UINT result = 0; DebugEntry(WBP_PageAddBefore); // // Make sure that we have the page order lock // QUIT_NOT_GOT_PAGE_ORDER_LOCK(result); // // Add a new page before the specified page // result = wbPageAdd(hRefPage, PAGE_BEFORE, phPage, RESET_CHANGED_FLAG); DC_EXIT_POINT: DebugExitDWORD(WBP_PageAddBefore, result); return(result); } // // WBP_PageAddAfter - See wb.h // STDMETHODIMP_(UINT) WbClient::WBP_PageAddAfter ( WB_PAGE_HANDLE hRefPage, PWB_PAGE_HANDLE phPage ) { UINT result = 0; DebugEntry(WBP_PageAddAfter); // // Make sure that we have the Page Order Lock // QUIT_NOT_GOT_PAGE_ORDER_LOCK(result); // // Add a new page before the specified page // result = wbPageAdd(hRefPage, PAGE_AFTER, phPage, RESET_CHANGED_FLAG); DC_EXIT_POINT: DebugExitDWORD(WBP_PageAddAfter, result); return(result); } // // WBP_PageHandle - See wb.h // STDMETHODIMP_(UINT) WbClient::WBP_PageHandle ( WB_PAGE_HANDLE hRefPage, UINT where, PWB_PAGE_HANDLE phPage ) { UINT result; DebugEntry(WBP_PageHandle); result = wbPageHandle(hRefPage, where, phPage); DebugExitDWORD(WBP_PageHandle, result); return(result); } // // WBP_PageHandleFromNumber // STDMETHODIMP_(UINT) WbClient::WBP_PageHandleFromNumber ( UINT pageNumber, PWB_PAGE_HANDLE phPage ) { UINT result; DebugEntry(WBP_PageHandleFromNumber); result = wbPageHandleFromNumber(pageNumber, phPage); DebugExitDWORD(WBP_PageHandleFromNumber, result); return(result); } // // WBP_PageNumberFromHandle // STDMETHODIMP_(UINT) WbClient::WBP_PageNumberFromHandle ( WB_PAGE_HANDLE hPage ) { UINT pageNumber = 0; DebugEntry(WBP_PageNumberFromHandle); if ((hPage < FIRST_PAGE_WORKSET) || (hPage > FIRST_PAGE_WORKSET + WB_MAX_PAGES - 1)) { WARNING_OUT(("WB: Invalid hPage=%u", (UINT) hPage)); DC_QUIT; } // // Validate the page handle given // if (GetPageState(hPage)->state != PAGE_IN_USE) { DC_QUIT; } pageNumber = wbPageOrderPageNumber(&(m_pageOrder), hPage); DC_EXIT_POINT: DebugExitDWORD(WBP_PageNumberFromHandle, pageNumber); return(pageNumber); } // // WBP_PageDelete - See wb.h // STDMETHODIMP_(UINT) WbClient::WBP_PageDelete ( WB_PAGE_HANDLE hPage ) { UINT result = 0; PWB_PAGE_STATE pPageState; DebugEntry(WBP_PageDelete); // // Make sure that we have the Page Order Lock // QUIT_NOT_GOT_PAGE_ORDER_LOCK(result); // // Delete the page // // // Check whether the page is already being deleted // pPageState = GetPageState(hPage); if ( (pPageState->state == PAGE_IN_USE) && (pPageState->subState == PAGE_STATE_EMPTY)) { // // Set the page state to show that it is now in the delete process // pPageState->subState = PAGE_STATE_LOCAL_DELETE; TRACE_OUT(("Moved page %d substate to PAGE_STATE_LOCAL_DELETE", hPage)); // // Update the page control object // if (wbWritePageControl(FALSE) != 0) { wbError(); DC_QUIT; } } DC_EXIT_POINT: DebugExitDWORD(WBP_PageDelete, result); return(result); } // // WBP_PageDeleteConfirm - See wb.h // STDMETHODIMP_(void) WbClient::WBP_PageDeleteConfirm ( WB_PAGE_HANDLE hPage ) { UINT result = 0; PWB_PAGE_ORDER pPageOrder; PWB_PAGE_STATE pPageState; DebugEntry(WBP_PageDeleteConfirm); // // Validate the specified page // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Delete the page // // // Check that the page specified is waiting for a delete confirm // pPageState = GetPageState(hPage); ASSERT(((pPageState->subState == PAGE_STATE_LOCAL_DELETE_CONFIRM) || (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM))); // // Delete the page from the page order // pPageOrder = &(m_pageOrder); wbPageOrderPageDelete(pPageOrder, hPage); // // Clear the page (to free memory) // if (pPageState->subState == PAGE_STATE_LOCAL_DELETE_CONFIRM) { TRACE_OUT(("Local delete - clearing the page")); if (wbPageClear(hPage, RESET_CHANGED_FLAG) != 0) { ERROR_OUT(("Unable to clear page")); DC_QUIT; } } // // Update the page state to "not in use", with a substate of "ready" (we // do not close the associated workset. // pPageState->state = PAGE_NOT_IN_USE; pPageState->subState = PAGE_STATE_READY; TRACE_OUT(("Moved page %d state to PAGE_NOT_IN_USE", hPage)); // // Continue updating the Page Order // wbProcessPageControlChanges(); // // Check the load state to see whether we are waiting to load the // contents // if (m_loadState == LOAD_STATE_PENDING_DELETE) { // // We are waiting to load. If there is now only one page available, we // are ready to load, otherwise we wait for the further page deletes to // happen. // if (m_pageOrder.countPages == 1) { // // Start the load proper // wbStartContentsLoad(); } } DC_EXIT_POINT: DebugExitVOID(WBP_PageDeleteConfirm); } // // WBP_PageMoveAfter // STDMETHODIMP_(UINT) WbClient::WBP_PageMove ( WB_PAGE_HANDLE hRefPage, WB_PAGE_HANDLE hPage, UINT where ) { UINT result = 0; DebugEntry(WBP_PageMove); // // Make sure that we have the Page Order Lock // QUIT_NOT_GOT_PAGE_ORDER_LOCK(result); // // Validate the specified page handles // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); ASSERT(GetPageState(hRefPage)->state == PAGE_IN_USE); // // Move the page // result = wbPageMove(hRefPage, hPage, where); DC_EXIT_POINT: DebugExitDWORD(WBP_PageMove, result); return(result); } // // WBP_PageCountGraphics // STDMETHODIMP_(UINT) WbClient::WBP_PageCountGraphics ( WB_PAGE_HANDLE hPage ) { UINT count; DebugEntry(WBP_PageCountGraphics); // // Count the number of graphics on the page // OM_WorksetCountObjects(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, &count); DebugExitDWORD(WBP_PageCountGraphics, count); return(count); } // // WBP_GraphicAllocate // STDMETHODIMP_(UINT) WbClient::WBP_GraphicAllocate ( WB_PAGE_HANDLE hPage, UINT length, PPWB_GRAPHIC ppGraphic ) { UINT result = 0; POM_OBJECTDATA pData; DebugEntry(WBP_GraphicAllocate); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Allocate a graphic object // result = OM_ObjectAlloc(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, length, &pData); if (result != 0) { ERROR_OUT(("OM_ObjectAlloc = %d", result)); DC_QUIT; } // // Set the length of the object // pData->length = length; // // Convert the ObMan pointer to a core pointer // *ppGraphic = GraphicPtrFromObjectData(pData); // // Initialize the graphic header // ZeroMemory(*ppGraphic, sizeof(WB_GRAPHIC)); DC_EXIT_POINT: DebugExitDWORD(WBP_GraphicAllocate, result); return(result); } // // WBP_GraphicAddLast // STDMETHODIMP_(UINT) WbClient::WBP_GraphicAddLast ( WB_PAGE_HANDLE hPage, PWB_GRAPHIC pGraphic, PWB_GRAPHIC_HANDLE phGraphic ) { UINT result = 0; POM_OBJECTDATA pData; PWB_PAGE_STATE pPageState; DebugEntry(WBP_GraphicAddLast); // // Check whether another person has an active contents lock // QUIT_CONTENTS_LOCKED(result); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // If the Client is asking for the lock, copy the local person ID into // the graphic object. // if (pGraphic->locked == WB_GRAPHIC_LOCK_LOCAL) { pGraphic->lockPersonID = m_personID; } // // Check whether the page has been deleted but not yet confirmed: in this // case return OK but do not add the object to the workset. // pPageState = GetPageState(hPage); if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE) || (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM)) { TRACE_OUT(("Object add requested in externally deleted page - ignored")); *phGraphic = 0; DC_QUIT; } // // Add the graphic object to the page // pData = ObjectDataPtrFromGraphic(pGraphic); result = OM_ObjectAdd(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, &pData, sizeof(WB_GRAPHIC), phGraphic, LAST); if (result != 0) { ERROR_OUT(("OM_ObjectAdd = %d", result)); DC_QUIT; } DC_EXIT_POINT: DebugExitDWORD(WBP_GraphicAddLast, result); return(result); } // // WBP_GraphicUpdateRequest // STDMETHODIMP_(UINT) WbClient::WBP_GraphicUpdateRequest ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic, PWB_GRAPHIC pGraphic ) { UINT result = 0; POM_OBJECTDATA pData; PWB_PAGE_STATE pPageState; DebugEntry(WBP_GraphicUpdateRequest); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Check whether another person has an active contents lock // QUIT_CONTENTS_LOCKED(result); // // Check whether another person has the graphic locked // QUIT_GRAPHIC_LOCKED(hPage, hGraphic, result); // // If the Client is asking for the lock, copy the local person ID into // the graphic object. // if (pGraphic->locked == WB_GRAPHIC_LOCK_LOCAL) { pGraphic->lockPersonID = m_personID; } // // Check whether the page has been deleted but not yet confirmed // pPageState = GetPageState(hPage); if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE) || (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM)) { TRACE_OUT(("Object update requested in externally deleted page - ignored")); DC_QUIT; } // // Update the object // pData = ObjectDataPtrFromGraphic(pGraphic); result = OM_ObjectUpdate(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic, &pData); // // Dont worry too much if the update fails because the object has been // deleted, just trace an alert and return OK - the front end will be // told that the object has gone later. // if (result != 0) { if (result == OM_RC_OBJECT_DELETED) { TRACE_OUT(("Update failed because object has been deleted")); result = 0; DC_QUIT; } ERROR_OUT(("OM_ObjectUpdate = %d", result)); DC_QUIT; } // // Note that the object has not yet been updated. An // OM_OBJECT_UPDATE_IND event will be generated. // DC_EXIT_POINT: DebugExitDWORD(WBP_GraphicUpdateRequest, result); return(result); } // // WBP_GraphicUpdateConfirm // STDMETHODIMP_(void) WbClient::WBP_GraphicUpdateConfirm ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic ) { DebugEntry(WBP_GraphicUpdateConfirm); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Confirm the update to ObMan // OM_ObjectUpdateConfirm(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic); DebugExitVOID(WBP_GraphicUpdateConfirm); } // // WBP_GraphicReplaceRequest // STDMETHODIMP_(UINT) WbClient::WBP_GraphicReplaceRequest ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic, PWB_GRAPHIC pGraphic ) { UINT result = 0; POM_OBJECTDATA pData; POM_OBJECT pObjPersonLock; PWB_PAGE_STATE pPageState; DebugEntry(WBP_GraphicReplaceRequest); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // We allow the replace to go ahead if: // - The object is locked by the local user // - The object is not locked and the contents are not locked by // a remote user // // Note that this allow the replace if the contents are locked by another // user, but the local user has the object locked. // if (wbGraphicLocked(hPage, hGraphic, &pObjPersonLock)) { if (pObjPersonLock != m_pObjLocal) { TRACE_OUT(("Graphic is locked by remote client")); result = WB_RC_GRAPHIC_LOCKED; DC_QUIT; } } else { QUIT_CONTENTS_LOCKED(result); } // // If the Client is asking for the lock, copy the local person ID into // the graphic object. // if (pGraphic->locked == WB_GRAPHIC_LOCK_LOCAL) { pGraphic->lockPersonID = m_personID; } // // Check whether the page has been deleted but not yet confirmed // pPageState = GetPageState(hPage); if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE) || (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM)) { TRACE_OUT(("Object replace requested in externally deleted page - ignored")); DC_QUIT; } // // Replace the object // pData = ObjectDataPtrFromGraphic(pGraphic); result = OM_ObjectReplace(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic, &pData); if (result != 0) { ERROR_OUT(("OM_ObjectReplace = %d", result)); DC_QUIT; } // // Note that the object has not yet been updated. An // OM_OBJECT_REPLACE_IND event will be generated. // DC_EXIT_POINT: DebugExitDWORD(WBP_GraphicReplaceRequest, result); return(result); } // // WBP_GraphicUpdateConfirm // STDMETHODIMP_(void) WbClient::WBP_GraphicReplaceConfirm ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic ) { DebugEntry(WBP_GraphicReplaceConfirm); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Confirm the update to ObMan // OM_ObjectReplaceConfirm(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic); DebugExitVOID(WBP_GraphicReplaceConfirm); } // // WBP_GraphicDeleteRequest // STDMETHODIMP_(UINT) WbClient::WBP_GraphicDeleteRequest ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic ) { UINT result = 0; DebugEntry(WBP_GraphicDeleteRequest); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Check whether another person has an active contents lock // QUIT_CONTENTS_LOCKED(result); // // Check whether another person has the graphic locked // QUIT_GRAPHIC_LOCKED(hPage, hGraphic, result); // // Delete the object // result = OM_ObjectDelete(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic); if (result != 0) { ERROR_OUT(("OM_ObjectDelete = %d", result)); DC_QUIT; } // // Note that at this point the object is not yet deleted. An // OM_OBJECT_DELETE_IND event is raised and processed by the Whiteboard // Core event handler. A WB_EVENT_GRAPHIC_DELETED is then posted to the // client. The client then calls WBP_GraphicDeleteConfirm to complete // the deletion. // DC_EXIT_POINT: DebugExitDWORD(WBP_GraphicDeleteRequest, result); return(result); } // // WBP_GraphicDeleteConfirm // STDMETHODIMP_(void) WbClient::WBP_GraphicDeleteConfirm ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic ) { DebugEntry(WBP_GraphicDeleteConfirm); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Confirm the delete // OM_ObjectDeleteConfirm(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic); DebugExitVOID(WBP_GraphicDeleteConfirm); } // // WBP_GraphicUnlock // STDMETHODIMP_(void) WbClient::WBP_GraphicUnlock ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic ) { UINT rc; POM_OBJECTDATA pData; PWB_PAGE_STATE pPageState; PWB_GRAPHIC pGraphic = NULL; PWB_GRAPHIC pNewGraphic = NULL; DebugEntry(WBP_GraphicUnlock); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Check whether the page has been deleted but not yet confirmed // pPageState = GetPageState(hPage); if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE) || (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM)) { TRACE_OUT(("Object update requested in externally deleted page - ignored")); DC_QUIT; } // // Read the object from ObMan // if (WBP_GraphicGet(hPage, hGraphic, &pGraphic) != 0) { TRACE_OUT(("Could not get graphic - leaving function")); DC_QUIT; } // // Check the local client has the graphic locked // QUIT_GRAPHIC_NOT_LOCKED(pGraphic, rc); // // Allocate a new graphic, copied from the existing one, and clear the // lock field. // if (WBP_GraphicAllocate(hPage, sizeof(WB_GRAPHIC), &pNewGraphic) != 0) { ERROR_OUT(("Could not allocate memory for update object")); DC_QUIT; } memcpy(pNewGraphic, pGraphic, sizeof(WB_GRAPHIC)); pNewGraphic->locked = WB_GRAPHIC_LOCK_NONE; // // Unlock & update the object // pData = ObjectDataPtrFromGraphic(pNewGraphic); pData->length = sizeof(WB_GRAPHIC); rc = OM_ObjectUpdate(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic, &pData); // // Dont worry too much if the update fails because the object has been // deleted, just trace an alert and return OK - the front end will be // told that the object has gone later. // if (rc != 0) { if (rc == OM_RC_OBJECT_DELETED) { TRACE_OUT(("Update failed because object has been deleted")); } else { ERROR_OUT(("OM_ObjectUpdate = %d", rc)); } DC_QUIT; } // // Note that the object has not yet been updated. An // OM_OBJECT_UPDATE_IND event will be generated. // DC_EXIT_POINT: // // If we read the graphic successfully, release it now // if (pGraphic != NULL) { WBP_GraphicRelease(hPage, hGraphic, pGraphic); } DebugExitVOID(WBP_GraphicUnlock); } // // WBP_GraphicMove // STDMETHODIMP_(UINT) WbClient::WBP_GraphicMove ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic, UINT where ) { UINT result = 0; PWB_PAGE_STATE pPageState; DebugEntry(WBP_GraphicMove); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Check whether another person has an active contents lock // QUIT_CONTENTS_LOCKED(result); // // Check whether the page has been deleted but not yet confirmed // pPageState = GetPageState(hPage); if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE) || (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM)) { TRACE_OUT(("Object moved in externally deleted page - ignored")); DC_QUIT; } // // Do the move // result = OM_ObjectMove(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic, (OM_POSITION)where); if (result != 0) { ERROR_OUT(("OM_ObjectMove = %d", result)); DC_QUIT; } DC_EXIT_POINT: DebugExitDWORD(WBP_GraphicMove, result); return(result); } // // WBP_GraphicSelectLast // STDMETHODIMP_(UINT) WbClient::WBP_GraphicSelect ( WB_PAGE_HANDLE hPage, POINT point, WB_GRAPHIC_HANDLE hRefGraphic, UINT where, PWB_GRAPHIC_HANDLE phGraphic ) { UINT result = 0; DebugEntry(WBP_GraphicSelect); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Get the handle of the last object in the workset // result = OM_ObjectH(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hRefGraphic, &hRefGraphic, (OM_POSITION)where); if (result == OM_RC_NO_SUCH_OBJECT) { result = WB_RC_NO_SUCH_GRAPHIC; DC_QUIT; } if (result != 0) { ERROR_OUT(("OM_ObjectH = %d", result)); DC_QUIT; } // // Get the previous matching graphic - this function starts from the // object in hRefGraphic and works back. // result = wbGraphicSelectPrevious(hPage, &point, hRefGraphic, phGraphic); DC_EXIT_POINT: DebugExitDWORD(WBP_GraphicSelect, result); return(result); } // // WBP_GraphicGet // STDMETHODIMP_(UINT) WbClient::WBP_GraphicGet ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic, PPWB_GRAPHIC ppGraphic ) { UINT result = 0; UINT rc; POM_OBJECTDATA pData; PWB_GRAPHIC pGraphic; POM_OBJECT pObjPersonLock; DebugEntry(WBP_GraphicGet); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Read the object. // result = OM_ObjectRead(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic, &pData); if (result != 0) { ERROR_OUT(("OM_ObjectRead = %d", result)); DC_QUIT; } // // Convert the ObMan pointer to a core pointer // pGraphic = GraphicPtrFromObjectData(pData); // // If the graphic object indicates that it is locked - verify that the // locking person is still in the call. // if (pGraphic->locked != WB_GRAPHIC_LOCK_NONE) { TRACE_OUT(("Graphic has lock flag set")); // // Convert the lock person ID in the graphic to a person handle // rc = OM_ObjectIDToPtr(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pGraphic->lockPersonID, &pObjPersonLock); if (rc == OM_RC_BAD_OBJECT_ID) { // // The locking person is no longer in the call - reset the lock flag // in the graphic. This tells the client that the graphic can be // changed. // TRACE_OUT(("Lock person has left call - resetting lock flag")); pGraphic->locked = WB_GRAPHIC_LOCK_NONE; } else { if (rc == 0) { // // The object is locked - check whether the lock belongs to the // local person or a remote person. // if (pObjPersonLock == m_pObjLocal) { // // Change the lock type to local to tell the client that they // have the lock on this object. // TRACE_OUT(("Lock belongs to local person")); pGraphic->locked = WB_GRAPHIC_LOCK_LOCAL; } else { // // Change the lock type to remote to tell the client that another // person has the lock on this object. // TRACE_OUT(("Lock belongs to remote person")); pGraphic->locked = WB_GRAPHIC_LOCK_REMOTE; } } } } // // Return the pointer to the graphic data // *ppGraphic = pGraphic; DC_EXIT_POINT: DebugExitDWORD(WBP_GraphicGet, result); return(result); } // // WBP_GraphicRelease // STDMETHODIMP_(void) WbClient::WBP_GraphicRelease ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic, PWB_GRAPHIC pGraphic ) { POM_OBJECTDATA pData; DebugEntry(WBP_GraphicRelease); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Release the object. // pData = ObjectDataPtrFromGraphic(pGraphic); OM_ObjectRelease(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hGraphic, &pData); DebugExitVOID(WBP_GraphicRelease); } // // WBP_GraphicHandle // STDMETHODIMP_(UINT) WbClient::WBP_GraphicHandle ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hRefGraphic, UINT where, PWB_GRAPHIC_HANDLE phGraphic ) { UINT result; DebugEntry(WBP_GraphicHandle); // // Check that the page handle is valid // ASSERT(GetPageState(hPage)->state == PAGE_IN_USE); // // Get the handle of the first object in the page workset. // result = OM_ObjectH(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, hRefGraphic, phGraphic, (OM_POSITION)where); if (result == OM_RC_NO_SUCH_OBJECT) { TRACE_OUT(("No objects there")); result = WB_RC_NO_SUCH_GRAPHIC; } DebugExitDWORD(WBP_GraphicHandle, result); return(result); } // // WBP_PersonHandleFirst // STDMETHODIMP_(void) WbClient::WBP_PersonHandleFirst ( POM_OBJECT * ppObjPerson ) { DebugEntry(WBP_PersonHandleFirst); OM_ObjectH(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, 0, ppObjPerson, FIRST); DebugExitVOID(WBP_PersonHandleFirst); } // // WBP_PersonHandleNext // STDMETHODIMP_(UINT) WbClient::WBP_PersonHandleNext ( POM_OBJECT pObjPerson, POM_OBJECT * ppObjPersonNext ) { UINT rc; DebugEntry(WBP_PersonHandleNext); // // Get the handle of the next object in the user information workset. // rc = OM_ObjectH(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObjPerson, ppObjPersonNext, AFTER); if (rc == OM_RC_NO_SUCH_OBJECT) { rc = WB_RC_NO_SUCH_PERSON; } else if (rc != 0) { ERROR_OUT(("OM_ObjectNextH = %d", rc)); } DebugExitDWORD(WBP_PersonHandleNext, rc); return(rc); } // // WBP_PersonHandleLocal // STDMETHODIMP_(void) WbClient::WBP_PersonHandleLocal ( POM_OBJECT * ppObjPerson ) { DebugEntry(WBP_PersonHandleLocal); *ppObjPerson = m_pObjLocal; DebugExitVOID(WBP_PersonHandleLocal); } // // WBP_PersonCountInCall // STDMETHODIMP_(UINT) WbClient::WBP_PersonCountInCall(void) { UINT count; POM_OBJECT pObj; DebugEntry(WBP_PersonCountInCall); // // Get the count: // pObj = NULL; WBP_PersonHandleFirst(&pObj); for (count = 1; ; count++) { if (WBP_PersonHandleNext(pObj, &pObj) == WB_RC_NO_SUCH_PERSON) { break; } } DebugExitDWORD(WBP_PersonCountInCall, count); return(count); } // // WBP_GetPersonData // STDMETHODIMP_(UINT) WbClient::WBP_GetPersonData ( POM_OBJECT pObjPerson, PWB_PERSON pPerson ) { UINT rc; DebugEntry(WBP_GetPersonData); ASSERT(!IsBadWritePtr(pPerson, sizeof(WB_PERSON))); // // Get the object. // rc = wbPersonGet(pObjPerson, pPerson); DebugExitDWORD(WBP_GetPersonData, rc); return(rc); } // // WBP_SetLocalPersonData // STDMETHODIMP_(UINT) WbClient::WBP_SetLocalPersonData(PWB_PERSON pPerson) { UINT rc; POM_OBJECTDATA pUserObject; DebugEntry(WBP_SetPersonData); ASSERT(!IsBadReadPtr(pPerson, sizeof(WB_PERSON))); // // Allocate a user object // rc = OM_ObjectAlloc(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, sizeof(WB_PERSON), &pUserObject); if (rc != 0) { ERROR_OUT(("OM_ObjectAlloc = %d", rc)); DC_QUIT; } // // Set the length of the object // pUserObject->length = sizeof(WB_PERSON); // // Copy the user information into the ObMan object // memcpy(pUserObject->data, pPerson, sizeof(WB_PERSON)); // // Replace the user object // rc = OM_ObjectReplace(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, m_pObjLocal, &pUserObject); if (rc != 0) { ERROR_OUT(("OM_ObjectReplace")); // // Discard the object // OM_ObjectDiscard(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, &pUserObject); DC_QUIT; } DC_EXIT_POINT: // // Note that the object has not yet been updated. An // OM_OBJECT_UPDATE_IND event will be generated. // DebugExitDWORD(WBP_SetPersonData, rc); return(rc); } // // WBP_PersonUpdateConfirm // STDMETHODIMP_(void) WbClient::WBP_PersonUpdateConfirm ( POM_OBJECT pObjPerson ) { DebugEntry(WBP_PersonUpdateConfirm); // // Confirm the update to ObMan // OM_ObjectUpdateConfirm(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObjPerson); DebugExitVOID(WBP_PersonUpdateConfirm); } // // WBP_PersonReplaceConfirm // STDMETHODIMP_(void) WbClient::WBP_PersonReplaceConfirm ( POM_OBJECT pObjPerson ) { DebugEntry(WBP_PersonReplaceConfirm); // // Confirm the replace to ObMan // OM_ObjectReplaceConfirm(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObjPerson); DebugExitVOID(WBP_PersonReplaceConfirm); } // // WBP_PersonLeftConfirm // STDMETHODIMP_(void) WbClient::WBP_PersonLeftConfirm ( POM_OBJECT pObjPerson ) { DebugEntry(WBP_PersonLeftConfirm); // // Confirm the update to ObMan // OM_ObjectDeleteConfirm(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObjPerson); DebugExitVOID(WBP_PersonLeftConfirm); } // // WBP_SyncPositionGet // STDMETHODIMP_(UINT) WbClient::WBP_SyncPositionGet ( PWB_SYNC pSync ) { UINT result; POM_OBJECTDATA pSyncObject = NULL; PWB_SYNC_CONTROL pSyncControl = NULL; DebugEntry(WBP_SyncPositionGet); ASSERT(!IsBadWritePtr(pSync, sizeof(WB_SYNC))); // // Read the Sync Control object // result = OM_ObjectRead(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, m_pObjSyncControl, &pSyncObject); if (result != 0) { ERROR_OUT(("Error reading Sync Control Object = %d", result)); DC_QUIT; } pSyncControl = (PWB_SYNC_CONTROL) pSyncObject->data; // // Copy the Sync Person details to the result field // NOTE: // LiveLan sends a larger object, we need to ignore the stuff past the // end. // if (pSyncControl->sync.length != sizeof(WB_SYNC)) { WARNING_OUT(("WBP_SyncPositionGet (interop): Remote created WB_SYNC of size %d, we expected size %d", pSyncControl->sync.length, sizeof(WB_SYNC))); } memcpy(pSync, &pSyncControl->sync, min(sizeof(WB_SYNC), pSyncControl->sync.length)); // // Release the Sync Control Object // OM_ObjectRelease(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, m_pObjSyncControl, &pSyncObject); DC_EXIT_POINT: DebugExitDWORD(WBP_SyncPositionGet, result); return(result); } // // WBP_SyncPositionUpdate // STDMETHODIMP_(UINT) WbClient::WBP_SyncPositionUpdate ( PWB_SYNC pSync ) { UINT result; POM_OBJECTDATA pSyncObject = NULL; PWB_SYNC_CONTROL pSyncControl = NULL; DebugEntry(WBP_SyncPositionUpdate); ASSERT(!IsBadReadPtr(pSync, sizeof(WB_SYNC))); // // Write the new sync control object (do not create it) // result = wbWriteSyncControl(pSync, FALSE); DebugExitDWORD(WBP_SyncPositionUpdate, result); return(result); } // // WBP_CancelLoad // STDMETHODIMP_(UINT) WbClient::WBP_CancelLoad(void) { UINT result = 0; DebugEntry(WBP_CancelLoad); // // Check a load is in progress // if (m_loadState == LOAD_STATE_EMPTY) { TRACE_OUT(("request to cancel load, but there is no load in progress")); result = WB_RC_NOT_LOADING; DC_QUIT; } TRACE_OUT(("Cancelling load in progress")); // // Close the file // if (m_hLoadFile != INVALID_HANDLE_VALUE) { CloseHandle(m_hLoadFile); m_hLoadFile = INVALID_HANDLE_VALUE; } // // reset the load state to show we're no longer loading. // m_loadState = LOAD_STATE_EMPTY; DC_EXIT_POINT: DebugExitDWORD(WBP_CancelLoad, result); return(result); } // // // Name: WBP_ValidateFile // // Purpose: Validate that a passed filename holds a valid whiteboard file // // Returns: 0 if successful // !0 if an error // // STDMETHODIMP_(UINT) WbClient::WBP_ValidateFile ( LPCSTR pFileName, HANDLE * phFile ) { UINT result = 0; HANDLE hFile; WB_FILE_HEADER fileHeader; UINT length; ULONG cbSizeRead; BOOL fileOpen = FALSE; DebugEntry(WBP_ValidateFile); // // Open the file // hFile = CreateFile(pFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == INVALID_HANDLE_VALUE) { WARNING_OUT(("Error opening file, win32 err=%d", GetLastError())); result = WB_RC_CREATE_FAILED; DC_QUIT; } // // Show that we have opened the file successfully // fileOpen = TRUE; // // Read the file header length // if (! ReadFile(hFile, (void *) &length, sizeof(length), &cbSizeRead, NULL)) { WARNING_OUT(("Error reading file header length, win32 err=%d", GetLastError())); result = WB_RC_READ_FAILED; DC_QUIT; } ASSERT(cbSizeRead == sizeof(length)); if (length != sizeof(fileHeader)) { WARNING_OUT(("Bad file header")); result = WB_RC_BAD_FILE_FORMAT; DC_QUIT; } // // Read the file header // if (! ReadFile(hFile, (void *) &fileHeader, sizeof(fileHeader), &cbSizeRead, NULL)) { WARNING_OUT(("Error reading file header, win32 err=%d", GetLastError())); result = WB_RC_READ_FAILED; DC_QUIT; } if (cbSizeRead != sizeof(fileHeader)) { WARNING_OUT(("Could not read file header")); result = WB_RC_BAD_FILE_FORMAT; DC_QUIT; } // // Validate the file header // if ( (fileHeader.type != TYPE_FILE_HEADER) || lstrcmp(fileHeader.functionProfile, WB_FP_NAME)) { WARNING_OUT(("Bad function profile in file header")); result = WB_RC_BAD_FILE_FORMAT; DC_QUIT; } DC_EXIT_POINT: // // Return the handle, if the user needs it. // if ( (result == 0) && (phFile != NULL)) { TRACE_OUT(("return file handle")); *phFile = hFile; } // // Close the file if there has been an error or the caller simply // doesnt want the file handle. // if ( (fileOpen) && ((phFile == NULL) || (result != 0)) ) { CloseHandle(hFile); } DebugExitDWORD(WBP_ValidateFile, result); return(result); } // // // Name: wbGraphicLocked // // Purpose: Test whether a client has a lock on the specified graphic, and // if so, return the person handle of the client holding the lock. // // Returns: TRUE if a client has a lock // FALSE otherwise // // BOOL WbClient::wbGraphicLocked ( WB_PAGE_HANDLE hPage, WB_GRAPHIC_HANDLE hGraphic, POM_OBJECT * ppObjLock ) { BOOL result = FALSE; UINT rc; PWB_GRAPHIC pGraphic = NULL; DebugEntry(wbGraphicLocked); // // Read the object // if (WBP_GraphicGet(hPage, hGraphic, &pGraphic) != 0) { DC_QUIT; } // // Look at its lock details // if (pGraphic->locked != WB_GRAPHIC_LOCK_NONE) { // // The lock flag in the graphic is set // // // Convert the lock user ID in the graphic to a handle // rc = OM_ObjectIDToPtr(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pGraphic->lockPersonID, ppObjLock); if (rc == 0) { TRACE_OUT(("Graphic is locked")); result = TRUE; DC_QUIT; } if (rc != OM_RC_BAD_OBJECT_ID) { // // An error occurred in converting the objectID // TRACE_OUT(("Error converting object ID to handle")); DC_QUIT; } } // // The object is not locked (or the lock user has left the call) // TRACE_OUT(("Graphic is not locked")); DC_EXIT_POINT: // // If the graphic is still held by us, release it // if (pGraphic != NULL) { WBP_GraphicRelease(hPage, hGraphic, pGraphic); } DebugExitBOOL(wbGraphicLocked, result); return(result); } // // // Name: wbAddLocalUserObject // // Purpose: Add an object to the user information workset for the local // user. // // Returns: Error code // // UINT WbClient::wbAddLocalUserObject(void) { UINT rc; POM_OBJECTDATA pData; PWB_PERSON pUser; CM_STATUS cmStatus; DebugEntry(wbAddLocalUserObject); TRACE_OUT(("Adding the necessary control objects")); // // Build a user object for this user and write it to the User Information // Workset. // rc = OM_ObjectAlloc(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, sizeof(WB_PERSON), &pData); if (rc != 0) { ERROR_OUT(("Error allocating object = %d", rc)); DC_QUIT; } pData->length = sizeof(WB_PERSON); pUser = (PWB_PERSON) (pData->data); // // Initialize the contents of the user object for this user // TRACE_OUT(("Initialising user contents")); ZeroMemory(pUser, sizeof(WB_PERSON)); pUser->currentPage = FIRST_PAGE_WORKSET; // lonchanc: it was 1. pUser->synced = FALSE; pUser->pointerActive = FALSE; pUser->pointerPage = FIRST_PAGE_WORKSET; // lonchanc: it was 1. pUser->colorId = (TSHR_UINT16)wbSelectPersonColor(); // // Fill in the Call Manager personID if we are in a call. // if (CMS_GetStatus(&cmStatus)) { TRACE_OUT(("CMG personID %u", cmStatus.localHandle)); pUser->cmgPersonID = cmStatus.localHandle; } else { pUser->cmgPersonID = 0; } // // Copy the user name into the object: // lstrcpy(pUser->personName, cmStatus.localName); // // Copy the person's color into the client's data // m_colorId = pUser->colorId; // // Add the object to the User Information Workset, saving the handle of // the user object in the client details. // rc = OM_ObjectAdd(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, &pData, WB_PERSON_OBJECT_UPDATE_SIZE, &m_pObjLocal, LAST); if (rc != 0) { // // The add failed, we must discard the object // OM_ObjectDiscard(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, &pData); ERROR_OUT(("Error adding user object = %d", rc)); DC_QUIT; } // // Save the ID of this user in the client details (for later use in the // lock information). // OM_ObjectPtrToID(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, m_pObjLocal, &(m_personID)); DC_EXIT_POINT: DebugExitDWORD(wbAddLocalUserObject, rc); return(rc); } // // // Name: wbGetEmptyPageHandle // // Purpose: Return a handle for a page that does not have its workset open. // // Returns: Handle of free page (or 0 if none exists) // // WB_PAGE_HANDLE WbClient::wbGetEmptyPageHandle(void) { UINT index; WB_PAGE_HANDLE hPage = WB_PAGE_HANDLE_NULL; PWB_PAGE_STATE pPageState = m_pageStates; // // Search the page list for an empty entry // for (index = 0; index < WB_MAX_PAGES; index++, pPageState++) { if ( (pPageState->state == PAGE_NOT_IN_USE) && (pPageState->subState == PAGE_STATE_EMPTY)) { hPage = PAGE_INDEX_TO_HANDLE(index); break; } } return(hPage); } // // // Name: wbGetReadyPageHandle // // Purpose: Return a handle for a page that has its workset open but is not // currently in use. // // Returns: Handle of free page (or 0 if none exists) // // WB_PAGE_HANDLE WbClient::wbGetReadyPageHandle(void) { UINT index; WB_PAGE_HANDLE hPage = WB_PAGE_HANDLE_NULL; PWB_PAGE_STATE pPageState = m_pageStates; // // Search the page list for a ready entry // for (index = 0; index < WB_MAX_PAGES; index++, pPageState++) { if ( (pPageState->state == PAGE_NOT_IN_USE) && (pPageState->subState == PAGE_STATE_READY)) { hPage = PAGE_INDEX_TO_HANDLE(index); break; } } return(hPage); } // // // Name: wbPageOrderPageNumber // // Purpose: Return the number of the specified page. // This function performs no validation on its parameters. // // Returns: None // // UINT WbClient::wbPageOrderPageNumber ( PWB_PAGE_ORDER pPageOrder, WB_PAGE_HANDLE hPage ) { UINT index; POM_WORKSET_ID pPage = pPageOrder->pages; DebugEntry(wbPageOrderPageNumber); // // Search the page order list for the page handle (workset ID) // for (index = 0; index <= pPageOrder->countPages; index++) { if (pPage[index] == (OM_WORKSET_ID)hPage) { DC_QUIT; } } // // The page was not found - this is an internal error // ERROR_OUT(("Page handle not found")); // // Return the page number starting from 1. // DC_EXIT_POINT: DebugExitDWORD(wbPageOrderPageNumber, index + 1); return(index + 1); } // // // Name: wbPageOrderPageAdd // // Purpose: Add a new page to a page order structure. This function expects // the parameters to be valid - they must be checked before // calling it. It also assumes that there is space in the page // list for the new page. // // Params: pPageOrder - pointer to page list // hRefPage - page used as a reference point for the new page // hPage - handle of the page to be added // where - relative position - before or after hRefPage // // Returns: None // // void WbClient::wbPageOrderPageAdd ( PWB_PAGE_ORDER pPageOrder, WB_PAGE_HANDLE hRefPage, WB_PAGE_HANDLE hPage, UINT where ) { UINT index; POM_WORKSET_ID pPage = pPageOrder->pages; DebugEntry(wbPageOrderPageAdd); // // Process according to the add position // switch(where) { case PAGE_FIRST: index = 0; if (pPageOrder->countPages != 0) { UT_MoveMemory(&pPage[1], &pPage[0], pPageOrder->countPages*sizeof(pPage[0])); } break; case PAGE_LAST: index = pPageOrder->countPages; break; case PAGE_AFTER: case PAGE_BEFORE: // // Make an empty space in the page order list // index = wbPageOrderPageNumber(pPageOrder, hRefPage); if (where == PAGE_BEFORE) { index--; } UT_MoveMemory(&pPage[index + 1], &pPage[index], (pPageOrder->countPages - index)*sizeof(pPage[0])); break; default: ERROR_OUT(("Bad where parameter")); DC_QUIT; } // // Save the new page handle in the list // pPage[index] = hPage; // // Show that the extra page is now present // pPageOrder->countPages += 1; DC_EXIT_POINT: DebugExitVOID(wbPageOrderPageAdd); } // // // Name: wbPageOrderPageDelete // // Purpose: Remove the specified page from a page order structure. This // function expects its the parameters to be valid - they must be // checked before calling it. // // Returns: None // // void WbClient::wbPageOrderPageDelete ( PWB_PAGE_ORDER pPageOrder, WB_PAGE_HANDLE hPage ) { UINT index; POM_WORKSET_ID pPage = pPageOrder->pages; DebugEntry(wbPageOrderPageDelete); index = wbPageOrderPageNumber(pPageOrder, hPage); UT_MoveMemory(&pPage[index - 1], &pPage[index], (pPageOrder->countPages - index)*sizeof(pPage[0])); pPageOrder->countPages -= 1; DebugExitVOID(wbPageOrderPageDelete); } // // // Name: wbPagesPageAdd // // Purpose: Add a new page to the internal page list. This function expects // the parameters to be valid - they must be checked before // calling it. It also assumes that there is space in the page // list for the new page. // // Returns: None // // void WbClient::wbPagesPageAdd ( WB_PAGE_HANDLE hRefPage, WB_PAGE_HANDLE hPage, UINT where ) { PWB_PAGE_STATE pPageState; DebugEntry(wbPagesPageAdd); // // Add the page to the page order structure // wbPageOrderPageAdd(&(m_pageOrder), hRefPage, hPage, where); // // Update the page state information // pPageState = GetPageState(hPage); pPageState->state = PAGE_IN_USE; pPageState->subState = PAGE_STATE_EMPTY; DebugExitVOID(wbPagesPageAdd); } // // // Name: wbClientReset // // Purpose: Reset the client data to a state where the client is not in a // call, but is registered with ObMan and has event and exit // handlers registered with utilities. // // Returns: None // // void WbClient::wbClientReset(void) { UINT index; PWB_PAGE_ORDER pPageOrder = &(m_pageOrder); PWB_PAGE_STATE pPageState = m_pageStates; DebugEntry(wbClientReset); // // Initialize object handles // m_hWSGroup = (OM_WSGROUP_HANDLE) NULL; m_pObjPageControl = NULL; m_pObjSyncControl = NULL; m_pObjLocal = NULL; m_pObjLock = NULL; m_pObjPersonLock = NULL; // // Initialize the status variables // m_errorState = ERROR_STATE_EMPTY; m_changed = FALSE; m_lockState = LOCK_STATE_EMPTY; m_lockType = WB_LOCK_TYPE_NONE; m_lockRequestType = WB_LOCK_TYPE_NONE; m_loadState = LOAD_STATE_EMPTY; if (m_hLoadFile != INVALID_HANDLE_VALUE) { CloseHandle(m_hLoadFile); m_hLoadFile = INVALID_HANDLE_VALUE; } m_countReadyPages = 0; // // Zero the whole structure // ZeroMemory(pPageOrder, sizeof(*pPageOrder)); // // Set the object type // pPageOrder->objectType = TYPE_CONTROL_PAGE_ORDER; // // Set up the page control elements // pPageOrder->generationLo = 1; pPageOrder->generationHi = 0; pPageOrder->countPages = 0; // // Initialize the page state structures // for (index = 0; index < WB_MAX_PAGES; index++, pPageState++) { pPageState->state = PAGE_NOT_IN_USE; pPageState->subState = PAGE_STATE_EMPTY; } DebugExitVOID(wbClientReset); } // // // Name: wbOnWsGroupRegisterCon // // Purpose: Routine processing OM_WSGROUP_REGISTER_CON events. // // Returns: Error code // // BOOL WbClient::wbOnWsGroupRegisterCon ( UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; BOOL processed; DebugEntry(wbOnWsGroupRegisterCon); // // Check that this is the event we are expecting // if (pEvent32->correlator != m_wsgroupCorrelator) { // // We are not expecting this event, this means that it must be for a // workset group which we wanted to deregister from (but had not yet // received confirmation). So deregister immediately. // // // Check that the return code for the registration is OK // if (pEvent32->result == 0) { OM_WSGroupDeregister(m_pomClient, &(pEvent16->hWSGroup)); } processed = FALSE; DC_QUIT; } // // Show that we have processed the event // processed = TRUE; // // Test for the correct state // if (m_subState != STATE_REG_PENDING_WSGROUP_CON) { // // We are not in the correct state for this event - this is an internal // error. // ERROR_OUT(("Not in correct state for WSGroupRegisterCon")); DC_QUIT; } // // Check that the return code for the registration is OK // if (pEvent32->result != 0) { // // Registration with the workset group failed - tidy up // wbError(); DC_QUIT; } // // Registration with the workset group succeeded // m_hWSGroup = pEvent16->hWSGroup; // // Get the clients network ID, used in graphic objects to determine where // they are loaded. // if (!wbGetNetUserID()) { // // Tidy up (and post an error event to the client) // ERROR_OUT(("Failed to get user ID")); wbError(); DC_QUIT; } // // Start opening the worksets. We open them one at a time and wait for // the response to avoid flooding the message queue. // The user information workset is given high priority. This allows // remote pointer movements to travel quickly. // if (OM_WorksetOpenPReq(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, NET_HIGH_PRIORITY, TRUE, &(m_worksetOpenCorrelator)) != 0) { ERROR_OUT(("User Information Workset Open Failed")); wbError(); DC_QUIT; } // // Move to the next state // m_subState = STATE_REG_PENDING_USER_WORKSET; DC_EXIT_POINT: DebugExitBOOL(wbOnWsGroupRegisterCon, processed); return(processed); } // // // Name: wbOnWorksetOpenCon // // Purpose: Routine processing OM_WORKSET_OPEN_CON events. // // Returns: Error code // // BOOL WbClient::wbOnWorksetOpenCon ( UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; BOOL processed = FALSE; OM_WORKSET_ID eventWorksetID; DebugEntry(wbOnWorksetOpenCon); // // Process according to the workset ID // eventWorksetID = pEvent16->worksetID; // // If the event is for a page workset // if (eventWorksetID >= FIRST_PAGE_WORKSET) { // // We are opening a page workset // processed = wbOnPageWorksetOpenCon(param1, param2); if (!processed) { DC_QUIT; } } // // We are done if this is a page workset other than the 1st page workset // if (eventWorksetID > FIRST_PAGE_WORKSET) { DC_QUIT; } // // Now check if it is one of the control worksets (the first page workset // is both a control workset and a page workset). // if (eventWorksetID != FIRST_PAGE_WORKSET) { // // Check the message correlator // if (pEvent32->correlator != m_worksetOpenCorrelator) { TRACE_OUT(("Correlators do not match - quitting")); DC_QUIT; } } // // We are opening a control workset - process the event // wbOnControlWorksetOpenCon(param1, param2); processed = TRUE; DC_EXIT_POINT: DebugExitBOOL(wbOnWorksetOpenCon, processed); return(processed); } // // // Name: wbOnControlWorksetOpenCon // // Purpose: Routine processing OM_WORKSET_OPEN_CON events for control // worksets. // // Returns: Error code // // void WbClient::wbOnControlWorksetOpenCon ( UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; UINT rc; OM_WORKSET_ID eventId; DebugEntry(wbOnControlWorksetOpenCon); // // Check the return code in the open // if (pEvent32->result != 0) { ERROR_OUT(("Error reported on workset open = %d", pEvent32->result)); wbError(); DC_QUIT; } // // If we are in registration, we are opening the required worksets - // continue the process. // if (m_state > STATE_REGISTERING) { ERROR_OUT(("Control workset open con after registration")); } // // Set up for opening the next workset // eventId = pEvent16->worksetID; switch(eventId) { case USER_INFORMATION_WORKSET: // // The user information workset is given high priority. This allows // remote pointer movements to travel quickly. // TRACE_OUT(("Opening Page Control workset")); rc = OM_WorksetOpenPReq(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, NET_HIGH_PRIORITY, FALSE, &(m_worksetOpenCorrelator)); m_subState = STATE_REG_PENDING_WORKSET_OPEN; break; case PAGE_CONTROL_WORKSET: // // The sync control workset is given high priority to allow sync // updates to travel quickly. // TRACE_OUT(("Opening Sync Control workset")); rc = OM_WorksetOpenPReq(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, NET_HIGH_PRIORITY, FALSE, &(m_worksetOpenCorrelator)); break; case SYNC_CONTROL_WORKSET: // // Open the first of the page worksets - we must do this to allow us // to use it as the only page available if we are the first person in // the call. // TRACE_OUT(("Opening first page workset")); rc = wbPageWorksetOpen((WB_PAGE_HANDLE)FIRST_PAGE_WORKSET, OPEN_LOCAL); break; case FIRST_PAGE_WORKSET: break; default: ERROR_OUT(("Bad workset ID")); break; } // // Check whether we have just opened another workset // if (eventId != FIRST_PAGE_WORKSET) { // // Test the return code from the open // if (rc != 0) { ERROR_OUT(("Workset open failed = %d", rc)); wbError(); } DC_QUIT; } // // We have now opened all the control worksets. We now add the required // control objects. // rc = wbAddLocalUserObject(); if (rc != 0) { // // Stop the join call process, tidy up and send an error message to the // client. // wbError(); DC_QUIT; } m_subState = STATE_REG_USER_OBJECT_ADDED; TRACE_OUT(("Moved to substate STATE_REG_USER_OBJECT_ADDED")); // // Check whether the Page Control objects are available yet (they could // have been added by another user in the call). // TRACE_OUT(("%x PAGE WS object, %x SYNC WS object", m_pObjPageControl, m_pObjSyncControl)); if ( (m_pObjPageControl == 0) && (m_pObjSyncControl == 0)) { TRACE_OUT(("No control objects - WE MIGHT BE FIRST IN CALL - get lock")); // // We may be the first user to register - request the lock on the Page // Control Workset. // rc = wbLock(WB_LOCK_TYPE_PAGE_ORDER); if (rc != 0) { ERROR_OUT(("Error from wbLock = %d", rc)); wbError(); DC_QUIT; } // // Set the new registration state // m_subState = STATE_REG_PENDING_LOCK; TRACE_OUT(("Moved to substate STATE_REG_PENDING_LOCK")); DC_QUIT; } else { if (m_pObjSyncControl == 0) { TRACE_OUT(("Waiting for sync control")); m_subState = STATE_REG_PENDING_SYNC_CONTROL; DC_QUIT; } if (m_pObjPageControl == 0) { TRACE_OUT(("Waiting for page control")); m_subState = STATE_REG_PENDING_PAGE_CONTROL; DC_QUIT; } } // // Complete registration // TRACE_OUT(("Page Control and Sync Control objects both there.")); TRACE_OUT(("Registration can be completed")); wbOnControlWorksetsReady(); DC_EXIT_POINT: DebugExitVOID(wbOnControlWorksetOpenCon); } // // // Name: wbPageWorksetOpen // // Purpose: Open a page workset // // Returns: Error code // // UINT WbClient::wbPageWorksetOpen ( WB_PAGE_HANDLE hPage, UINT localOrExternal ) { UINT result; PWB_PAGE_STATE pPageState; DebugEntry(wbPageWorksetOpen); // // Get the page state // pPageState = GetPageState(hPage); ASSERT((pPageState->state == PAGE_NOT_IN_USE)); ASSERT((pPageState->subState == PAGE_STATE_EMPTY)); // // Open the workset. We allow ObMan to choose the priority, this means // that ObMan uses a variable priority scheme allowing small objects to // overtake large ones. // result = OM_WorksetOpenPReq(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, OM_OBMAN_CHOOSES_PRIORITY, FALSE, &(pPageState->worksetOpenCorrelator)); if (result != 0) { ERROR_OUT(("WorksetOpen failed = %d", result)); DC_QUIT; } // // Update the page state // if (localOrExternal == OPEN_LOCAL) { pPageState->subState = PAGE_STATE_LOCAL_OPEN_CONFIRM; TRACE_OUT(("Moved page %d state to PAGE_STATE_PENDING_OPEN_CONFIRM", (UINT) hPage)); } else { pPageState->subState = PAGE_STATE_EXTERNAL_OPEN_CONFIRM; TRACE_OUT(("Moved page %d state to PAGE_STATE_PENDING_OPEN_CONFIRM", (UINT) hPage)); } DC_EXIT_POINT: DebugExitDWORD(wbPageWorksetOpen, result); return(result); } // // // Name: wbOnPageWorksetOpenCon // // Purpose: Routine processing OM_WORKSET_OPEN_CON events for page worksets // // Returns: Error code // // BOOL WbClient::wbOnPageWorksetOpenCon ( UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; BOOL processed = FALSE; OM_WORKSET_ID eventId; PWB_PAGE_STATE pPageState; WB_PAGE_HANDLE hPage; UINT oldState; DebugEntry(wbOnPageWorksetOpenCon); // // Get the page state pointer // eventId = pEvent16->worksetID; hPage = (WB_PAGE_HANDLE)eventId; pPageState = GetPageState(hPage); // // Check the message correlator // if (pEvent32->correlator != pPageState->worksetOpenCorrelator) { TRACE_OUT(("Correlators do not match - quitting")); DC_QUIT; } // // Show that we have processed this event // processed = TRUE; // // Check the return code in the open // if (pEvent32->result != 0) { ERROR_OUT(("Error reported on page workset open = %d", pEvent32->result)); pPageState->subState = PAGE_STATE_EMPTY; TRACE_OUT(("Moved page %d substate to PAGE_STATE_EMPTY", (UINT)hPage)); DC_QUIT; } // // Update the page state to indicate that the page is now ready for use // oldState = pPageState->subState; pPageState->subState = PAGE_STATE_READY; TRACE_OUT(("Moved page %d to substate to PAGE_STATE_READY", (UINT)hPage)); switch (oldState) { case PAGE_STATE_LOCAL_OPEN_CONFIRM: // // This workset was opened locally, therefore it is being opened as // part of the workset cache. Nothing more to do. // break; case PAGE_STATE_EXTERNAL_OPEN_CONFIRM: // // This workset was opened as a result of external updates to the // Page Control object. We therefore need to add the page to the // page list now that the workset is open. We no longer know where // the page is to be added - so call the main Page Control update // routine again to get all the information. // wbProcessPageControlChanges(); break; default: ERROR_OUT(("Bad page state %d", pPageState->subState)); break; } // // Increment the number of pages in ready state. This count is never // decremented - once a workset is open it stays open. // m_countReadyPages += 1; // // If we are in registration and are waiting for the cache of ready // pages, we must complete registration now. // if ( (m_state == STATE_REGISTERING) && (m_subState == STATE_REG_PENDING_READY_PAGES) ) { // // If there are enough pages in the cache // if (wbCheckReadyPages()) { // // We have enough ready pages - complete registration // wbCompleteRegistration(); DC_QUIT; } // // There are not yet enough pages in the cache. CheckReadyPages will // have made a new workset open request, so we will receive another // workset open confirm soon. // DC_QUIT; } DC_EXIT_POINT: DebugExitBOOL(wbOnPageWorksetOpenCon, processed); return(processed); } // // // Name: wbOnWorksetLockCon // // Purpose: Routine processing OM_WORKSET_LOCK_CON events. // // Returns: Error code // // BOOL WbClient::wbOnWorksetLockCon ( UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; BOOL processed = FALSE; UINT rc; DebugEntry(wbOnWorksetLockCon); // // Check the message correlator // if (pEvent32->correlator != m_lockCorrelator) { DC_QUIT; } // // The message is for us - set the result to "processed" // processed = TRUE; // // Check that the event is for the Page Control Workset (this is the // only expected workset). // if (pEvent16->worksetID != PAGE_CONTROL_WORKSET) { ERROR_OUT(("Unexpected workset in LockCon = %d", pEvent16->worksetID)); } // // Process according to the current lock state // switch (m_lockState) { // // We were waiting for lock confirmation // case LOCK_STATE_PENDING_LOCK: // // Check the return code in the event // if (pEvent32->result != 0) { TRACE_OUT(("Posting WBP_EVENT_LOCK_FAILED, rc %d", pEvent32->result)); WBP_PostEvent(0, // No delay WBP_EVENT_LOCK_FAILED, // Lock request failed 0, // No parameters 0); // // The lock failed - update the state. This means that // another user has acquired the lock. We expect to get a // lock object add indication soon. // m_lockState = LOCK_STATE_EMPTY; TRACE_OUT(("Lock request failed - lock state is now EMPTY")); DC_QUIT; } // // Write the lock details to the Page Control Workset // rc = wbWriteLock(); if (rc != 0) { ERROR_OUT(("Unable to write lock details = %d", rc)); // // Tidy up by unlocking the Page Control Workset // OM_WorksetUnlock(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET); // // Tell the client of the failure // TRACE_OUT(("Posting WBP_EVENT_LOCK_FAILED")); WBP_PostEvent(0, // No delay WBP_EVENT_LOCK_FAILED, // Lock request failed 0, // No parameters 0); // // Update the lock state // m_lockState = LOCK_STATE_EMPTY; TRACE_OUT(("Moved lock state to LOCK_STATE_EMPTY")); DC_QUIT; } // // Once we get here the write of the lock object above will // trigger an object add event that completes the lock // processing. // m_lockState = LOCK_STATE_PENDING_ADD; TRACE_OUT(("Moved lock state to LOCK_STATE_PENDING_ADD")); break; // // The application has cancelled the lock request before it has had // time to complete - tidy up. // case LOCK_STATE_CANCEL_LOCK: TRACE_OUT(("LOCK_STATE_CANCEL_LOCK")); // // If the request failed, just reset the state. // // // The lock was cancelled - unlock the workset if necessary, // and notify the front-end of the unlock. // if (pEvent32->result == 0) { // // We have locked the workset successfully, but in the // meantime the front-end has cancelled the lock, so unlock // the workset now. // TRACE_OUT(( "Lock cancelled before workset locked, so unlock now")); OM_WorksetUnlock(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET); } m_lockState = LOCK_STATE_EMPTY; // // Tell the app that we have cancelled the lock. // TRACE_OUT(("Posting WBP_EVENT_UNLOCKED")); WBP_PostEvent(0, WBP_EVENT_UNLOCKED, 0, 0); break; // // Another has got in before us // case LOCK_STATE_LOCKED_OUT: // // We have received a lock confirmation and should have been // expecting the lock. But we are locked out. This means that // another user has got in just before us, acquired the lock // and added the lock object. We have processed the add and // changed the lock state accordingly. This lock confirmation // will therefore normally be a failure. If by some fluke it // isn't, then we treat it as a failure for safety. // if (pEvent32->result == 0) { ERROR_OUT(("Lock violation")); // // Tidy up by unlocking the Page Control Workset - leave // the state as LOCKED_OUT; we'll clear it on receipt of // the unlock (either local, or from the locking user). // OM_WorksetUnlock(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET); } break; default: ERROR_OUT(("Bad lock state %d", m_lockState)); break; } // Switch on lock state DC_EXIT_POINT: DebugExitBOOL(wbOnWorksetLockCon, processed); return(processed); } // // // Name: wbOnWorksetUnlockInd // // Purpose: Routine processing OM_WORKSET_UNLOCK_IND events. // // Returns: Error code // // BOOL WbClient::wbOnWorksetUnlockInd ( UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; BOOL processed = TRUE; DebugEntry(wbOnWorksetUnlockInd); // // We are only interested if the workset id is that of the Page Control // Workset. // if (pEvent16->worksetID != PAGE_CONTROL_WORKSET) { TRACE_OUT(("Unexpected workset in unlock = %d", pEvent16->worksetID)); DC_QUIT; } switch (m_lockState) { // // We had the lock and are waiting to unlock or another user had // the lock and has now removed it. // case LOCK_STATE_LOCKED_OUT: // // We received the unlock of the workset before the removal of // the lock object; we just ignore this, since the deletion of // the lock object is our indication that the wb lock is removed. // TRACE_OUT(("Unlock of page control workset while locked out")); break; // // We are unlocking after an error acquiring the lock or after a // user has cancelled alock before we had time to complete it. // case LOCK_STATE_CANCEL_LOCK: // // An error occurred in getting the lock - the client has // already been informed so we just record the state change. // m_lockState = LOCK_STATE_EMPTY; TRACE_OUT(("Moved lock state to LOCK_STATE_EMPTY")); break; // // We are waiting for the lock - but have got an unlock instead. // This could be from another user, or from previous aborted // attempts by us to get the lock. We ignore the event and wait // for our lock confirmation. // case LOCK_STATE_PENDING_LOCK: TRACE_OUT(( "Got unlock indication while waiting for lock confirmation")); break; // // We can get an unlock indication without ever having seen the // lock object if the lock object was never added (failure at // another user) or if ObMan has spoiled the add and delete. // case LOCK_STATE_EMPTY: TRACE_OUT(("Unlock received in LOCK_STATE_EMPTY - ignoring")); break; // // Unlock not expected in this state // default: ERROR_OUT(("Bad lock state %d", m_lockState)); break; } DC_EXIT_POINT: DebugExitBOOL(wbOnWorksetUnlockInd, processed); return(processed); } // // // Name: wbOnControlWorksetsReady // // Purpose: The control worksets have been opened and set up. Continue the // registration process by updating the internal page order to // ensure that in matches the external order. // // Returns: Error code // // void WbClient::wbOnControlWorksetsReady(void) { DebugEntry(wbOnControlWorksetsReady); // // Read the Page Control object and compare its content to the internal // Page Order. // wbProcessPageControlChanges(); // // Update the state to show that we are waiting for the Page Order // Updated event indicating that the internal page order now matches // the external order. // m_subState = STATE_REG_PENDING_PAGE_ORDER; TRACE_OUT(("Moved sub state to STATE_REG_PENDING_PAGE_ORDER")); DebugExitVOID(wbOnControlWorksetsReady); } // // // Name: wbCompleteRegistration // // Purpose: Perform the final steps in registering a client. These are: // post a WB_EVENT_REGISTERED event to the client; check if // another user has a lock on the contents or page order, if so, // post a WB_EVENT_CONTENTS_LOCKED or WB_EVENT_PAGE_ORDER_LOCKED // to the client. // // Returns: Error code // // void WbClient::wbCompleteRegistration(void) { DebugEntry(wbCompleteRegistration); // // Inform the client that we are fully registered // TRACE_OUT(("Posting WBP_EVENT_REGISTER_OK")); WBP_PostEvent(0, // No delay WBP_EVENT_JOIN_CALL_OK, // Fully registered 0, // No parameters 0); // // Notify the client of the lock status // wbSendLockNotification(); // // Record that we are now fully registered // m_state = STATE_IDLE; m_subState = STATE_EMPTY; TRACE_OUT(("Moved to state STATE_IDLE")); DebugExitVOID(wbCompleteRegistration); } // // // Name: wbLeaveCall // // Purpose: Remove a client from a call/workset group // // Returns: None // // void WbClient::wbLeaveCall(void) { DebugEntry(wbLeaveCall); // // If we have not got far enough to have entered a call - leave now // (there is nothing to tidy up). // if (m_state < STATE_REGISTERING) { DC_QUIT; } // // If we have the lock - delete the lock object (the workset will be // unlocked by ObMan when we deregister). // if (m_lockState == LOCK_STATE_GOT_LOCK) { TRACE_OUT(("Still got lock - deleting lock object, handle %d", m_pObjLock)); if (OM_ObjectDelete(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, m_pObjLock) != 0) { ERROR_OUT(("Error deleting lock object")); } // // If all is well at this point the unlock process will be // completed when the object delete ind is received. // m_lockState = LOCK_STATE_PENDING_DELETE; TRACE_OUT(("Moved to state LOCK_STATE_PENDING_DELETE")); } // // Fix up the sub state to indicate that all registration actions have // been completed (to ensure that they are all undone). // if (m_state > STATE_REGISTERING) { m_subState = STATE_REG_END; TRACE_OUT(("Moved to substate STATE_REG_END")); } // // Delete the user object representing the local user from the User // Information Workset (if it is present). Note that we are about to // deregister from ObMan - this acts as automatic confirmation of the // delete request so we do not need to wait for the // OM_OBJECT_DELETE_IND event. // if (m_subState >= STATE_REG_USER_OBJECT_ADDED) { TRACE_OUT(("Deleting user object")); if (OM_ObjectDelete(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, m_pObjLocal) != 0) { // // Trace the error but do not quit - we expect everything to be // tidied up when we deregister from ObMan. // ERROR_OUT(("Error deleting local user object")); } } // // If we have already registered with the Workset Group, deregister // now. we have not yet received the confirmation, and get it later we // will deregister immediately. // if (m_subState > STATE_REG_PENDING_WSGROUP_CON) { OM_WSGroupDeregister(m_pomClient, &(m_hWSGroup)); } else { // // We have not yet received the Workset Group Registration // confirmation, change the value in the correlator field so that // we recognize the fact that we have cancelled registration later. // m_wsgroupCorrelator--; } // // Reset the handles of objects added during registration // TRACE_OUT(("Resetting client data")); wbClientReset(); // // Set the client state to the appropriate value // m_state = STATE_STARTED; m_subState = STATE_STARTED_START; TRACE_OUT(("Moved state to STATE_STARTED")); DC_EXIT_POINT: DebugExitVOID(wbLeaveCall); } // // // Name: wbContentsDelete // // Purpose: Remove all the current graphics and pages, leaving a single // blank page. // // Returns: None // // void WbClient::wbContentsDelete ( UINT changedFlagAction ) { PWB_PAGE_ORDER pPageOrder = &(m_pageOrder); PWB_PAGE_STATE pPageState; UINT index; DebugEntry(wbContentsDelete); // // Just clear the first page in the list // wbPageClear(pPageOrder->pages[0], changedFlagAction); // // If there is only one page left in the list - we're done. // if (pPageOrder->countPages == 1) { DC_QUIT; } // // There is more than one page // // // Mark all of the active pages (except the first) as "delete pending" // for (index = 1; index < pPageOrder->countPages; index++) { pPageState = GetPageState((pPageOrder->pages)[index]); if ((pPageState->state == PAGE_IN_USE) && (pPageState->subState == PAGE_STATE_EMPTY)) { pPageState->subState = PAGE_STATE_LOCAL_DELETE; } } // // Write the page control information. The replace event generated by // the write will kick off the actual deletion of the pages marked. // wbWritePageControl(FALSE); DC_EXIT_POINT: DebugExitVOID(wbContentsDelete); } // // // Name: wbStartContentsLoad // // Purpose: Start the loading of a file (after the contents have been // cleared). // // Returns: Error code // // void WbClient::wbStartContentsLoad(void) { DebugEntry(wbStartContentsLoad); // // Specify the first (and only) page handle as the page to load to // wbPageHandleFromNumber(1, &m_loadPageHandle); // // Update the load state to show that we are now loading // m_loadState = LOAD_STATE_LOADING; TRACE_OUT(("Moved load state to LOAD_STATE_LOADING")); // // Load the first page - subsequent pages are chained from this first one // wbPageLoad(); DebugExitVOID(wbStartContentsLoad); } // // // Name: wbLock // // Purpose: Request the lock for the Whiteboard contents or the page order // generating one of the following events: // // WB_EVENT_CONTENTS_LOCKED // WB_EVENT_CONTENTS_LOCK_FAILED. // // Returns: Error code // // UINT WbClient::wbLock(WB_LOCK_TYPE lockType) { UINT result = 0; OM_CORRELATOR correlator; DebugEntry(wbLock); // // If we already have the lock we can merely change its status // if (m_lockState == LOCK_STATE_GOT_LOCK) { TRACE_OUT(("Already got the lock")); m_lockRequestType = lockType; result = wbWriteLock(); DC_QUIT; } // // Request the lock for the Page Control Workset // result = OM_WorksetLockReq(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, &correlator); if (result != 0) { ERROR_OUT(("OM_WorksetLockReq failed, result = %d", result)); DC_QUIT; } TRACE_OUT(("Requested lock for the Page Control Workset")); // // Save the lock details // m_lockState = LOCK_STATE_PENDING_LOCK; m_lockCorrelator = correlator; m_lockRequestType = lockType; TRACE_OUT(("Moved lock state to LOCK_STATE_PENDING_LOCK")); TRACE_OUT(("Lock type requested = %d", lockType)); // // We return now, further processing is done when the OM_WORKSET_LOCK_CON // event is received. // DC_EXIT_POINT: DebugExitDWORD(wbLock, result); return(result); } // // // Name: wbUnlock // // Purpose: Unlock the Contents or Page Order. // // Returns: Error code // // void WbClient::wbUnlock(void) { DebugEntry(wbUnlock); // // Check that we have the lock // if (m_lockState != LOCK_STATE_GOT_LOCK) { ERROR_OUT(("Local person doesn't have lock")); DC_QUIT; } // // Delete the lock object // TRACE_OUT(("Delete Lock handle %x", m_pObjLock)); if (OM_ObjectDelete(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, m_pObjLock) != 0) { ERROR_OUT(("Could not delete lock object")); DC_QUIT; } // // If all is well at this point the unlock process will be completed when // the object delete ind is received. // m_lockState = LOCK_STATE_PENDING_DELETE; TRACE_OUT(("Moved to state LOCK_STATE_PENDING_DELETE")); DC_EXIT_POINT: DebugExitVOID(wbUnlock); } // // // Name: wbObjectSave // // Purpose: Save a structure to file // // Returns: Error code // // UINT WbClient::wbObjectSave ( HANDLE hFile, LPBYTE pData, UINT length ) { UINT result = 0; ULONG cbSizeWritten; DebugEntry(wbObjectSave); // // Save the length // if (! WriteFile(hFile, (void *) &length, sizeof(length), &cbSizeWritten, NULL)) { result = WB_RC_WRITE_FAILED; ERROR_OUT(("Error writing length to file, win32 err=%d", GetLastError())); DC_QUIT; } ASSERT(cbSizeWritten == sizeof(length)); // // Save the object data // if (! WriteFile(hFile, pData, length, &cbSizeWritten, NULL)) { result = WB_RC_WRITE_FAILED; ERROR_OUT(("Error writing data to file, win32 err=%d", GetLastError())); DC_QUIT; } ASSERT(cbSizeWritten == length); DC_EXIT_POINT: DebugExitDWORD(wbObjectSave, result); return result; } // // // Name: wbPageSave // // Purpose: Save the contents of a single page to file. // // Returns: Error code // // UINT WbClient::wbPageSave ( WB_PAGE_HANDLE hPage, HANDLE hFile ) { UINT result = 0; UINT rc; OM_WORKSET_ID worksetID = (OM_WORKSET_ID)hPage; POM_OBJECT pObj; POM_OBJECTDATA pData; WB_END_OF_PAGE endOfPage; DebugEntry(wbPageSave); // // Get the first object // result = OM_ObjectH(m_pomClient, m_hWSGroup, worksetID, 0, &pObj, FIRST); if (result == OM_RC_NO_SUCH_OBJECT) { // This can happen on an empty page, not an error TRACE_OUT(("No objects left, quitting with good return")); result = 0; DC_QUIT; } if (result != 0) { ERROR_OUT(("Error getting first object in page")); DC_QUIT; } // // Loop through the objects // for( ; ; ) { // // Get a pointer to the object // result = OM_ObjectRead(m_pomClient, m_hWSGroup, worksetID, pObj, &pData); if (result != 0) { ERROR_OUT(("Error reading object = %d", result)); DC_QUIT; } // // Save the object data // rc = wbObjectSave(hFile, (LPBYTE) pData->data, pData->length); // // The return code is tested after we have released the object because // we must always do the release. // // // Release the object // OM_ObjectRelease(m_pomClient, m_hWSGroup, worksetID, pObj, &pData); // // Now test the write return code // if (rc != 0) { result = rc; ERROR_OUT(("Error writing object data = %d", result)); DC_QUIT; } // // Get the next object // result = OM_ObjectH(m_pomClient, m_hWSGroup, worksetID, pObj, &pObj, AFTER); if (result == OM_RC_NO_SUCH_OBJECT) { TRACE_OUT(("No objects left, quitting with good return")); result = 0; DC_QUIT; } } DC_EXIT_POINT: // // If we have successfully written the page contents, we write an end-of- // page marker to the file. // if (result == 0) { // // Set the end of page object details // ZeroMemory(&endOfPage, sizeof(endOfPage)); endOfPage.length = sizeof(endOfPage); endOfPage.type = TYPE_END_OF_PAGE; // // Write the end-of-page object // result = wbObjectSave(hFile, (LPBYTE) &endOfPage, sizeof(endOfPage)); if (result != 0) { ERROR_OUT(("Error writing end-of-page = %d", result)); } } DebugExitDWORD(wbPageSave, result); return(result); } // // // Name: wbPageLoad // // Purpose: Load the contents of a single page from file. // // Returns: Error code // // void WbClient::wbPageLoad(void) { UINT result = 0; UINT type; POM_OBJECT pObj; POM_OBJECTDATA pData = NULL; PWB_GRAPHIC pGraphic = NULL; WB_PAGE_HANDLE hPage = m_loadPageHandle; WB_PAGE_HANDLE hNewPage; UINT postDelay = 0; DebugEntry(wbPageLoad); TRACE_OUT(("Entered wbPageLoad for page %d", (UINT) hPage)); // // Check the load state - if we're not loading, then quit (can happen if // the load is cancelled). // if (m_loadState == LOAD_STATE_EMPTY) { TRACE_OUT(("Load has been cancelled - abandoning page load")); DC_QUIT; } // // Check that we have a full complement of ready pages before starting // the load. // if (!wbCheckReadyPages()) { // // There are not enough pages worksets ready to be used. We exit now // to allow the page to be made ready before we continue. We set up a // delay on the message that will be used to restart the process to // allow the worksets to be opened before we get back in here. // postDelay = 200; DC_QUIT; } // // If we are waiting to add a new page, get the handle of the page we // expect to add next here. (We have to do this as ObMan requires that // we allocate memory for the object in the correct workset, but we do // not want to actually add the page here because we may not need it.) // if (m_loadState == LOAD_STATE_PENDING_NEW_PAGE) { hNewPage = wbGetReadyPageHandle(); // // If we cannot get a ready page - we must have run out of pages (we // have already done a check on the availability of ready pages above). // If we cannot get a new page we continue using the old. // if (hNewPage != WB_PAGE_HANDLE_NULL) { hPage = hNewPage; } } // // Read the next object // result = wbObjectLoad(m_hLoadFile, (OM_WORKSET_ID)hPage, &pGraphic); if (result != 0) { ERROR_OUT(("Error reading object = %d", result)); DC_QUIT; } pData = ObjectDataPtrFromGraphic(pGraphic); type = pGraphic->type; // // Process the object according to type // // // End of file marker // if (type == TYPE_END_OF_FILE) { // // Let the Front End know that the load has completed // TRACE_OUT(("Posting WBP_EVENT_LOAD_COMPLETE")); WBP_PostEvent( 0, // No delay WBP_EVENT_LOAD_COMPLETE, // Load completed 0, // No parameters 0); // // Leave now - the file will be closed below // DC_QUIT; } // // It is not an end-of file object. So it must be either an end-of page // or a graphic object. In either case we may already have flagged the // need to add a new page. // // // Add a new page (if necessary) // if (m_loadState == LOAD_STATE_PENDING_NEW_PAGE) { // // If we could not get a new page handle above leave with an error // if (hPage == m_loadPageHandle) { ERROR_OUT(("Run out of pages for load")); result = WB_RC_TOO_MANY_PAGES; DC_QUIT; } // // Add a new page after the current page. The new page handle is saved // in the client details. // result = wbPageAdd(m_loadPageHandle, PAGE_AFTER, &(m_loadPageHandle), DONT_RESET_CHANGED_FLAG); if (result != 0) { ERROR_OUT(("Failed to add page")); DC_QUIT; } // // Check that we got the page handle we expected // ASSERT((hPage == m_loadPageHandle)); // // Show that we are no longer waiting for a new page // m_loadState = LOAD_STATE_LOADING; } // // End of page marker // if (type == TYPE_END_OF_PAGE) { TRACE_OUT(("End of page object")); // // Discard the object // OM_ObjectDiscard(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, &pData); pData = NULL; // // Set the load state to "pending new page" and leave the routine // immediately. The process continues when we return to this routine. // m_loadState = LOAD_STATE_PENDING_NEW_PAGE; // // Exit (we post ourselves a message below to get us back into this // routine later). // postDelay = 100; DC_QUIT; } // // The object is a standard graphic // TRACE_OUT(("Graphic object")); // // Add the object to the page // result = OM_ObjectAdd(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, &pData, sizeof(WB_GRAPHIC), &pObj, LAST); if (result != 0) { DC_QUIT; } // // Show that we have finished with the object // pGraphic = NULL; pData = NULL; DC_EXIT_POINT: // // If we still have the object - discard it // if (pData != NULL) { TRACE_OUT(("Discarding object")); OM_ObjectDiscard(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage, &pData); } // // If an error occurred or we have reached the end-of-file - close the // file. // if ((result != 0) || (type == TYPE_END_OF_FILE)) { CloseHandle(m_hLoadFile); m_hLoadFile = INVALID_HANDLE_VALUE; // // If the final result is an error - post an error message to ourselves // if (result != 0) { TRACE_OUT(("Posting WBP_EVENT_LOAD_FAILED")); WBP_PostEvent( 0, // No delay WBP_EVENT_LOAD_FAILED, // Load the next object 0, // No parameters 0); } // // Record that we are no longer in the load process // m_loadState = LOAD_STATE_EMPTY; TRACE_OUT(("Moved load state to LOAD_STATE_EMPTY")); } // // send a message to load the next page, unless the load has been // cancelled // if (m_loadState != LOAD_STATE_EMPTY) { // // We have not reached the end-of-file and there has been no error. // Post a message to ourselves to continue the load process. // TRACE_OUT(("Posting WBPI_EVENT_LOAD_NEXT")); WBP_PostEvent(postDelay, // With delay WBPI_EVENT_LOAD_NEXT, // Load the next object 0, // No parameters 0); } DebugExitVOID(wbPageLoad); } // // // Name: wbObjectLoad // // Purpose: Load a single object from file. // // Returns: Error code // // UINT WbClient::wbObjectLoad ( HANDLE hFile, WB_PAGE_HANDLE hPage, PPWB_GRAPHIC ppGraphic ) { UINT result = 0; OM_WORKSET_ID worksetID = (OM_WORKSET_ID)hPage; UINT length; ULONG cbSizeRead; POM_OBJECTDATA pData = NULL; PWB_GRAPHIC pGraphic = NULL; DebugEntry(wbObjectLoad); TRACE_OUT(("Entered wbObjectLoad for page %d", (UINT) hPage)); // // Read the next object's length // if ( (! ReadFile(hFile, (void *) &length, sizeof(length), &cbSizeRead, NULL)) || (cbSizeRead != sizeof(length)) || (length > OM_MAX_OBJECT_SIZE) || (length == 0) ) { // // Make sure we return a sensible error. // ERROR_OUT(("reading object length, win32 err=%d, length=%d", GetLastError(), length)); result = WB_RC_BAD_FILE_FORMAT; DC_QUIT; } // // Allocate memory for the object // result = OM_ObjectAlloc(m_pomClient, m_hWSGroup, worksetID, length, &pData); if (result != 0) { ERROR_OUT(("Error allocating object = %d", result)); DC_QUIT; } pData->length = length; pGraphic = GraphicPtrFromObjectData(pData); // // Read the object into memory // if ( (! ReadFile(hFile, (void *) pGraphic, length, &cbSizeRead, NULL)) || (cbSizeRead != length)) { // // Make sure we return a sensible error. // ERROR_OUT(( "Reading object from file: win32 err=%d, asked for %d got %d bytes", GetLastError(), length, cbSizeRead)); result = WB_RC_BAD_FILE_FORMAT; DC_QUIT; } // // Validate the object type // switch (pGraphic->type) { // // Standard type, end-of-page or end-of-file // case TYPE_END_OF_PAGE: case TYPE_END_OF_FILE: case TYPE_GRAPHIC_FREEHAND: case TYPE_GRAPHIC_LINE: case TYPE_GRAPHIC_RECTANGLE: case TYPE_GRAPHIC_FILLED_RECTANGLE: case TYPE_GRAPHIC_ELLIPSE: case TYPE_GRAPHIC_FILLED_ELLIPSE: case TYPE_GRAPHIC_TEXT: case TYPE_GRAPHIC_DIB: break; // // Unrecognized object type - probably wrong version // default: result = WB_RC_BAD_FILE_FORMAT; DC_QUIT; break; } // // For graphic objects, set the flag in the object header showing that it // was loaded from file. Add our user ID so we know where it came from. // if ( (pGraphic->type != TYPE_END_OF_FILE) && (pGraphic->type != TYPE_END_OF_PAGE)) { pGraphic->loadedFromFile = TRUE; pGraphic->loadingClientID = m_clientNetID; } *ppGraphic = pGraphic; DC_EXIT_POINT: // // If an error has occurred - discard the object (if we have it) // if ((result != 0) && (pData != NULL)) { OM_ObjectDiscard(m_pomClient, m_hWSGroup, worksetID, &pData); } DebugExitDWORD(wbObjectLoad, result); return(result); } // // // Name: wbPageHandleFromNumber // // Purpose: Return the handle of a page specified by page number // // Returns: Error code // // UINT WbClient::wbPageHandleFromNumber ( UINT pageNumber, PWB_PAGE_HANDLE phPage ) { UINT result = 0; WB_PAGE_HANDLE hPage; PWB_PAGE_ORDER pPageOrder = &(m_pageOrder); DebugEntry(wbPageHandleFromNumber); // // Validate the requested page number // if ((pageNumber < 1)|| (pageNumber > WB_MAX_PAGES)) { result = WB_RC_BAD_PAGE_NUMBER; DC_QUIT; } if (pageNumber > pPageOrder->countPages) { result = WB_RC_NO_SUCH_PAGE; DC_QUIT; } // // Get the page handle // hPage = (pPageOrder->pages)[pageNumber - 1]; // // Check that this page is in use // if (GetPageState(hPage)->state != PAGE_IN_USE) { ERROR_OUT(("Page list is bad")); } // // Return the page handle // *phPage = hPage; DC_EXIT_POINT: DebugExitDWORD(wbPageHandleFromNumber, result); return(result); } // // // Name: wbPageClear // // Purpose: Clear the specified page of all graphic objects // // Returns: Error code // // UINT WbClient::wbPageClear ( WB_PAGE_HANDLE hPage, UINT changedFlagAction ) { UINT result = 0; DebugEntry(wbPageClear); // // Show that the contents have changed, if required. // if (changedFlagAction == RESET_CHANGED_FLAG) { m_changed = TRUE; TRACE_OUT(("Changed flag now TRUE")); } // // Request that the page be cleared // result = OM_WorksetClear(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage); DebugExitDWORD(wbPageClear, result); return(result); } // // // Name: wbPageClearConfirm // // Purpose: Complete the clearing of a page // // Returns: Error code // // void WbClient::wbPageClearConfirm(WB_PAGE_HANDLE hPage) { DebugEntry(wbPageClearConfirm); // // Request that the page be cleared // OM_WorksetClearConfirm(m_pomClient, m_hWSGroup, (OM_WORKSET_ID)hPage); // // Check the load state to see whether we are waiting to load the // contents // if (m_loadState == LOAD_STATE_PENDING_CLEAR) { // // We are waiting to load. If there is only one page available (ie the // one that has just been cleared) we are ready to load, otherwise we // wait for the page deletes to happen. // if ((m_pageOrder).countPages == 1) { // // Start the load proper // wbStartContentsLoad(); } else { // // Move the load state to show that we are waiting for all the pages // to be deleted. // m_loadState = LOAD_STATE_PENDING_DELETE; TRACE_OUT(("Moved load state to LOAD_STATE_PENDING_DELETE")); } } DebugExitVOID(wbPageClearConfirm); } // // // Name: wbCheckReadyPages // // Purpose: Check that we have enough worksets open for the local user to // use immediately (during page adds). // // Returns: None // // BOOL WbClient::wbCheckReadyPages(void) { BOOL bResult = TRUE; WB_PAGE_HANDLE hNewPage; UINT countPages = m_pageOrder.countPages; UINT countReadyPages = m_countReadyPages; // // If we have opened all the worksets // if (countReadyPages == WB_MAX_PAGES) { // // Quit there are no more worksets that we can open // DC_QUIT; } // // If the number of pages in use is getting close to the number of ready // pages. // if ( (countReadyPages >= PREINITIALIZE_PAGES) && (countPages <= (countReadyPages - PREINITIALIZE_PAGES))) { DC_QUIT; } // // If the number of pages ready is less than the required cache size, // open another one. // hNewPage = wbGetEmptyPageHandle(); if (hNewPage != WB_PAGE_HANDLE_NULL) { // // Open the workset associated with the page // wbPageWorksetOpen(hNewPage, OPEN_LOCAL); } bResult = FALSE; DC_EXIT_POINT: return(bResult); } // // // Name: wbPageAdd // // Purpose: Add a new (blank) page in a specified position // // Returns: Error code // // UINT WbClient::wbPageAdd ( WB_PAGE_HANDLE hRefPage, UINT where, PWB_PAGE_HANDLE phPage, UINT changedFlagAction ) { UINT result = 0; WB_PAGE_HANDLE hNewPage; DebugEntry(wbPageAdd); // // Check that there are not too many pages already // if (m_pageOrder.countPages == WB_MAX_PAGES) { result = WB_RC_TOO_MANY_PAGES; DC_QUIT; } // // Validate the specified reference page // ASSERT(GetPageState(hRefPage)->state == PAGE_IN_USE); // // Get a handle for the new page // hNewPage = wbGetReadyPageHandle(); // // If there are no handles ready we attempt to create one and return a // busy indication. // if (hNewPage == WB_PAGE_HANDLE_NULL) { result = WB_RC_BUSY; DC_QUIT; } // // Make the internal update immediately - this allows the client to // reference the new page as soon as this function has returned. // wbPagesPageAdd(hRefPage, hNewPage, where); // // Update the Page Control Object // result = wbWritePageControl(FALSE); if (result != 0) { wbError(); DC_QUIT; } // // Show that the contents have changed (if required). // if (changedFlagAction == RESET_CHANGED_FLAG) { m_changed = TRUE; TRACE_OUT(("Changed flag now TRUE")); } // // Return the handle of the new page // *phPage = hNewPage; DC_EXIT_POINT: // // If we successfully added the page, or could not get a spare page // handle, attempt to create a spare one for next time. // if ((result == 0) || (result == WB_RC_BUSY)) { wbCheckReadyPages(); } DebugExitDWORD(wbPageAdd, result); return(result); } // // // Name: wbPageMove // // Purpose: Move a page relative to another page // // Returns: Error code // // UINT WbClient::wbPageMove ( WB_PAGE_HANDLE hRefPage, WB_PAGE_HANDLE hPage, UINT where ) { UINT result = 0; PWB_PAGE_ORDER pPageOrder = &(m_pageOrder); DebugEntry(wbPageMove); // // Extract the page to be moved // wbPageOrderPageDelete(pPageOrder, hPage); // // Add it back at its new position // wbPageOrderPageAdd(pPageOrder, hRefPage, hPage, where); // // Update the page control object // result = wbWritePageControl(FALSE); if (result != 0) { wbError(); DC_QUIT; } // // Show that the contents have changed // m_changed = TRUE; TRACE_OUT(("Changed flag now TRUE")); DC_EXIT_POINT: DebugExitDWORD(wbPageMove, result); return(result); } // // // Name: wbPageHandle // // Purpose: Return a page handle. The page for which the handle is // required can be specified relative to another page or as the // first/last page. // // Returns: Error code // // UINT WbClient::wbPageHandle ( WB_PAGE_HANDLE hRefPage, UINT where, PWB_PAGE_HANDLE phPage ) { UINT result = 0; UINT pageNumber; PWB_PAGE_ORDER pPageOrder = &(m_pageOrder); POM_WORKSET_ID pPage = pPageOrder->pages; WB_PAGE_HANDLE hPage; DebugEntry(wbPageHandle); // // Check the relative position // switch (where) { case PAGE_FIRST: hPage = pPage[0]; break; case PAGE_LAST: hPage = pPage[pPageOrder->countPages - 1]; break; case PAGE_AFTER: case PAGE_BEFORE: // // Validate the specified reference page // ASSERT(GetPageState(hRefPage)->state == PAGE_IN_USE); // // Get the page number of the reference page // pageNumber = wbPageOrderPageNumber(pPageOrder, hRefPage); TRACE_OUT(("Reference page number is %d", pageNumber)); // // Get the page number of the required page // pageNumber = (UINT)(pageNumber + ((where == PAGE_AFTER) ? 1 : -1)); TRACE_OUT(("New page number is %d", pageNumber)); // // Check that the new page is valid // TRACE_OUT(("Number of pages is %d", pPageOrder->countPages)); if ( (pageNumber < 1) || (pageNumber > pPageOrder->countPages)) { TRACE_OUT(("Returning WB_RC_NO_SUCH_PAGE")); result = WB_RC_NO_SUCH_PAGE; DC_QUIT; } // // Get the handle of the page // hPage = pPage[pageNumber - 1]; TRACE_OUT(("Returning handle %d", (UINT) hPage)); break; } // // Return the page handle // *phPage = hPage; DC_EXIT_POINT: DebugExitDWORD(wbPageHandle, result); return(result); } // // // Name: wbGraphicSelectPrevious // // Purpose: Return the next graphic object in the specified page whose // bounding rectangle contains the specified point. The function // starts with the graphic whose handle is given as parameter and // will return this graphic if it contains the point. // // Returns: Error code // // UINT WbClient::wbGraphicSelectPrevious ( WB_PAGE_HANDLE hPage, LPPOINT pPoint, WB_GRAPHIC_HANDLE hGraphic, PWB_GRAPHIC_HANDLE phGraphic ) { UINT result = 0; OM_WORKSET_ID worksetID = (OM_WORKSET_ID)hPage; PWB_GRAPHIC pGraphic; POM_OBJECTDATA pData; RECT rect; DebugEntry(wbGraphicSelectPrevious); *phGraphic = (WB_GRAPHIC_HANDLE) NULL; // // Loop back through the objects starting at the reference point // do { // // Get the object from ObMan // result = OM_ObjectRead(m_pomClient, m_hWSGroup, worksetID, hGraphic, &pData); // // Leave the loop if error on read - we do not need to do the release // if (result != 0) { DC_QUIT; } pGraphic = GraphicPtrFromObjectData(pData); // // Extract the bounding rectangle of the object // RECT_FROM_TSHR_RECT16(&rect, pGraphic->rectBounds); // // Release the object // OM_ObjectRelease(m_pomClient, m_hWSGroup, worksetID, hGraphic, &pData); // // Check whether the point lies in bounds // if (PtInRect(&rect, *pPoint)) { // // Set the result handle // TRACE_OUT(("Returning graphic handle")); *phGraphic = hGraphic; DC_QUIT; } // // Get the next object to test // result = OM_ObjectH(m_pomClient, m_hWSGroup, worksetID, hGraphic, &hGraphic, BEFORE); } while (result == 0); // // Correct the return code (if necessary) // if (result == OM_RC_NO_SUCH_OBJECT) { TRACE_OUT(("Returning WB_RC_NO_SUCH_GRAPHIC")); result = WB_RC_NO_SUCH_GRAPHIC; } DC_EXIT_POINT: DebugExitDWORD(wbGraphicSelectPrevious, result); return(result); } // // // Name: wbCoreExitHandler // // Purpose: Exit handler for the Whiteboard Core. This handler is // registered with the Utilities by the WBP_Start call. It is // deregistered by the client deregistration process, so it is // only called when an abnormal termination occurs. // // Returns: None // // void CALLBACK wbCoreExitHandler(LPVOID clientData) { WbClient* pwbClient = (WbClient *)clientData; pwbClient->wbExitHandler(); } void WbClient::wbExitHandler(void) { DebugEntry(wbExitHandler); // // Leave the current call if there is one, removing any locks etc. // wbLeaveCall(); // // Dereg from call manager // if (m_pcmClient != NULL) { CMS_Deregister(&(m_pcmClient)); } // // Dereg exit handler // if (m_subState >= STATE_START_REGISTERED_EXIT) { UT_DeregisterExit(m_putTask, wbCoreExitHandler, this); } // // Dereg obman // if (m_subState >= STATE_START_REGISTERED_OM) { OM_Deregister(&m_pomClient); } // // Dereg event handler // if (m_subState >= STATE_START_REGISTERED_EVENT) { UT_DeregisterEvent(m_putTask, wbCoreEventHandler, this); } // // delete ourself! // delete this; DebugExitVOID(wbExitHandler); } // // // Name: wbCoreEventHandler // // Purpose: Event handler for the Whiteboard Core. This handler is // registered with the Utilities by the WBP_Start call. // // Params: clientData - pointer to the data stored for a client // event - event identifier // param1 - word event parameter (content depends on event) // param2 - long event parameter (content depends on event) // // Returns: Error code // // BOOL CALLBACK wbCoreEventHandler ( LPVOID clientData, UINT event, UINT_PTR param1, UINT_PTR param2 ) { WbClient* pwbClient = (WbClient *)clientData; return(pwbClient->wbEventHandler(event, param1, param2)); } BOOL WbClient::wbEventHandler ( UINT event, UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; BOOL processed = FALSE; DebugEntry(wbEventHandler); TRACE_OUT(("event %d, param1 %d, param2 %d", event, param1, param2)); switch (event) { // // Confirmation that we have registered with a workset group // case OM_WSGROUP_REGISTER_CON: TRACE_OUT(("OM_WSGROUP_REGISTER_CON %x %x",param1,param2)); processed = wbOnWsGroupRegisterCon(param1, param2); break; // // Confirmation that we have moved a workset group // case OM_WSGROUP_MOVE_CON: TRACE_OUT(("OM_WSGROUP_MOVE_CON %x %x",param1,param2)); processed = wbOnWsGroupMoveCon(param1, param2); break; // // Our workset group has been moved // case OM_WSGROUP_MOVE_IND: TRACE_OUT(("OM_WSGROUP_MOVE_IND %x %x",param1,param2)); processed = wbOnWsGroupMoveInd(param1, param2); break; // // A workset has been created - we do nothing // case OM_WORKSET_NEW_IND: TRACE_OUT(("OM_WORKSET_NEW_IND %x %x",param1,param2)); processed = TRUE; break; // // A workset has been opened // case OM_WORKSET_OPEN_CON: TRACE_OUT(("OM_WORKSET_OPEN_CON %x %x",param1,param2)); processed = wbOnWorksetOpenCon(param1, param2); break; // // A workset has been locked // case OM_WORKSET_LOCK_CON: TRACE_OUT(("OM_WORKSET_LOCK_CON %x %x",param1,param2)); processed = wbOnWorksetLockCon(param1, param2); break; // // A workset has been unlocked // case OM_WORKSET_UNLOCK_IND: TRACE_OUT(("OM_WORKSET_UNLOCK_IND %x %x",param1,param2)); processed = wbOnWorksetUnlockInd(param1, param2); break; // // ObMan has run out of resources // case OM_OUT_OF_RESOURCES_IND: TRACE_OUT(("OM_OUT_OF_RESOURCES_IND %x %x",param1,param2)); wbError(); processed = TRUE; break; // // A workset has been cleared // case OM_WORKSET_CLEAR_IND: TRACE_OUT(("OM_WORKSET_CLEAR_IND %x %x",param1,param2)); processed = wbOnWorksetClearInd(param1, param2); break; // // A new object has been added to a workset // case OM_OBJECT_ADD_IND: TRACE_OUT(("OM_OBJECT_ADD_IND %x %x",param1,param2)); processed = wbOnObjectAddInd(param1, (POM_OBJECT)param2); break; // // An object has been moved // case OM_OBJECT_MOVE_IND: TRACE_OUT(("OM_OBJECT_MOVE_IND %x %x",param1,param2)); processed = wbOnObjectMoveInd(param1, param2); break; // // An object has been deleted // case OM_OBJECT_DELETE_IND: TRACE_OUT(("OM_OBJECT_DELETE_IND %x %x",param1,param2)); processed = wbOnObjectDeleteInd(param1, (POM_OBJECT)param2); break; // // An object has been updated // case OM_OBJECT_UPDATE_IND: TRACE_OUT(("OM_OBJECT_UPDATE_IND %x %x",param1,param2)); processed = wbOnObjectUpdateInd(param1, (POM_OBJECT)param2); break; // // An object has been updated // case OM_OBJECT_REPLACE_IND: TRACE_OUT(("OM_OBJECT_REPLACE_IND %x %x",param1,param2)); processed = wbOnObjectReplaceInd(param1, (POM_OBJECT)param2); break; // // Load chaining event // case WBPI_EVENT_LOAD_NEXT: TRACE_OUT(("WBPI_EVENT_LOAD_NEXT")); wbPageLoad(); processed = TRUE; break; // // Whiteboard page clear indication // case WBP_EVENT_PAGE_CLEAR_IND: TRACE_OUT(("WBP_EVENT_PAGE_CLEAR_IND")); processed = wbOnWBPPageClearInd((WB_PAGE_HANDLE) param1); break; // // Whiteboard lock notification // case WBP_EVENT_PAGE_ORDER_LOCKED: case WBP_EVENT_CONTENTS_LOCKED: TRACE_OUT(("WBP_EVENT_xxx_LOCKED (%#hx) %#hx %#lx", event, param1, param2)); processed = wbOnWBPLock(); break; // // Whiteboard lock failure notification // case WBP_EVENT_LOCK_FAILED: TRACE_OUT(("WBP_EVENT_LOCK_FAILED %x %x",param1,param2)); processed = wbOnWBPLockFailed(); break; // // Whiteboard Unlock notification // case WBP_EVENT_UNLOCKED: TRACE_OUT(("WBP_EVENT_UNLOCKED %x %x",param1,param2)); processed = wbOnWBPUnlocked(); break; // // Whiteboard Page Order Updated notification // case WBP_EVENT_PAGE_ORDER_UPDATED: TRACE_OUT(("WBP_EVENT_PAGE_ORDER_UPDATED %x %x", param1, param2)); processed = wbOnWBPPageOrderUpdated(); break; // // We are not interested in this event - do nothing // default: TRACE_OUT(("Event ignored")); break; } // Switch on event type DebugExitBOOL(wbEventHandler, processed); return(processed); } // // wbJoinCallError // // This function should be called in STATE_REGISTERING only. // // void WbClient::wbJoinCallError(void) { DebugEntry(wbJoinCallError); ASSERT((m_state == STATE_REGISTERING)); // // Post a registration failed message to the client // TRACE_OUT(("Posting WBP_EVENT_REGISTER_FAILED")); WBP_PostEvent( 0, // No delay WBP_EVENT_JOIN_CALL_FAILED, // Failure 0, // No parameters 0); // // Tidy up after the attempt to join the call // wbLeaveCall(); DebugExitVOID(wbJoinCallError); } // // wbError // void WbClient::wbError(void) { DebugEntry(wbError); // // An error has occurred during Core processing. We act according to the // current state. // switch (m_state) { // // If the error has occurred during registration, post a registration // failure message to the client and cancel registration. // case STATE_REGISTERING: wbJoinCallError(); break; // // If the error occurred during normal running, we tell the client who // must deregister. // case STATE_IDLE: // // Only take action if we are not already in fatal error state // if (m_errorState == ERROR_STATE_EMPTY) { // // Post an error message to the client // TRACE_OUT(("Posting WBP_EVENT_ERROR")); WBP_PostEvent( 0, // No delay WBP_EVENT_ERROR, // Error 0, // No parameters 0); // // Record that an error has occurred // m_errorState = ERROR_STATE_FATAL; TRACE_OUT(("Moved error state to ERROR_STATE_FATAL")); } break; // // Client is in an unknown state // default: ERROR_OUT(("Bad main state for call")); break; } DebugExitVOID(wbError); } // // // Name: wbOnWSGroupMoveCon // // Purpose: Routine processing OM_WSGROUP_MOVE_CON events. // // BOOL WbClient::wbOnWsGroupMoveCon ( UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; BOOL processed = FALSE; UINT rc; BOOL failedToJoin = FALSE; DebugEntry(wbOnWsGroupMoveCon); // // Check that this is the event we are expecting // if (pEvent32->correlator != m_wsgroupCorrelator) { DC_QUIT; } // // Show that we have processed the event // processed = TRUE; // // Test for the correct state // if (m_subState != STATE_REG_PENDING_WSGROUP_MOVE) { // // We are not in the correct state for this event - this is an internal // error. // ERROR_OUT(("Wrong state for WSGroupMoveCon")); } // // Check that the return code for the move is OK // if (pEvent32->result != 0) { // // Moving the workset group failed - post a "join call failed" message // to the front-end. // TRACE_OUT(("WSGroup move failed, result = %d", pEvent32->result)); failedToJoin = TRUE; DC_QUIT; } // // The WSGroupMove has completed successfully. Replace our local user // object by deleting the current one (we must have one to get to this // point) and adding a new one. // // The reason we do this is that our existing user object has been moved // from the local domain into a call, but since it is in a non-persistent // workset, the Obman behaviour for this object when the call ends is // undefined. So we replace the object to get a defined behaviour. // TRACE_OUT(("Deleting local user object")); rc = OM_ObjectDelete(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, m_pObjLocal); if (rc != 0) { ERROR_OUT(("Error deleting local user object = %u", rc)); } TRACE_OUT(("Adding new local user object")); rc = wbAddLocalUserObject(); if (rc != 0) { TRACE_OUT(("Failed to add local user object")); failedToJoin = TRUE; DC_QUIT; } // // Get the clients network ID, used in graphic objects to determine where // they are loaded. // if (!wbGetNetUserID()) { // // Tidy up (and post an error event to the client) // ERROR_OUT(("Failed to get user ID, rc %u", rc)); failedToJoin = TRUE; DC_QUIT; } // // We added our user object successfully, so now wait for the // OBJECT_ADD_IND to arrive. // m_subState = STATE_REG_PENDING_NEW_USER_OBJECT; DC_EXIT_POINT: if (failedToJoin) { // // We have failed to join the call, so clean up. // wbError(); } DebugExitBOOL(wbOnWsGroupMoveCon, processed); return(processed); } // // // Name: wbOnWSGroupMoveInd // // Purpose: Routine processing OM_WSGROUP_MOVE_IND events. // // BOOL WbClient::wbOnWsGroupMoveInd ( UINT_PTR param1, UINT_PTR callID ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; BOOL processed = TRUE; DebugEntry(wbOnWsGroupMoveInd); if (callID != OM_NO_CALL) { TRACE_OUT(("Moved into new call")); DC_QUIT; } // // If we are registering, treat it as a failure to join the call, // otherwise let the client know about the network failure. // if (m_state == STATE_REGISTERING) { TRACE_OUT(("Call went down while registering")); wbError(); DC_QUIT; } TRACE_OUT(("Posting WBP_EVENT_NETWORK_LOST")); WBP_PostEvent(0, WBP_EVENT_NETWORK_LOST, // Unlocked 0, // No parameters 0); // // Tidy up the User Information workset (the local client is now the // only user). Note that since the user information workset it // non-persistent, Obman will delete the remote user objects for us. // // // - check we have opened the user workset // if ( (m_state > STATE_REGISTERING) || (m_subState > STATE_REG_PENDING_USER_WORKSET)) { // // Delete the lock object. // if (m_pObjLock != NULL) { TRACE_OUT(("Deleting lock object %d", m_pObjLock)); if (OM_ObjectDelete(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, m_pObjLock) != 0) { ERROR_OUT(("Error deleting lock object")); } if (m_lockState == LOCK_STATE_GOT_LOCK) { // // If all is well at this point the unlock process will be // completed when the object delete ind is received. // m_lockState = LOCK_STATE_PENDING_DELETE; TRACE_OUT(("Moved to state LOCK_STATE_PENDING_DELETE")); } else { m_lockState = LOCK_STATE_EMPTY; TRACE_OUT(("Moved to state LOCK_STATE_EMPTY")); } } } DC_EXIT_POINT: DebugExitBOOL(wbOnWSGroupMoveInd, processed); return(processed); } // // // Name: wbOnWorksetClearInd // // Purpose: Routine processing OM_WORKSET_CLEAR_IND events. // // Returns: Error code // // BOOL WbClient::wbOnWorksetClearInd ( UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; BOOL processed = FALSE; DebugEntry(wbOnWorksetClearInd); // // Check that the workset group is ours // if (pEvent16->hWSGroup != m_hWSGroup) { ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup)); DC_QUIT; } // // We will process the event // processed = TRUE; // // Process the event according to the workset ID // switch(pEvent16->worksetID) { // // Page Control Workset // case PAGE_CONTROL_WORKSET: ERROR_OUT(("Unexpected clear for Page Control Workset")); break; // // Lock Workset // case SYNC_CONTROL_WORKSET: ERROR_OUT(("Unexpected clear for Sync Control Workset")); break; // // User Information Workset // case USER_INFORMATION_WORKSET: ERROR_OUT(("Unexpected clear for User Information Workset")); break; // // Other (should be a Page Workset) // default: // // Tell the client that the page has been cleared - the client must then // confirm the clear. // TRACE_OUT(("Posting WBP_EVENT_PAGE_CLEAR_IND")); WBP_PostEvent( 0, WBP_EVENT_PAGE_CLEAR_IND, pEvent16->worksetID, 0); break; } DC_EXIT_POINT: DebugExitBOOL(wbOnWorksetClearInd, processed); return(processed); } // // // Name: wbOnWBPPageClearInd // // Purpose: Routine processing WBP_PAGE_CLEAR_IND events. // // Returns: Error code // // BOOL WbClient::wbOnWBPPageClearInd(WB_PAGE_HANDLE hPage) { BOOL processed; DebugEntry(wbOnWBPPageClearInd); // // This routine catches WB_PAGE_CLEAR_IND events posted to the client. // Because of the asynchronous nature of page order updates these can // sometimes have been sent previously for pages that are now no longer // in use. We trap these events here, confirm the clear to ObMan and // discard the event. // if (GetPageState(hPage)->state != PAGE_IN_USE) { TRACE_OUT(("Page is not in use - confirming workset clear immediately")); // // Accept the page clear immediately // wbPageClearConfirm(hPage); processed = TRUE; } else { // // If we get here the page is in use - so we must pass the event on to // the client. Resetting the result code of this routine to "not // processed" will ask the utilities to pass it on to the next event // handler. // processed = FALSE; } DebugExitBOOL(wbOnWBPPageClearInd, processed); return(processed); } // // // Name: wbOnObjectAddInd // // Purpose: Routine processing OM_OBJECT_ADD_IND events. // // Returns: Error code // // BOOL WbClient::wbOnObjectAddInd ( UINT_PTR param1, POM_OBJECT pObj ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; BOOL processed = FALSE; DebugEntry(wbOnObjectAddInd); // // Check that the workset group is ours // if (pEvent16->hWSGroup != m_hWSGroup) { ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup)); DC_QUIT; } // // We will process the event // processed = TRUE; // // Process the event according to the workset ID // switch(pEvent16->worksetID) { // // Page Control Workset // case PAGE_CONTROL_WORKSET: wbOnPageObjectAddInd(pObj); break; // // Sync Control Workset // case SYNC_CONTROL_WORKSET: wbOnSyncObjectAddInd(pObj); break; // // User Information Workset // case USER_INFORMATION_WORKSET: wbOnUserObjectAddInd(pObj); break; // // Other (should be a Page Workset) // default: wbOnGraphicObjectAddInd(pEvent16->worksetID, pObj); break; } DC_EXIT_POINT: DebugExitBOOL(wbOnObjectAddInd, processed); return(processed); } // // // Name: wbGetPageObjectType // // Purpose: Get the type of an object in the Page Control Workset // // Returns: Error code // // UINT WbClient::wbGetPageObjectType ( POM_OBJECT pObj, UINT * pObjectType ) { UINT result; POM_OBJECTDATA pData; DebugEntry(wbGetPageObjectType); // // Read the object to get its type // result = OM_ObjectRead(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, pObj, &pData); if (result != 0) { ERROR_OUT(("Error reading object = %d", result)); wbError(); DC_QUIT; } // // The first two bytes of the object data give its type // *pObjectType = *((TSHR_UINT16 *)pData->data); // // Release the object // OM_ObjectRelease(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, pObj, &pData); DC_EXIT_POINT: DebugExitDWORD(wbGetPageObjectType, result); return(result); } // // // Name: wbOnPageObjectAddInd // // Purpose: Routine processing OM_OBJECT_ADD_IND events occurring on the // Page Control Workset. // // Returns: Error code // // void WbClient::wbOnPageObjectAddInd(POM_OBJECT pObj) { UINT objectType; DebugEntry(wbOnPageObjectAddInd); // // Read the object to get its type // if (wbGetPageObjectType(pObj, &objectType) != 0) { DC_QUIT; } // // Act according to the type of object added // switch (objectType) { case TYPE_CONTROL_LOCK: TRACE_OUT(("It is a lock object")); wbReadLock(); break; case TYPE_CONTROL_PAGE_ORDER: TRACE_OUT(("It is the Page Control object")); wbOnPageControlObjectAddInd(pObj); break; default: ERROR_OUT(("Unknown object type added to Page Control Workset")); break; } DC_EXIT_POINT: DebugExitVOID(wbOnPageObjectAddInd); } // // // Name: wbOnPageControlObjectAddInd // // Purpose: Routine processing add of page control object // // Returns: Error code // // void WbClient::wbOnPageControlObjectAddInd(POM_OBJECT pObj) { DebugEntry(wbOnPageControlObjectAddInd); // // We only ever expect to get one of these objects // if (m_pObjPageControl != 0) { // // Check that this is the same object - the add has been triggered by // the workset open but we have already read the contents. // ASSERT((m_pObjPageControl == pObj)); } // // Save the handle of the object // m_pObjPageControl = pObj; TRACE_OUT(("Got Page Control object")); // // Continue according to the current state // switch (m_state) { case STATE_REGISTERING: // // We now have a Page Control Object - if we are waiting for the // object we can now move to the next stage. // if (m_subState == STATE_REG_PENDING_PAGE_CONTROL) { // // If we have the lock on the Page Control Workset then we are in // control of the registration process. We must add the sync // control object to the Sync Workset. // if (m_lockState == LOCK_STATE_GOT_LOCK) { // // Create the Sync Control Object // if (wbCreateSyncControl() != 0) { ERROR_OUT(("Error adding Sync Control Object")); wbError(); DC_QUIT; } } // // If we do not have the sync control object then wait for it - // otherwise we can complete initialisation. // if (m_pObjSyncControl == 0) { m_subState = STATE_REG_PENDING_SYNC_CONTROL; TRACE_OUT(("Moved substate to STATE_REG_PENDING_SYNC_CONTROL")); DC_QUIT; } else { // // If it is us who has the Page Control Workset locked - release // the lock. // if (m_lockState == LOCK_STATE_GOT_LOCK) { // // Unlock the workset // wbUnlock(); // // Wait for notification of the lock being released // TRACE_OUT(("Sub state change %d to %d", m_subState, STATE_REG_PENDING_UNLOCK)); m_subState = STATE_REG_PENDING_UNLOCK; } else { TRACE_OUT(("Page Control and Sync Control objects both there.")); TRACE_OUT(("Registration can be completed")); wbOnControlWorksetsReady(); } } } // // In other registration states we are not ready to process the // event. It will be dealt with later. // break; case STATE_IDLE: // // We must already have a Page COntrol Object since we are in idle // state. So this is an error. It may have been caused by another // client so we just trace it rather than asserting. // ERROR_OUT(("Unexpected add of Page Control Object in idle state")); break; default: ERROR_OUT(("Bad main state")); break; } DC_EXIT_POINT: DebugExitVOID(wbOnPageControlObjectAddInd); } // // // Name: wbOnSyncObjectAddInd // // Purpose: Routine processing OM_OBJECT_ADD_IND events occurring on the // Sync Control Workset. // // Returns: Error code // // void WbClient::wbOnSyncObjectAddInd(POM_OBJECT pObj) { DebugEntry(wbOnSyncObjectAddInd); // // We only expect this during registration // switch(m_state) { // // We are waiting for registration to continue // case STATE_REGISTERING: switch(m_subState) { // // We are waiting for a Sync Control Object // case STATE_REG_PENDING_SYNC_CONTROL: m_pObjSyncControl = pObj; // // The Sync Control object has been added. We do not need to do // anything with it yet. // // // If we already have the page control object then we can // complete initilisation, otherwise we have to wait for it. // if (m_pObjPageControl == 0) { TRACE_OUT(("Sub state change %d to %d", m_subState, STATE_REG_PENDING_PAGE_CONTROL)); m_subState = STATE_REG_PENDING_PAGE_CONTROL; } else { // // If it is us who has the Page Control Workset locked - // release the lock. // if (m_lockState == LOCK_STATE_GOT_LOCK) { // // Unlock the workset // wbUnlock(); // // Wait for notification of the lock being released // TRACE_OUT(("Sub state change %d to %d", m_subState, STATE_REG_PENDING_UNLOCK)); m_subState = STATE_REG_PENDING_UNLOCK; } else { TRACE_OUT(("Page Control and Sync Control objects both there.")); TRACE_OUT(("Registration can be completed")); wbOnControlWorksetsReady(); } } break; default: // // Save the handle of the Sync Control Object // m_pObjSyncControl = pObj; break; } break; // // We are fully registered and are therefore not expecting an add event // on this workset. However, since we are registered we must be // satisfied that we have a Sync Control Object - so ignore the error. // case STATE_IDLE: ERROR_OUT(("Sync object add not expected in idle state")); break; // // The client is in an unknown state // default: ERROR_OUT(("Client in unknown state = %d", m_state)); break; } DebugExitVOID(wbOnSyncObjectAddInd); } // // // Name: wbOnUserObjectAddInd // // Purpose: A user object has been added to the User Information Workset. // Inform the client that a new user has joined the call. // // Returns: Error code // // void WbClient::wbOnUserObjectAddInd(POM_OBJECT pObj) { UINT countUsers; DebugEntry(wbOnUserObjectAddInd); OM_WorksetCountObjects(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, &countUsers); TRACE_OUT(("Number of users is now %d", countUsers)); // // Ignore the add indication for our own user. // if (m_pObjLocal == pObj) { TRACE_OUT(("Got add of own user object")); // // If we have the lock (temporarily, with NULL lock owner handle), // then we need to update the lock object with our actual handle. // if ((m_pObjLock != NULL) && (m_lockState == LOCK_STATE_GOT_LOCK)) { TRACE_OUT(("Got the lock - update lock object")); wbWriteLock(); } if ((m_state == STATE_REGISTERING) && (m_subState == STATE_REG_PENDING_NEW_USER_OBJECT)) { // // We have successfully joined the call. // TRACE_OUT(("Posting WBP_EVENT_JOIN_CALL_OK")); WBP_PostEvent( 0, // No delay WBP_EVENT_JOIN_CALL_OK, // Unlocked 0, // No parameters 0); // // Update the state to show that we are ready for work again // m_state = STATE_IDLE; m_subState = STATE_EMPTY; TRACE_OUT(("Moved state back to STATE_IDLE")); } DC_QUIT; } // // If we have created our user object we must check to see if the new // user has usurped our color. If so we may need to change color. // if (m_pObjLocal != NULL) { TRACE_OUT(("We have added our user object - check colors")); wbCheckPersonColor(pObj); } // // Ignore these events unless we are fully registered // if (m_state != STATE_IDLE) { TRACE_OUT(("Ignoring user object add - not fully registered")); DC_QUIT; } // // Tell the client that a new user has joined // TRACE_OUT(("Posting WBP_EVENT_USER_JOINED")); WBP_PostEvent( 0, // No delay WBP_EVENT_PERSON_JOINED, // Event type 0, // No short parameter (UINT_PTR) pObj); // User object handle // // Try to read the lock object - we may not have been able to do this // yet. // wbReadLock(); DC_EXIT_POINT: DebugExitVOID(wbOnUserObjectAddInd); } // // // Name: wbOnGraphicObjectAddInd // // Purpose: A graphic object has been added to a page workset. // Inform the client that a new graphic has been added. // // Returns: Error code // // void WbClient::wbOnGraphicObjectAddInd ( OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID; POM_OBJECTDATA pData; PWB_GRAPHIC pGraphic; UINT result; DebugEntry(wbOnGraphicObjectAddInd); // // NFC, SFR 6450. If this object was loaded from file on this machine, // then we dont need to set the "changed flag". Otherwise record that // the contents have changed // // // Read the object. // result = OM_ObjectRead(m_pomClient, m_hWSGroup, worksetID, pObj, &pData); if (result != 0) { WARNING_OUT(("OM_ObjectRead (%u) failed, set changed flag anyway ", result)); m_changed = TRUE; TRACE_OUT(("changed flag now TRUE")); } else { // // Convert the ObMan pointer to a core pointer // pGraphic = GraphicPtrFromObjectData(pData); if ( ! ((pGraphic->loadedFromFile) && (pGraphic->loadingClientID == m_clientNetID))) { TRACE_OUT(("Not loaded from file locally - Set changed flag on")); m_changed = TRUE; TRACE_OUT(("Changed flag now TRUE")); } // // Finished with the object, so release it. // OM_ObjectRelease(m_pomClient, m_hWSGroup, worksetID, pObj, &pData); } // // These events are ignored unless we are fully registered (the client // can do nothing about them if it is not registered correctly). // if (m_state != STATE_IDLE) { TRACE_OUT(("Ignoring add of graphic object - not registered")); DC_QUIT; } // // Check that this page is actually in use // if (GetPageState(hPage)->state != PAGE_IN_USE) { TRACE_OUT(("Ignoring add to page not in use")); DC_QUIT; } // // Inform the client of the object being added // TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_ADDED")); WBP_PostEvent( 0, // No delay WBP_EVENT_GRAPHIC_ADDED, // Event type hPage, // (UINT_PTR)pObj); // User object handle DC_EXIT_POINT: DebugExitVOID(wbOnGraphicObjectAddInd); } // // // Name: wbOnObjectMoveInd // // Purpose: This routine is called whenever OM_OBJECT_MOVE_IND events are // received. // // Returns: Error code // // BOOL WbClient::wbOnObjectMoveInd ( UINT_PTR param1, UINT_PTR param2 ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2; BOOL processed = FALSE; DebugEntry(wbOnObjectMoveInd); // // Check that the workset group is ours // if (pEvent16->hWSGroup != m_hWSGroup) { ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup)); DC_QUIT; } // // We will process the event // processed = TRUE; // // Process the event according to the workset ID // switch(pEvent16->worksetID) { // // Page Control Workset Lock Workset User Information Workset // case PAGE_CONTROL_WORKSET: case SYNC_CONTROL_WORKSET: case USER_INFORMATION_WORKSET: // // Event not expected for these worksets // ERROR_OUT(("Unexpected for workset %d", (UINT) pEvent16->worksetID)); break; // // Other (should be a Page Workset) // default: wbOnGraphicObjectMoveInd(pEvent16->worksetID, (POM_OBJECT) param2); break; } DC_EXIT_POINT: DebugExitBOOL(wbOnObjectMoveInd, processed); return(processed); } // // // Name: wbOnGraphicObjectMoveInd // // Purpose: This routine is called whenever an OM_OBJECT_MOVE_IND is // received for a graphic object. // // Returns: Error code // // void WbClient::wbOnGraphicObjectMoveInd ( OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID; DebugEntry(wbOnGraphicObjectMoveInd); // // Record that the contents have changed // m_changed = TRUE; // // These events are ignored unless we are fully registered (the client // can do nothing about them). // if (m_state != STATE_IDLE) { TRACE_OUT(("Ignoring move of graphic object before registration")); DC_QUIT; } // // Check that this page is actually in use // if (GetPageState(hPage)->state != PAGE_IN_USE) { TRACE_OUT(("Ignoring move in page not in use")); DC_QUIT; } // // Inform the client of the object being added // TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_MOVED")); WBP_PostEvent( 0, // No delay WBP_EVENT_GRAPHIC_MOVED, // Event type hPage, // Page handle (UINT_PTR)pObj); // Object handle DC_EXIT_POINT: DebugExitVOID(wbOnGraphicObjectMoveInd); } // // // Name: wbOnObjectDeleteInd // // Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is // received. // // Returns: Error code // // BOOL WbClient::wbOnObjectDeleteInd ( UINT_PTR param1, POM_OBJECT pObj ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; BOOL processed = FALSE; DebugEntry(wbOnObjectDeleteInd); // // Check that the workset group is ours // if (pEvent16->hWSGroup != m_hWSGroup) { ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup)); DC_QUIT; } // // We will process the event // processed = TRUE; // // Process the event according to the workset ID // switch(pEvent16->worksetID) { // // Page Control Workset // case PAGE_CONTROL_WORKSET: wbOnPageObjectDeleteInd(pObj); break; // // Sync Workset // case SYNC_CONTROL_WORKSET: ERROR_OUT(("Illegal object delete on sync control workset - ignored")); // // We do not confirm the delete since we do not want to lose the Sync // Control Object. // break; // // User Information Workset // case USER_INFORMATION_WORKSET: wbOnUserObjectDeleteInd(pObj); break; // // Other (should be a Page Workset) // default: wbOnGraphicObjectDeleteInd(pEvent16->worksetID, pObj); break; } DC_EXIT_POINT: DebugExitBOOL(wbOnObjectDeleteInd, processed); return(processed); } // // // Name: wbOnPageObjectDeleteInd // // Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is // received for an object in the Page Control Workset. // // Returns: Error code // // void WbClient::wbOnPageObjectDeleteInd(POM_OBJECT pObj) { UINT objectType; DebugEntry(wbOnPageObjectDeleteInd); // // Get the type of object that is being deleted // if (wbGetPageObjectType(pObj, &objectType) != 0) { DC_QUIT; } switch(objectType) { case TYPE_CONTROL_PAGE_ORDER: // // The object is the Page Control Object - something serious is wrong // as this object should never be deleted. // ERROR_OUT(("Attempt to delete page control object")); break; case TYPE_CONTROL_LOCK: TRACE_OUT(("Lock object being deleted")); wbOnLockControlObjectDeleteInd(pObj); break; default: ERROR_OUT(("Bad object type")); break; } DC_EXIT_POINT: DebugExitVOID(wbOnPageObjectDeleteInd); } // // // Name: wbOnLockControlObjectDeleteInd // // Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is // received for a lock object in the Page Control Workset. // // Returns: Error code // // void WbClient::wbOnLockControlObjectDeleteInd(POM_OBJECT pObj ) { DebugEntry(wbOnLockControlObjectDeleteInd); // // Confirm the delete to ObMan // TRACE_OUT(("Lock handle %x, expecting %x", pObj, m_pObjLock)); if (pObj != m_pObjLock) { WARNING_OUT(("Unexpected lock handle %x, expecting %x", pObj, m_pObjLock)); } OM_ObjectDeleteConfirm(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, pObj); m_pObjLock = NULL; // // Process according to the current lock state // switch(m_lockState) { case LOCK_STATE_PENDING_DELETE: // // We are deleting our lock object. We must unlock the // workset. // TRACE_OUT(("Our lock object delete confirmed - unlocking the workset")); OM_WorksetUnlock(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET); break; case LOCK_STATE_LOCKED_OUT: // // The user with the lock has deleted the lock object. We treat // this as a removal of the whiteboard lock - we reset the // state at the end of this function. // TRACE_OUT(("Remote user's lock object deleted")); break; case LOCK_STATE_EMPTY: // // We have just deleted the object at the end of a call to tidy // up. Carry on so we reset lockType / pObjPersonLock etc. // TRACE_OUT(("LOCK_STATE_EMPTY")); break; case LOCK_STATE_PENDING_LOCK: WARNING_OUT(("LOCK_STATE_PENDING_LOCK")); // // We don't expect to get here. If by some chance we do, then // just quit, since we should still get the workset lock con. // DC_QUIT; break; default: ERROR_OUT(("Bad lock state %d", m_lockState)); break; } // // The lock object has been deleted, so there is no lock active // m_lockType = WB_LOCK_TYPE_NONE; m_pObjPersonLock = NULL; // // Record that there is now no lock // m_lockState = LOCK_STATE_EMPTY; TRACE_OUT(("Moved lock state to LOCK_STATE_EMPTY")); // // Notify the client of the lock status change // wbSendLockNotification(); DC_EXIT_POINT: DebugExitVOID(wbOnLockControlObjectDeleteInd); } // // // Name: wbOnGraphicObjectDeleteInd // // Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is // received for an object in a page workset. // // Returns: Error code // // void WbClient::wbOnGraphicObjectDeleteInd ( OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID; BOOL bConfirm = FALSE; DebugEntry(wbOnGraphicObjectDeleteInd); // // Record that the contents have changed // m_changed = TRUE; TRACE_OUT(("Changed flag now TRUE")); // // These events are handled within the core until the client is ready. // if (m_state != STATE_IDLE) { TRACE_OUT(("Delete of graphic object before registration")); bConfirm = TRUE; } // // Check that this page is actually in use // if (GetPageState(hPage)->state != PAGE_IN_USE) { TRACE_OUT(("Delete in page that is not in use")); bConfirm = TRUE; } // // Check whether we are to pass the event on to the client // if (bConfirm) { // // Confirm the delete to ObMan // TRACE_OUT(("Confirming delete immediately")); OM_ObjectDeleteConfirm(m_pomClient, m_hWSGroup, worksetID, pObj); } else { // // Inform the client of the object being added // TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_DELETE_IND")); WBP_PostEvent( 0, // No delay WBP_EVENT_GRAPHIC_DELETE_IND, // Event type hPage, // Page handle (UINT_PTR)pObj); // Object handle } DebugExitVOID(wbOnGraphicObjectDeleteInd); } // // // Name: wbOnUserObjectDeleteInd // // Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is // received for an object in the User Information Workset. // // Returns: Error code // // void WbClient::wbOnUserObjectDeleteInd ( POM_OBJECT pObjPerson ) { DebugEntry(wbOnUserObjectDeleteInd); // // If the user which has been removed had a lock then remove its user // handle from the client data. The lock is still there, and will be // removed when we get the WORKSET_UNLOCK_IND for the lock workset. This // arrives after the user-object delete, because the user workset is of // higher priority. // if (m_pObjPersonLock == pObjPerson) { m_pObjPersonLock = NULL; } // // These events are ignored unless we are fully registered (the client // can do nothing about them). // if (m_state != STATE_IDLE) { TRACE_OUT(("Delete of user object before registration - confirming")); // // Confirm the delete // OM_ObjectDeleteConfirm(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObjPerson); // // Nothing more to be done // DC_QUIT; } // // Inform the client of the user leaving // TRACE_OUT(("Posting WBP_EVENT_USER_LEFT_IND")); WBP_PostEvent( 0, // No delay WBP_EVENT_PERSON_LEFT, // Event type 0, // No short parameter (UINT_PTR) pObjPerson); // User object handle DC_EXIT_POINT: DebugExitVOID(wbOnUserObjectDeleteInd); } // // // Name: wbOnObjectUpdateInd // // Purpose: This routine is called whenever an OM_OBJECT_UPDATE_IND is // received for an object in a page workset. // // Returns: Error code // // BOOL WbClient::wbOnObjectUpdateInd ( UINT_PTR param1, POM_OBJECT pObj ) { POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1; BOOL processed = FALSE; DebugEntry(wbOnObjectUpdateInd); // // Check that the workset group is ours // if (pEvent16->hWSGroup != m_hWSGroup) { ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup)); DC_QUIT; } // // We will process the event // processed = TRUE; // // Process the event according to the workset ID // switch(pEvent16->worksetID) { // // Page Control Workset // case PAGE_CONTROL_WORKSET: ERROR_OUT(("Illegal object update on page control workset - ignored")); // // Updates on the Page Control Object are not allowed - do not // confirm it. // break; // // Lock Workset // case SYNC_CONTROL_WORKSET: ERROR_OUT(("Illegal object update on sync control workset")); // // Updates to the Sync Control Object itself are not allowed - do not // confirm it. // break; // // User Information Workset // case USER_INFORMATION_WORKSET: wbOnUserObjectUpdateInd(pObj); break; // // Other (should be a Page Workset) // default: wbOnGraphicObjectUpdateInd(pEvent16->worksetID, pObj); break; } DC_EXIT_POINT: DebugExitBOOL(wbOnObjectUpdateInd, processed); return(processed); } // // // Name: wbOnUserObjectUpdateInd // // Purpose: This routine is called whenever an OM_OBJECT_UPDATE_IND is // received for an object in the User Information Workset. // // Returns: Error code // // void WbClient::wbOnUserObjectUpdateInd(POM_OBJECT pObj) { DebugEntry(wbOnUserObjectUpdateInd); // // if the updated user object is not the local user's, and we have // already added the local user's object, then check the color hasn't // changed to clash with the local user's color. // if ( (m_pObjLocal != pObj) && (m_pObjLocal != NULL)) { TRACE_OUT(("Check color of updated user object")); wbCheckPersonColor(pObj); } // // Don't inform the front end if we aren't fully registered // if (m_state != STATE_IDLE) { TRACE_OUT(("User object updated before registration - confirming")); // // Confirm the update immediately // OM_ObjectUpdateConfirm(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObj); // // Nothing more to be done // DC_QUIT; } // // Tell the client that a user has been updated // TRACE_OUT(("Posting WBP_EVENT_PERSON_UPDATE_IND")); WBP_PostEvent( 0, // No delay WBP_EVENT_PERSON_UPDATE, // Event type 0, // No short parameter (UINT_PTR) pObj); // User object handle DC_EXIT_POINT: DebugExitVOID(wbOnUserObjectUpdateInd); } // // // Name: wbOnUserObjectReplaceInd // // Purpose: This routine is called whenever an OM_OBJECT_REPLACE_IND is // received for an object in the User Information Workset. // // // Returns: Error code // // void WbClient::wbOnUserObjectReplaceInd(POM_OBJECT pObj) { DebugEntry(wbOnUserObjectReplaceInd); // // if the updated user object is not the local user's, and we have // already added the local user's object, then check the color hasn't // changed to clash with the local user's color. // if ( (m_pObjLocal != pObj) && (m_pObjLocal != NULL)) { TRACE_OUT(("Check color of updated user object")); wbCheckPersonColor(pObj); } // // Don't inform the front end if we aren't fully registered // if (m_state != STATE_IDLE) { TRACE_OUT(("User object replaced before registration - confirming")); // // Confirm the replace immediately // OM_ObjectReplaceConfirm(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObj); // // Nothing more to be done // DC_QUIT; } // // Tell the client that a user has been updated // TRACE_OUT(("Posting WBP_EVENT_PERSON_UPDATE_IND")); WBP_PostEvent( 0, // No delay WBP_EVENT_PERSON_REPLACE, // Event type 0, // No short parameter (UINT_PTR) pObj); // User object handle DC_EXIT_POINT: DebugExitVOID(wbOnUserObjectReplaceInd); } // // // Name: wbOnGraphicObjectUpdateInd // // Purpose: This routine is called whenever an OM_OBJECT_UPDATE_IND is // received for an object in a page workset. // // Returns: Error code // // void WbClient::wbOnGraphicObjectUpdateInd ( OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID; BOOL bConfirm = FALSE; DebugEntry(wbOnGraphicObjectUpdateInd); // // Record that the contents have changed // m_changed = TRUE; TRACE_OUT(("Changed flag now TRUE")); // // These events are handled within the core until the client is ready. // if (m_state != STATE_IDLE) { TRACE_OUT(("Update of graphic object before registration")); bConfirm = TRUE; } // // Check that this page is actually in use // if (GetPageState(hPage)->state != PAGE_IN_USE) { TRACE_OUT(("Update for page that is not in use")); bConfirm = TRUE; } // // Check whether we are to confirm the update now or ask the client // if (bConfirm) { // // Confirm the update immediately // TRACE_OUT(("Confirming update immediately")); OM_ObjectUpdateConfirm(m_pomClient, m_hWSGroup, worksetID, pObj); } else { // // Inform the client of the object being added // TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_UPDATE_IND")); WBP_PostEvent( 0, // No delay WBP_EVENT_GRAPHIC_UPDATE_IND, // Event type hPage, // Page handle (UINT_PTR)pObj); // Object handle } DebugExitVOID(wbOnGraphicObjectUpdateInd); } // // // Name: wbOnObjectReplaceInd // // Purpose: This routine is called whenever an OM_OBJECT_REPLACE_IND is // received. // // Returns: Error code // // BOOL WbClient::wbOnObjectReplaceInd ( UINT_PTR param1, POM_OBJECT pObj ) { POM_EVENT_DATA16 pEvent = (POM_EVENT_DATA16) ¶m1; BOOL processed = FALSE; DebugEntry(wbOnObjectReplaceInd); // // Check that the workset group is ours // if (pEvent->hWSGroup != m_hWSGroup) { ERROR_OUT(("Event for unknown workset group = %d", pEvent->hWSGroup)); DC_QUIT; } // // We will process the event // processed = TRUE; // // Process the event according to the workset ID // switch (pEvent->worksetID) { // // Page Control Workset // case PAGE_CONTROL_WORKSET: wbOnPageObjectReplaceInd(pObj); break; // // Lock Workset // case SYNC_CONTROL_WORKSET: wbOnSyncObjectReplaceInd(pObj); break; // // User Information Workset // case USER_INFORMATION_WORKSET: wbOnUserObjectReplaceInd(pObj); break; // // Other (should be a Page Workset) // default: wbOnGraphicObjectReplaceInd(pEvent->worksetID, pObj); break; } DC_EXIT_POINT: DebugExitBOOL(wbOnObjectReplaceInd, processed); return(processed); } // // // Name: wbOnPageObjectReplaceInd // // Purpose: This routine is called whenever the Page Control object is // replaced. // // Returns: Error code // // void WbClient::wbOnPageObjectReplaceInd(POM_OBJECT pObj) { UINT objectType; DebugEntry(wbOnPageObjectReplaceInd); // // Confirm the change to ObMan (cannot fail) // OM_ObjectReplaceConfirm(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, pObj); // // Read the object to get its type // if (wbGetPageObjectType(pObj, &objectType) != 0) { DC_QUIT; } // // Act according to the type of object added // switch (objectType) { case TYPE_CONTROL_LOCK: wbReadLock(); break; case TYPE_CONTROL_PAGE_ORDER: wbOnPageControlObjectReplaceInd(); break; default: ERROR_OUT(("Unknown object type added to Page Control Workset")); break; } DC_EXIT_POINT: DebugExitVOID(wbOnPageObjectReplaceInd); } // // // Name: wbOnPageControlObjectReplaceInd // // Purpose: This routine is called whenever the Page Control object is // replaced. // // Returns: Error code // // void WbClient::wbOnPageControlObjectReplaceInd(void) { DebugEntry(wbOnPageControlObjectReplaceInd); // // Process according to the current state // switch (m_state) { case STATE_REGISTERING: // // During registration we do nothing - the Page Order is updated // explicitly as one of the last registration actions. // break; case STATE_IDLE: // // When we are fully registered we must send events to the front-end // indicating what changes have been made to the page list. // wbProcessPageControlChanges(); break; default: ERROR_OUT(("Bad client major state")); break; } DebugExitVOID(wbOnPageControlObjectReplaceInd); } // // // Name: wbProcessPageControlChanges // // Purpose: This routine is called whenever the Page Control object is // replaced in idle state. It reads the new Page Control data // and starts the process of informing the client of any changes. // // Returns: Error code // // void WbClient::wbProcessPageControlChanges(void) { BYTE toBeMarked[WB_MAX_PAGES]; UINT indexExternal; UINT indexInternal; UINT lLengthExternal; BOOL addOutstanding = TRUE; PWB_PAGE_ORDER pPageOrderExternal; PWB_PAGE_ORDER pPageOrderInternal = &(m_pageOrder); PWB_PAGE_STATE pPageState; POM_WORKSET_ID pPageExternal; UINT countPagesExternal; POM_OBJECTDATA pData = NULL; DebugEntry(wbProcessPageControlChanges); // // Read the new Page Control Object // if (OM_ObjectRead(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, m_pObjPageControl, &pData) != 0) { ERROR_OUT(("Error reading Page Control Object")); wbError(); DC_QUIT; } // // Extract details from the external page order // lLengthExternal = pData->length; pPageOrderExternal = (PWB_PAGE_ORDER) pData->data; pPageExternal = pPageOrderExternal->pages; countPagesExternal = pPageOrderExternal->countPages; // // Process existing and newly added pages // for (indexExternal = 0; indexExternal < countPagesExternal; indexExternal++) { // // Convert the index into the Page Control Object to an index into the // internal Page List. // indexInternal = PAGE_WORKSET_ID_TO_INDEX(pPageExternal[indexExternal]); // // Test and update the internal page state as necessary // pPageState = &((m_pageStates)[indexInternal]); // // If the page is in use locally then we do not need to do anything // (the external and internal page lists agree already). // if (pPageState->state != PAGE_IN_USE) { switch (pPageState->subState) { case PAGE_STATE_EMPTY: // // The page does not yet have a workset open for it - open one // now. (But only open one per call to this routine to prevent // swamping the message queue. The other outstanding opens will // be done when this routine is next called). // wbPageWorksetOpen(PAGE_INDEX_TO_HANDLE(indexInternal), OPEN_EXTERNAL); // // Leave now - this routine will be called again when the open // confirm is received for the workset just opened. // DC_QUIT; break; case PAGE_STATE_LOCAL_OPEN_CONFIRM: case PAGE_STATE_EXTERNAL_OPEN_CONFIRM: case PAGE_STATE_EXTERNAL_ADD: // // Do nothing - the page is already in the add process // TRACE_OUT(("Page %d is already pending local add", PAGE_INDEX_TO_HANDLE(indexInternal))); break; case PAGE_STATE_READY: // // The page workset has been opened previously - we can just mark // the page as being in use immediately. // pPageState->state = PAGE_IN_USE; pPageState->subState = PAGE_STATE_EMPTY; TRACE_OUT(("Moved page %d state to PAGE_IN_USE", (UINT) PAGE_INDEX_TO_HANDLE(indexInternal) )); break; default: ERROR_OUT(("Bad page substate %d", pPageState->subState)); break; } } } // // Mark any pages that no longer appear in the Page Control Object as // "delete pending" (unless they are already marked). // FillMemory(toBeMarked, sizeof(toBeMarked), TRUE); // // Flag which pages should be marked // for (indexExternal = 0; indexExternal < countPagesExternal; indexExternal++) { toBeMarked[PAGE_WORKSET_ID_TO_INDEX(pPageExternal[indexExternal])] = 0; } // // Mark them // for (indexInternal = 0; indexInternal < WB_MAX_PAGES; indexInternal++) { pPageState = &((m_pageStates)[indexInternal]); if ( (toBeMarked[indexInternal] == 1) && (pPageState->state == PAGE_IN_USE)) { switch (pPageState->subState) { case PAGE_STATE_EMPTY: // // Ask the client for confirmation of the delete // TRACE_OUT(("Posting WBP_EVENT_PAGE_DELETE_IND")); WBP_PostEvent( 0, // No delay WBP_EVENT_PAGE_DELETE_IND, // Page being deleted PAGE_INDEX_TO_HANDLE(indexInternal), // Page handle 0); // // Update the page state // pPageState->subState = PAGE_STATE_EXTERNAL_DELETE_CONFIRM; TRACE_OUT(("Moved page %d substate to PAGE_STATE_EXTERNAL_DELETE_CONFIRM", (UINT) PAGE_INDEX_TO_HANDLE(indexInternal) )); // // Leave now - this routine will be called again when the delete // confirm is received for this workset. // DC_QUIT; break; case PAGE_STATE_LOCAL_DELETE: // // Ask the client for confirmation of the delete // TRACE_OUT(("Posting WBP_EVENT_PAGE_DELETE_IND")); WBP_PostEvent( 0, // No delay WBP_EVENT_PAGE_DELETE_IND, // Page being deleted PAGE_INDEX_TO_HANDLE(indexInternal), // Page handle 0); // // Update the page state // pPageState->subState = PAGE_STATE_LOCAL_DELETE_CONFIRM; TRACE_OUT(("Moved page %d substate to PAGE_STATE_LOCAL_DELETE_CONFIRM", (UINT) PAGE_INDEX_TO_HANDLE(indexInternal) )); // // Leave now - this routine will be called again when the delete // confirm is received for this workset. // DC_QUIT; break; case PAGE_STATE_EXTERNAL_DELETE: case PAGE_STATE_EXTERNAL_DELETE_CONFIRM: case PAGE_STATE_LOCAL_DELETE_CONFIRM: // // We are already expecting a delete for this page // TRACE_OUT(("Page %d is already pending local delete", PAGE_INDEX_TO_HANDLE(indexInternal))); DC_QUIT; break; default: ERROR_OUT(("Bad page substate %d", pPageState->subState)); break; } } } // // There are no deletes or adds outstanding now // // // Copy the new page order to the internal page list // memcpy(pPageOrderInternal, pPageOrderExternal, lLengthExternal); // // Inform the client of the change // TRACE_OUT(("Posting WBP_EVENT_PAGE_ORDER_UPDATED")); WBP_PostEvent( 0, // No delay WBP_EVENT_PAGE_ORDER_UPDATED, // Event number 0, // No parameters 0); // // Check the number of pages ready in the cache // wbCheckReadyPages(); DC_EXIT_POINT: // // Release the Page Control Object // if (pData != NULL) { OM_ObjectRelease(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, m_pObjPageControl, &pData); } DebugExitVOID(wbProcessPageControlChanges); } // // // Name: wbOnSyncObjectReplaceInd // // Purpose: This routine is called whenever the Sync Control object is // replaced. // // Returns: Error code // // void WbClient::wbOnSyncObjectReplaceInd(POM_OBJECT pObj) { POM_OBJECTDATA pSyncObject; PWB_SYNC_CONTROL pSyncControl; OM_OBJECT_ID syncPersonID; DebugEntry(wbOnSyncControlReplaced); // // Confirm the replace of the object // OM_ObjectReplaceConfirm(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, pObj); // // Read the object and determine whether it was written by this client or // another. // if (OM_ObjectRead(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, m_pObjSyncControl, &pSyncObject) != 0) { ERROR_OUT(("Error reading Sync Control Object")); wbError(); DC_QUIT; } pSyncControl = (PWB_SYNC_CONTROL) pSyncObject->data; // // Get the user ID from the object // syncPersonID = pSyncControl->personID; // // Release the Sync Control Object // OM_ObjectRelease(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, m_pObjSyncControl, &pSyncObject); pSyncControl = NULL; // // If the user ID in the object is not the ID of the current client, we // must post a message to the front-end. // if (memcmp(&syncPersonID, &(m_personID), sizeof(syncPersonID)) != 0) { // // Post a "sync position updated" event to the front-end // TRACE_OUT(("Posting WBP_EVENT_SYNC_POSITION_UPDATED")); WBP_PostEvent( 0, WBP_EVENT_SYNC_POSITION_UPDATED, 0, 0); } DC_EXIT_POINT: DebugExitVOID(wbOnSyncControlReplaced); } // // // Name: wbOnGraphicObjectReplaceInd // // Purpose: This routine is called whenever an OM_OBJECT_REPLACE_IND is // received for an object in a page workset. // // Returns: Error code // // void WbClient::wbOnGraphicObjectReplaceInd ( OM_WORKSET_ID worksetID, POM_OBJECT pObj ) { WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID; BOOL bConfirm = FALSE; DebugEntry(wbOnGraphicObjectReplaceInd); // // Record that the contents have changed // m_changed = TRUE; TRACE_OUT(("Changed flag now TRUE")); // // These events are handled within the core until the client is ready. // if (m_state != STATE_IDLE) { TRACE_OUT(("Replace of graphic object before registration")); bConfirm = TRUE; } // // Check that this page is actually in use // if (GetPageState(hPage)->state != PAGE_IN_USE) { TRACE_OUT(("Replace in page that is not in use")); bConfirm = TRUE; } // // Check whether we are to pass the replace on to the client // if (bConfirm) { // // Confirm the change to ObMan (cannot fail) // TRACE_OUT(("Confirming replace immediately")); OM_ObjectReplaceConfirm(m_pomClient, m_hWSGroup, worksetID, pObj); } else { // // Inform the client of the object being added // TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_REPLACE_IND")); WBP_PostEvent( 0, // No delay WBP_EVENT_GRAPHIC_REPLACE_IND, // Event type hPage, // Page handle (UINT_PTR)pObj); // Object handle } DebugExitVOID(wbOnGraphicObjectReplaceInd); } // // // Name: wbWritePageControl // // Purpose: Write the page control information to the Page Control Workset // from the copy held in client data. We write only those pages // which are marked as being in use (and are not pending delete). // // Returns: Error code // // UINT WbClient::wbWritePageControl(BOOL create) { UINT result = 0; UINT rc; UINT index; UINT length; PWB_PAGE_ORDER pPageOrderInternal = &(m_pageOrder); PWB_PAGE_ORDER pPageOrderExternal; WB_PAGE_HANDLE hPage; PWB_PAGE_STATE pPageState; POM_OBJECT pObj; POM_OBJECTDATA pData = NULL; UINT generation; DebugEntry(wbWritePageControl); // // Allocate memory for the object. // length = sizeof(WB_PAGE_ORDER) - ( (WB_MAX_PAGES - pPageOrderInternal->countPages) * sizeof(OM_WORKSET_ID)); if (OM_ObjectAlloc(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, length, &pData) != 0) { ERROR_OUT(("Error allocating object")); DC_QUIT; } pData->length = length; // // Get a pointer to the page control object itself // pPageOrderExternal = (PWB_PAGE_ORDER) pData->data; // // Set the object type // pPageOrderExternal->objectType = TYPE_CONTROL_PAGE_ORDER; // // Increment the page list generation number indicating that we have // written a new version of the page list. // generation = MAKELONG(pPageOrderInternal->generationLo, pPageOrderInternal->generationHi); generation++; pPageOrderInternal->generationLo = LOWORD(generation); pPageOrderInternal->generationHi = HIWORD(generation); // // Copy the page control data // pPageOrderExternal->objectType = TYPE_CONTROL_PAGE_ORDER; pPageOrderExternal->generationLo = pPageOrderInternal->generationLo; pPageOrderExternal->generationHi = pPageOrderInternal->generationHi; pPageOrderExternal->countPages = 0; // // Loop through the internal page order finding the pages that are in // use. // for (index = 0; index < pPageOrderInternal->countPages; index++) { // // Get the handle of the next page // hPage = (pPageOrderInternal->pages)[index]; // // Check the page state // pPageState = GetPageState(hPage); if ( (pPageState->state == PAGE_IN_USE) && (pPageState->subState == PAGE_STATE_EMPTY)) { // // Add the page to the external list // (pPageOrderExternal->pages)[pPageOrderExternal->countPages] = hPage; pPageOrderExternal->countPages++; } } // // We expect always to copy at least one page // ASSERT((pPageOrderExternal->countPages >= 1)); // // Check whether we are creating or replacing the object // if (create) { // // Add the object to the workset (we never update these objects, so the // update length is set to 0). // rc = OM_ObjectAdd(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, &pData, 0, &pObj, LAST); } else { // // Replace the existing object // TRACE_OUT(("Replacing Page Control Object")); rc = OM_ObjectReplace(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, m_pObjPageControl, &pData); } if (rc != 0) { // // Discard the object - it was not used to replace the existing one // TRACE_OUT(("Adding Page Control Object")); OM_ObjectDiscard(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, &pData); ERROR_OUT(("Error adding/replacing page control object")); DC_QUIT; } DC_EXIT_POINT: DebugExitDWORD(wbWritePageControl, result); return(result); } // // // Name: wbCreateSyncControl // // Purpose: Create the sync control object // // Returns: None // // UINT WbClient::wbCreateSyncControl(void) { UINT result; WB_SYNC sync; DebugEntry(wbCreateSyncControl); // // Set the sync information to no page, empty rectangle // ZeroMemory(&sync, sizeof(WB_SYNC)); sync.length = WB_SYNC_SIZE; sync.currentPage = WB_PAGE_HANDLE_NULL; // // Write the object // result = wbWriteSyncControl(&sync, TRUE); DebugExitDWORD(wbCreateSyncControl, result); return(result); } // // // Name: wbWriteSyncControl // // Purpose: Write the Sync Control object to the Page Control Workset // // Returns: Error code // // UINT WbClient::wbWriteSyncControl ( PWB_SYNC pSync, BOOL create ) { UINT result = 0; UINT rc; POM_OBJECT pObj; POM_OBJECTDATA pData = NULL; PWB_SYNC_CONTROL pSyncControl; DebugEntry(wbWriteSyncControl); // // Allocate memory for the object. // rc = OM_ObjectAlloc(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, WB_SYNC_CONTROL_SIZE, &pData); if (rc != 0) { ERROR_OUT(("Error allocating object")); DC_QUIT; } pData->length = WB_SYNC_CONTROL_SIZE; // // Copy the sync control data from the client information // pSyncControl = (PWB_SYNC_CONTROL) pData->data; pSyncControl->personID = m_personID; memcpy(&(pSyncControl->sync), pSync, WB_SYNC_SIZE); // // Check whether we are creating or replacing the object // if (create) { // // Add the object to the workset // rc = OM_ObjectAdd(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, &pData, WB_SYNC_CONTROL_SIZE, &pObj, LAST); // // If successful // if (rc == 0) { // // Save the handle of the sync control object // m_pObjSyncControl = pObj; // // Make sure we do not discard the object below // pData = NULL; } } else { // // Replace the existing object // rc = OM_ObjectReplace(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, m_pObjSyncControl, &pData); // // Make sure we do not discard the object below // pData = NULL; } DC_EXIT_POINT: // // If we still have the Sync Control object - discard it // if (pData != NULL) { // // Discard the object - it was not used to replace the existing one // OM_ObjectDiscard(m_pomClient, m_hWSGroup, SYNC_CONTROL_WORKSET, &pData); } // // If an error occurred during processing - report it // if (rc != 0) { ERROR_OUT(("Error adding/replacing sync control object")); wbError(); DC_QUIT; } DebugExitDWORD(wbWriteSyncControl, result); return(result); } // // // Name: wbSelectPersonColor // // Purpose: Select a color identifier for the local user // // Returns: Selected color // // UINT WbClient::wbSelectPersonColor(void) { UINT count = 0; UINT result; POM_OBJECT pObjUser; DebugEntry(wbSelectPersonColor); // // Select the color according to the order in the workset. See comments // in wbCheckPersonColor for further details. // // // start at the first object, search for the position of the local user's // user object. // result = OM_ObjectH(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, 0, &pObjUser, FIRST); while ((result == 0) && (pObjUser != m_pObjLocal)) { count++; result = OM_ObjectH(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObjUser, &pObjUser, AFTER); } if ((result != 0) && (result != OM_RC_NO_SUCH_OBJECT)) { ERROR_OUT(("Unexpected return code from ObMan")); } DebugExitDWORD(wbSelectPersonColor, count); return (count); } // // // Name: wbCheckPersonColor // // Purpose: Check whether a new user has usurped our color. If so we must // update our own color. // // Returns: None // // void WbClient::wbCheckPersonColor ( POM_OBJECT hCheckObject ) { POM_OBJECTDATA pCheckObject = NULL; PWB_PERSON pUser; WB_PERSON user; DebugEntry(wbCheckPersonColor); // // Read the new user information // if (OM_ObjectRead(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, hCheckObject, &pCheckObject) != 0) { wbError(); DC_QUIT; } pUser = (PWB_PERSON) pCheckObject->data; // // Compare the color identifier in the new user with that of the local // user, if they are different there is nothing to do. // if (pUser->colorId == m_colorId) { TRACE_OUT(("New user has same color as local user = %d", pUser->colorId)); // // The user color is determined by the order in the workset group of // the user objects. The first user has color 0, the second color 1 // etc. // // When a user leaves the workset, however, the colors do not change. // // When a new user joins, it sets its color to its new position, and // the others will then be forced to change accordingly. Whenever an // object add or update is received where the new remote user color // clashes with the local one, it is always the local user's job to // change color, since the remote user has selected his new color // according to his current position in the workset. The local user // can't have the same position (since two users have two distinct user // objects, so therefore must have the wrong color. // // // Get the user object for the local user // if (wbPersonGet(m_pObjLocal, &user) != 0) { DC_QUIT; } // // Update the color // TRACE_OUT(("Old color ID for local user is %d", user.colorId)); user.colorId = (TSHR_UINT16)wbSelectPersonColor(); TRACE_OUT(("New color ID for local user is %d", user.colorId)); // // Copy the person's color into the client's data // m_colorId = user.colorId; // // Write the new user information back // if (wbPersonUpdate(&user) != 0) { DC_QUIT; } } DC_EXIT_POINT: // // If an object has been read, release it now // if (pCheckObject != NULL) { OM_ObjectRelease(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, hCheckObject, &pCheckObject); } DebugExitVOID(wbCheckPersonColor); } // // // Name: wbWriteLock // // Purpose: Add a lock object to the Page Control Workset // // Returns: Error code // // UINT WbClient::wbWriteLock(void) { UINT result; POM_OBJECTDATA pData; PWB_LOCK pLock; POM_OBJECT pObj; DebugEntry(wbWriteLock); // // Create a lock object // result = OM_ObjectAlloc(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, sizeof(WB_LOCK), &pData); if (result != 0) { ERROR_OUT(("Unable to allocate lock object = %d", result)); wbError(); DC_QUIT; } pData->length = sizeof(WB_LOCK); // // Set the lock object fields // pLock = (PWB_LOCK) pData->data; pLock->objectType = TYPE_CONTROL_LOCK; pLock->personID = m_personID; pLock->lockType = m_lockRequestType; // // If we already have the lock, then we can just replace the object // if (m_pObjLock == NULL) { // // Add the lock object to the Workset. The Add indication received // by the remote users signals the presence of the lock to them. // result = OM_ObjectAdd(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, &pData, sizeof(WB_LOCK), &pObj, LAST); } else { // // Replace the existing object // result = OM_ObjectReplace(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, m_pObjLock, &pData); } if (result != 0) { // // The add or replace failed, we must discard the object // OM_ObjectDiscard(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, &pData); ERROR_OUT(("Error adding user object")); wbError(); DC_QUIT; } // // Save the handle of the lock object // TRACE_OUT(("Lock handle was %x, now %x", m_pObjLock, pObj)); m_pObjLock = pObj; DC_EXIT_POINT: DebugExitDWORD(wbWriteLock, result); return(result); } // // // Name: wbReadLock // // Purpose: Update the lock information stored in the client data after a // change in the Lock Object. // // Returns: Error code // // void WbClient::wbReadLock(void) { UINT count = 0; DebugEntry(wbReadLock); // // Before we read the lock information we need to ensure that the // PAGE_CONTROL_WORKSET and the USER_INFORMATION_WORKSET both contain // the objects we need. If either of the objects are missing, quit and // wait until we are called again - this function will be called // whenever new objects are added to these worksets. // OM_WorksetCountObjects(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, &count); TRACE_OUT(("%d objects in USER_INFORMATION_WORKSET", count)); if (count == 0) { TRACE_OUT(("Need to wait for USER_INFO object")); DC_QUIT; } OM_WorksetCountObjects(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, &count); TRACE_OUT(("%d objects in PAGE_CONTROL_WORKSET", count)); if (count == 0) { TRACE_OUT(("Need to wait for PAGE_CONTROL object")); DC_QUIT; } TRACE_OUT(("Process lock")); wbProcessLockNotification(); DC_EXIT_POINT: DebugExitVOID(wbReadLock); } // // // Name: wbProcessLockNotification // // Purpose: // // Returns: Error code // // void WbClient::wbProcessLockNotification(void) { UINT rc = 0; POM_OBJECTDATA pData; PWB_LOCK pLock; WB_LOCK_TYPE lockType; POM_OBJECT pObjPersonLock; POM_OBJECT pObj; POM_OBJECT pObjLock; UINT objectType = 0; DebugEntry(wbProcessLockNotification); // // Get the handle of the lock object. We use the last object in the // workset to protect against lock objects being left lying around. // rc = OM_ObjectH(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, 0, &pObj, LAST); if (rc != 0) { ERROR_OUT(("Error getting lock object handle = %d", rc)); wbError(); DC_QUIT; } // // Check that this is the CONTROL_LOCK object. Quit if it isnt - we // will be called again later when the object arrices. // rc = wbGetPageObjectType(pObj, &objectType); if (rc != 0) { DC_QUIT; } if (objectType != TYPE_CONTROL_LOCK) { TRACE_OUT(("not LOCK control object - quit")); DC_QUIT; } // // Save the handle of the lock object // pObjLock = pObj; // // Read the object // rc = OM_ObjectRead(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, pObj, &pData); if (rc != 0) { ERROR_OUT(("Error reading lock object %d", rc)); wbError(); DC_QUIT; } pLock = (PWB_LOCK) &(pData->data); // // Save the lock details // lockType = (WB_LOCK_TYPE)pLock->lockType; TRACE_OUT(("Lock type %d", lockType)); // // Convert the object ID held in the PAGE_CONTROL workset to an object // handle. // rc = OM_ObjectIDToPtr(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pLock->personID, &pObjPersonLock); // // The return code is checked after the object release to ensure that // the object is not held and read again. // // // Release the lock object // OM_ObjectRelease(m_pomClient, m_hWSGroup, PAGE_CONTROL_WORKSET, pObj, &pData); // // Check the return code from the ID to Handle call // if (rc == OM_RC_BAD_OBJECT_ID) { WARNING_OUT(("Unknown ID - wait for next add of user object")); DC_QUIT; } else if (rc != 0) { ERROR_OUT(("Error (%d) converting lock user ID to handle", rc)); wbError(); DC_QUIT; } // // Validate the lock state and details // switch (m_lockState) { // // In this state we do not actually have the lock, but are waiting // for confirmation of an earlier workset-lock request. In this // case, we let the front end know that the lock request has failed // before sending indication of the lock by the other user. // case LOCK_STATE_PENDING_LOCK: ASSERT((pObjPersonLock != m_pObjLocal)); m_lockState = LOCK_STATE_LOCKED_OUT; TRACE_OUT(("Moved lock state to LOCK_STATE_LOCKED_OUT")); WBP_PostEvent( 0, // No delay WBP_EVENT_LOCK_FAILED, // Lock request failed 0, // No parameters 0); break; // // In these states we do not have a lock - this must be a new lock // from remote user or an update to an old lock. // case LOCK_STATE_EMPTY: case LOCK_STATE_LOCKED_OUT: ASSERT((pObjPersonLock != m_pObjLocal)); // // Update the lock state to show that we are now locked out // m_lockState = LOCK_STATE_LOCKED_OUT; TRACE_OUT(("Moved lock state to LOCK_STATE_LOCKED_OUT")); break; // // In these states we have the lock (or are expecting to get it) // case LOCK_STATE_GOT_LOCK: case LOCK_STATE_PENDING_ADD: ASSERT((pObjPersonLock == m_pObjLocal)); // // Update the lock state to show that we are now locked out // m_lockState = LOCK_STATE_GOT_LOCK; TRACE_OUT(("Moved lock state to LOCK_STATE_GOT_LOCK")); break; // // The lock request has been cancelled - unlock the WS. // case LOCK_STATE_CANCEL_LOCK: break; // // In any other state we are not expecting any lock // default: ERROR_OUT(("Not expecting lock object add")); break; } // // Save the lock details // TRACE_OUT(("Lock handle was %x, now %x", m_pObjLock, pObjLock)); m_pObjLock = pObjLock; m_lockType = lockType; m_pObjPersonLock = pObjPersonLock; // // If the lock has subsequently been cancelled, unlock the WS. // if (m_lockState == LOCK_STATE_CANCEL_LOCK) { TRACE_OUT(("Cancel lock")); m_lockState = LOCK_STATE_GOT_LOCK; wbUnlock(); } else { // // Inform the client of the lock. The notification will be trapped // by the core if the client is not fully registered. // wbSendLockNotification(); } DC_EXIT_POINT: DebugExitVOID(wbProcessLockNotification); } // // // Name: wbSendLockNotification // // Purpose: Post a lock notification to the client. The lock information // held in the client memory must be up to date when this function // is called. // // Returns: Error code // // void WbClient::wbSendLockNotification(void) { UINT result = 0; UINT lockEvent; DebugEntry(wbSendLockNotification); // // Check that we are in a valid state for sending a lock notification // if ( (m_lockState == LOCK_STATE_GOT_LOCK) || (m_lockState == LOCK_STATE_LOCKED_OUT) || (m_lockState == LOCK_STATE_EMPTY)) { // // Verify the lock type // switch (m_lockType) { case WB_LOCK_TYPE_CONTENTS: TRACE_OUT(("Posting WBP_EVENT_CONTENTS_LOCKED")); lockEvent = WBP_EVENT_CONTENTS_LOCKED; break; case WB_LOCK_TYPE_PAGE_ORDER: TRACE_OUT(("Posting WBP_EVENT_PAGE_ORDER_LOCKED")); lockEvent = WBP_EVENT_PAGE_ORDER_LOCKED; break; case WB_LOCK_TYPE_NONE: TRACE_OUT(("Posting WBP_EVENT_UNLOCKED")); lockEvent = WBP_EVENT_UNLOCKED; break; default: ERROR_OUT(("Bad lock type %d", (UINT) m_lockType)); DC_QUIT; } // // Tell the client that the lock has been acquired or released // WBP_PostEvent( 0, lockEvent, 0, (UINT_PTR)m_pObjPersonLock); TRACE_OUT(("Sent lock notification")); } DC_EXIT_POINT: DebugExitVOID(wbSendLockNotification); } // // // Name: wbOnWBPLock // // Purpose: Process a successful lock acquisitoin // // Returns: Error code // // BOOL WbClient::wbOnWBPLock(void) { BOOL processed = TRUE; DebugEntry(wbOnWBPLock); // // If we are registering and have just acquired the lock - we can now // continue the registration process. // // // Test the current state // switch (m_state) { // // We are waiting for registration to continue // case STATE_REGISTERING: // // Act on the registration substate // if (m_subState == STATE_REG_PENDING_LOCK) { // // Check that it is us who now has the lock // if (m_lockState != LOCK_STATE_GOT_LOCK) { TRACE_OUT(("It is not us who has the lock")); // // Another client has acquired the lock - we must wait for them // to add the Page Control Object. // m_subState = STATE_REG_PENDING_PAGE_CONTROL; TRACE_OUT(("Moved to substate STATE_REG_PENDING_PAGE_CONTROL")); DC_QUIT; } // // We now have the Page Control Workset locked - check for the // existence of the Page Control and Sync Control objects. (We // have to do this because another client could have locked the // workset, added the objects and unlocked the workset just before // we requested the lock. The Page Control Object may not have // reached us before we requested the lock. Now that we have the // lock we are guaranteed to have all objects in the workset so the // object add events may have arrived just before the lock // confirmation. // if ( (m_pObjPageControl != 0) && (m_pObjSyncControl != 0)) { // // Unlock the workset // wbUnlock(); // // Wait for the unlock to complete // m_subState = STATE_REG_PENDING_UNLOCK; TRACE_OUT(("Moved to substate STATE_REG_PENDING_UNLOCK")); DC_QUIT; } // // We are the first in the call - we must add the Page Control // Object. (It is possible that another client added the Page // Control object and then failed. To cover this we check // separately for the Page Control and Sync objects.) // if (m_pObjPageControl == 0) { // // Add a single page to the page control object using the first // page workset (which we always open). // wbPagesPageAdd(0, FIRST_PAGE_WORKSET, PAGE_FIRST); // // Write the Page Control information // if (wbWritePageControl(TRUE) != 0) { ERROR_OUT(("Error adding Page Control Object")); wbError(); DC_QUIT; } // // Update the state to "waiting for Page Control" // m_subState = STATE_REG_PENDING_PAGE_CONTROL; TRACE_OUT(("Moved to substate STATE_REG_PENDING_PAGE_CONTROL")); DC_QUIT; } // // The Page Control object is there, so the Sync Control object // must not be (we checked above for both being there and would // have exited by now if they were). // ASSERT((m_pObjSyncControl == 0)); // // Create the Sync Control Object. // if (wbCreateSyncControl() != 0) { ERROR_OUT(("Error adding Sync Control Object")); wbError(); DC_QUIT; } // // Wait for the Sync Control object to be added // m_subState = STATE_REG_PENDING_SYNC_CONTROL; TRACE_OUT(("Moved substate to STATE_REG_PENDING_SYNC_CONTROL")); DC_QUIT; } break; case STATE_IDLE: // // We are fully registered. The event must be passed on to the // front-end // processed = FALSE; break; // // We are in an unknown state // default: ERROR_OUT(("Bad client major state")); break; } DC_EXIT_POINT: DebugExitBOOL(wbOnWBPLock, processed); return(processed); } // // // Name: wbOnWBPLockFailed // // Purpose: Process a failed lock acquisition // // Returns: Error code // // BOOL WbClient::wbOnWBPLockFailed(void) { BOOL processed = TRUE; DebugEntry(wbOnWBPLockFailed); // // Check the current state // switch (m_state) { case STATE_REGISTERING: // // If we are registering and have just failed to acquire the lock - // this is because another user has the lock. If both the page and // sync objects have been added, finish registration, otherwise wait // for them to be added. // if ( (m_pObjPageControl != 0) && (m_pObjSyncControl != 0)) { TRACE_OUT(("Page Control and Sync Control objects both there.")); TRACE_OUT(("Registration can be completed")); wbOnControlWorksetsReady(); DC_QUIT; } if (m_pObjPageControl == 0) { TRACE_OUT(("Waiting for page control")); m_subState = STATE_REG_PENDING_PAGE_CONTROL; DC_QUIT; } if (m_pObjSyncControl == 0) { TRACE_OUT(("Waiting for sync control")); m_subState = STATE_REG_PENDING_SYNC_CONTROL; DC_QUIT; } break; case STATE_IDLE: // // We are fully registered. The event must be passed on to the // front-end // processed = FALSE; break; default: ERROR_OUT(("Bad main state")); break; } DC_EXIT_POINT: DebugExitBOOL(wbOnWBPLockFailed, processed); return(processed); } // // // Name: wbOnWBPUnlocked // // Purpose: Process an unlock notification // // Returns: Error code // // BOOL WbClient::wbOnWBPUnlocked(void) { BOOL processed = TRUE; DebugEntry(wbOnWBPUnlocked); // // If we are registering and waiting to unlock the Page Control Workset // we must complete registration here. // // // Check the current state // switch (m_state) { case STATE_REGISTERING: // // Check whether we are expecting his event // if(m_subState == STATE_REG_PENDING_UNLOCK) { // // Continue the registration process // wbOnControlWorksetsReady(); DC_QUIT; } // // We were not expecting the unlock event // WARNING_OUT(("Unexpected unlock event")); break; case STATE_IDLE: // // We are fully registered. The event must be passed on to the // front-end // processed = FALSE; break; default: ERROR_OUT(("Bad main state")); break; } // Switch on client state DC_EXIT_POINT: DebugExitBOOL(wbOnWBPUnlocked, processed); return(processed); } // // // Name: wbOnWBPPageOrderUpdated // // Purpose: Process a page order updated notification // // Returns: Error code // // BOOL WbClient::wbOnWBPPageOrderUpdated(void) { BOOL processed = FALSE; DebugEntry(wbOnWBPPageOrderUpdated); // // If we are registering and waiting for the Page Order to be brought // up-to-date we can now continue registration. // if (m_state == STATE_REGISTERING) { // // Show that we have processed the event (we do not want to pass it on // to the client, they are not yet fully registered and will not be // expecting it). // processed = TRUE; // // If we have enough pages ready in the cache, we have completed // registration. (Otherwise the call to CheckReadyPages will open // another page and will complete registration later.) // if (wbCheckReadyPages()) { wbCompleteRegistration(); DC_QUIT; } // // We must wait for sufficiently many pages to be ready // m_subState = STATE_REG_PENDING_READY_PAGES; TRACE_OUT(("Moved substate to STATE_REG_PENDING_READY_PAGES")); } DC_EXIT_POINT: DebugExitBOOL(wbOnWBPPageOrderUpdated, processed); return(processed); } // // // Name: wbPersonGet // // Purpose: Get user details // // Returns: Error code // // UINT WbClient::wbPersonGet ( POM_OBJECT pObjUser, PWB_PERSON pUser ) { UINT result = 0; POM_OBJECTDATA pUserObject; DebugEntry(wbPersonGet); if (pObjUser == m_pObjLocal) { TRACE_OUT(("Call is for local user details")); } // // Read the object. // result = OM_ObjectRead(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObjUser, &pUserObject); if (result != 0) { ERROR_OUT(("OM_ObjectRead = %d", result)); DC_QUIT; } // // Copy the read user object into the buffer passed // memcpy(pUser, pUserObject->data, sizeof(WB_PERSON)); TRACE_OUT(("CMG personID %u", pUser->cmgPersonID)); // // Release the object // OM_ObjectRelease(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, pObjUser, &pUserObject); // // If the call is for the local user, update the color field to ensure it // doesn't get overwritten in a race condition with the front-end (i.e. // the front end tries to update the user before the color-change event // has been received). The core "knows better" than ObMan what the local // user's color is. This is safe because the color field is only ever // changed locally. // if (pObjUser == m_pObjLocal) { pUser->colorId = (TSHR_UINT16)m_colorId; } DC_EXIT_POINT: DebugExitDWORD(wbPersonGet, result); return(result); } // // // Name: wbPersonUpdate // // Purpose: Update the local user object - this is only used by the CORE - // the front-end calls WBP_SetPersonData, which does a _replace_. // // Returns: Error code // // UINT WbClient::wbPersonUpdate(PWB_PERSON pUser) { UINT result = 0; POM_OBJECTDATA pUserObject; DebugEntry(wbPersonUpdate); // // Allocate a user object // result = OM_ObjectAlloc(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, sizeof(WB_PERSON), &pUserObject); if (result != 0) { ERROR_OUT(("OM_ObjectAlloc = %d", result)); DC_QUIT; } // // Set the length of the object // pUserObject->length = WB_PERSON_OBJECT_UPDATE_SIZE, // // Copy the user information into the ObMan object // memcpy(pUserObject->data, pUser, sizeof(WB_PERSON)); // // Update the object // result = OM_ObjectUpdate(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, m_pObjLocal, &pUserObject); if (result != 0) { ERROR_OUT(("OM_ObjectUpdate = %d", result)); // // Discard the object // OM_ObjectDiscard(m_pomClient, m_hWSGroup, USER_INFORMATION_WORKSET, &pUserObject); DC_QUIT; } // // Note that the object has not yet been updated. An // OM_OBJECT_UPDATE_IND event will be generated. // DC_EXIT_POINT: DebugExitDWORD(wbPersonUpdate, result); return(result); } // // // Name: wbGetNetUserID(). // // Purpose: Get the network user ID for this client // // Returns: // // BOOL WbClient::wbGetNetUserID(void) { BOOL result = TRUE; UINT rc = 0; DebugEntry(wbGetNetUserID); rc = OM_GetNetworkUserID(m_pomClient, m_hWSGroup, &m_clientNetID); if (rc != 0) { if (rc == OM_RC_LOCAL_WSGROUP) { m_clientNetID = 0; } else { result = FALSE; } } DebugExitBOOL(wbGetNetUserID, result); return(result); }