Implement RDP persistent bitmap cache
This commit is contained in:
parent
9d627e0df2
commit
161617c4a4
@ -481,76 +481,6 @@ fail:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpgfx_send_cache_import_offer_pdu(RdpgfxClientContext* context,
|
||||
const RDPGFX_CACHE_IMPORT_OFFER_PDU* pdu)
|
||||
{
|
||||
UINT16 index;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
wStream* s;
|
||||
RDPGFX_PLUGIN* gfx;
|
||||
RDPGFX_CHANNEL_CALLBACK* callback;
|
||||
RDPGFX_HEADER header;
|
||||
RDPGFX_CACHE_ENTRY_METADATA* cacheEntries;
|
||||
|
||||
if (!context || !pdu)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
gfx = (RDPGFX_PLUGIN*)context->handle;
|
||||
|
||||
if (!gfx || !gfx->listener_callback)
|
||||
return ERROR_BAD_CONFIGURATION;
|
||||
|
||||
callback = gfx->listener_callback->channel_callback;
|
||||
|
||||
if (!callback)
|
||||
return ERROR_BAD_CONFIGURATION;
|
||||
|
||||
header.flags = 0;
|
||||
header.cmdId = RDPGFX_CMDID_CACHEIMPORTOFFER;
|
||||
header.pduLength = RDPGFX_HEADER_SIZE + 2 + pdu->cacheEntriesCount * 12;
|
||||
DEBUG_RDPGFX(gfx->log, "SendCacheImportOfferPdu: cacheEntriesCount: %" PRIu16 "",
|
||||
pdu->cacheEntriesCount);
|
||||
s = Stream_New(NULL, header.pduLength);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if ((error = rdpgfx_write_header(s, &header)))
|
||||
goto fail;
|
||||
|
||||
if (pdu->cacheEntriesCount <= 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Invalid cacheEntriesCount: %" PRIu16 "", pdu->cacheEntriesCount);
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* cacheEntriesCount (2 bytes) */
|
||||
Stream_Write_UINT16(s, pdu->cacheEntriesCount);
|
||||
|
||||
for (index = 0; index < pdu->cacheEntriesCount; index++)
|
||||
{
|
||||
cacheEntries = &(pdu->cacheEntries[index]);
|
||||
Stream_Write_UINT64(s, cacheEntries->cacheKey); /* cacheKey (8 bytes) */
|
||||
Stream_Write_UINT32(s, cacheEntries->bitmapLength); /* bitmapLength (4 bytes) */
|
||||
}
|
||||
|
||||
error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
|
||||
NULL);
|
||||
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -668,6 +598,340 @@ static UINT rdpgfx_recv_evict_cache_entry_pdu(RDPGFX_CHANNEL_CALLBACK* callback,
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load cache import offer from file (offline replay)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rdpgfx_load_cache_import_offer(RDPGFX_PLUGIN* gfx, RDPGFX_CACHE_IMPORT_OFFER_PDU* offer)
|
||||
{
|
||||
int idx, count;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
PERSISTENT_CACHE_ENTRY entry;
|
||||
rdpPersistentCache* persistent = NULL;
|
||||
rdpSettings* settings = gfx->settings;
|
||||
|
||||
offer->cacheEntriesCount = 0;
|
||||
|
||||
if (!settings->BitmapCachePersistEnabled)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (!settings->BitmapCachePersistFile)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
persistent = persistent_cache_new();
|
||||
|
||||
if (!persistent)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
if (persistent_cache_open(persistent, settings->BitmapCachePersistFile, FALSE, 3) < 1) {
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (persistent_cache_get_version(persistent) != 3) {
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
count = persistent_cache_get_count(persistent);
|
||||
|
||||
if (count < 1) {
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (count >= RDPGFX_CACHE_ENTRY_MAX_COUNT)
|
||||
count = RDPGFX_CACHE_ENTRY_MAX_COUNT - 1;
|
||||
|
||||
if (count > gfx->MaxCacheSlots)
|
||||
count = gfx->MaxCacheSlots;
|
||||
|
||||
offer->cacheEntriesCount = (UINT16)count;
|
||||
|
||||
for (idx = 0; idx < count; idx++)
|
||||
{
|
||||
if (persistent_cache_read_entry(persistent, &entry) < 1) {
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offer->cacheEntries[idx].cacheKey = entry.key64;
|
||||
offer->cacheEntries[idx].bitmapLength = entry.size;
|
||||
}
|
||||
|
||||
persistent_cache_free(persistent);
|
||||
|
||||
return error;
|
||||
fail:
|
||||
persistent_cache_free(persistent);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpgfx_save_persistent_cache(RDPGFX_PLUGIN* gfx)
|
||||
{
|
||||
int idx;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
PERSISTENT_CACHE_ENTRY cacheEntry;
|
||||
rdpPersistentCache* persistent = NULL;
|
||||
rdpSettings* settings = gfx->settings;
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*)gfx->iface.pInterface;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!settings->BitmapCachePersistEnabled)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (!settings->BitmapCachePersistFile)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (!context->ExportCacheEntry)
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
|
||||
persistent = persistent_cache_new();
|
||||
|
||||
if (!persistent)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
if (persistent_cache_open(persistent, settings->BitmapCachePersistFile, TRUE, 3) < 1) {
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < gfx->MaxCacheSlots; idx++)
|
||||
{
|
||||
if (gfx->CacheSlots[idx])
|
||||
{
|
||||
UINT16 cacheSlot = (UINT16)idx;
|
||||
|
||||
if (context->ExportCacheEntry(context, cacheSlot, &cacheEntry) != CHANNEL_RC_OK)
|
||||
continue;
|
||||
|
||||
persistent_cache_write_entry(persistent, &cacheEntry);
|
||||
}
|
||||
}
|
||||
|
||||
persistent_cache_free(persistent);
|
||||
|
||||
return error;
|
||||
fail:
|
||||
persistent_cache_free(persistent);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpgfx_send_cache_import_offer_pdu(RdpgfxClientContext* context,
|
||||
const RDPGFX_CACHE_IMPORT_OFFER_PDU* pdu)
|
||||
{
|
||||
UINT16 index;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
wStream* s;
|
||||
RDPGFX_HEADER header;
|
||||
RDPGFX_CHANNEL_CALLBACK* callback;
|
||||
RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
|
||||
|
||||
if (!context || !pdu)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
gfx = (RDPGFX_PLUGIN*)context->handle;
|
||||
|
||||
if (!gfx || !gfx->listener_callback)
|
||||
return ERROR_BAD_CONFIGURATION;
|
||||
|
||||
callback = gfx->listener_callback->channel_callback;
|
||||
|
||||
if (!callback)
|
||||
return ERROR_BAD_CONFIGURATION;
|
||||
|
||||
header.flags = 0;
|
||||
header.cmdId = RDPGFX_CMDID_CACHEIMPORTOFFER;
|
||||
header.pduLength = RDPGFX_HEADER_SIZE + 2 + pdu->cacheEntriesCount * 12;
|
||||
DEBUG_RDPGFX(gfx->log, "SendCacheImportOfferPdu: cacheEntriesCount: %" PRIu16 "",
|
||||
pdu->cacheEntriesCount);
|
||||
s = Stream_New(NULL, header.pduLength);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if ((error = rdpgfx_write_header(s, &header)))
|
||||
goto fail;
|
||||
|
||||
if (pdu->cacheEntriesCount <= 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Invalid cacheEntriesCount: %" PRIu16 "", pdu->cacheEntriesCount);
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* cacheEntriesCount (2 bytes) */
|
||||
Stream_Write_UINT16(s, pdu->cacheEntriesCount);
|
||||
|
||||
for (index = 0; index < pdu->cacheEntriesCount; index++)
|
||||
{
|
||||
const RDPGFX_CACHE_ENTRY_METADATA* cacheEntry = &(pdu->cacheEntries[index]);
|
||||
Stream_Write_UINT64(s, cacheEntry->cacheKey); /* cacheKey (8 bytes) */
|
||||
Stream_Write_UINT32(s, cacheEntry->bitmapLength); /* bitmapLength (4 bytes) */
|
||||
}
|
||||
|
||||
error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
|
||||
NULL);
|
||||
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpgfx_send_cache_offer(RDPGFX_PLUGIN* gfx)
|
||||
{
|
||||
int idx, count;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
PERSISTENT_CACHE_ENTRY entry;
|
||||
RDPGFX_CACHE_IMPORT_OFFER_PDU offer = { 0 };
|
||||
rdpPersistentCache* persistent = NULL;
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*)gfx->iface.pInterface;
|
||||
rdpSettings* settings = gfx->settings;
|
||||
|
||||
if (!settings->BitmapCachePersistEnabled)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (!settings->BitmapCachePersistFile)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
persistent = persistent_cache_new();
|
||||
|
||||
if (!persistent)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
if (persistent_cache_open(persistent, settings->BitmapCachePersistFile, FALSE, 3) < 1) {
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (persistent_cache_get_version(persistent) != 3) {
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
count = persistent_cache_get_count(persistent);
|
||||
|
||||
if (count >= RDPGFX_CACHE_ENTRY_MAX_COUNT)
|
||||
count = RDPGFX_CACHE_ENTRY_MAX_COUNT - 1;
|
||||
|
||||
if (count > gfx->MaxCacheSlots)
|
||||
count = gfx->MaxCacheSlots;
|
||||
|
||||
offer.cacheEntriesCount = (UINT16) count;
|
||||
|
||||
WLog_DBG(TAG, "Sending Cache Import Offer: %d", count);
|
||||
|
||||
for (idx = 0; idx < count; idx++)
|
||||
{
|
||||
if (persistent_cache_read_entry(persistent, &entry) < 1) {
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offer.cacheEntries[idx].cacheKey = entry.key64;
|
||||
offer.cacheEntries[idx].bitmapLength = entry.size;
|
||||
}
|
||||
|
||||
persistent_cache_free(persistent);
|
||||
|
||||
if (offer.cacheEntriesCount > 0) {
|
||||
error = rdpgfx_send_cache_import_offer_pdu(context, &offer);
|
||||
if (error != CHANNEL_RC_OK) {
|
||||
WLog_Print(gfx->log, WLOG_ERROR, "Failed to send cache import offer PDU");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
fail:
|
||||
persistent_cache_free(persistent);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpgfx_load_cache_import_reply(RDPGFX_PLUGIN* gfx, RDPGFX_CACHE_IMPORT_REPLY_PDU* reply)
|
||||
{
|
||||
int idx;
|
||||
int count;
|
||||
UINT16 cacheSlot;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
PERSISTENT_CACHE_ENTRY entry;
|
||||
rdpPersistentCache* persistent = NULL;
|
||||
rdpSettings* settings = gfx->settings;
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*)gfx->iface.pInterface;
|
||||
|
||||
if (!settings->BitmapCachePersistEnabled)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (!settings->BitmapCachePersistFile)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
persistent = persistent_cache_new();
|
||||
|
||||
if (!persistent)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
if (persistent_cache_open(persistent, settings->BitmapCachePersistFile, FALSE, 3) < 1) {
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (persistent_cache_get_version(persistent) != 3) {
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
count = persistent_cache_get_count(persistent);
|
||||
|
||||
count = (count < reply->importedEntriesCount) ? count : reply->importedEntriesCount;
|
||||
|
||||
WLog_DBG(TAG, "Receiving Cache Import Reply: %d", count);
|
||||
|
||||
for (idx = 0; idx < count; idx++)
|
||||
{
|
||||
if (persistent_cache_read_entry(persistent, &entry) < 1) {
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cacheSlot = reply->cacheSlots[idx];
|
||||
|
||||
if (context && context->ImportCacheEntry)
|
||||
context->ImportCacheEntry(context, cacheSlot, &entry);
|
||||
}
|
||||
|
||||
persistent_cache_free(persistent);
|
||||
|
||||
return error;
|
||||
fail:
|
||||
persistent_cache_free(persistent);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -675,7 +939,7 @@ static UINT rdpgfx_recv_evict_cache_entry_pdu(RDPGFX_CHANNEL_CALLBACK* callback,
|
||||
*/
|
||||
static UINT rdpgfx_recv_cache_import_reply_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
UINT16 index;
|
||||
UINT16 idx;
|
||||
RDPGFX_CACHE_IMPORT_REPLY_PDU pdu;
|
||||
RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*)gfx->iface.pInterface;
|
||||
@ -689,22 +953,25 @@ static UINT rdpgfx_recv_cache_import_reply_pdu(RDPGFX_CHANNEL_CALLBACK* callback
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2ull * pdu.importedEntriesCount))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
pdu.cacheSlots = (UINT16*)calloc(pdu.importedEntriesCount, sizeof(UINT16));
|
||||
if (pdu.importedEntriesCount > RDPGFX_CACHE_ENTRY_MAX_COUNT)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (!pdu.cacheSlots)
|
||||
for (idx = 0; idx < pdu.importedEntriesCount; idx++)
|
||||
{
|
||||
WLog_Print(gfx->log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (index = 0; index < pdu.importedEntriesCount; index++)
|
||||
{
|
||||
Stream_Read_UINT16(s, pdu.cacheSlots[index]); /* cacheSlot (2 bytes) */
|
||||
Stream_Read_UINT16(s, pdu.cacheSlots[idx]); /* cacheSlot (2 bytes) */
|
||||
}
|
||||
|
||||
DEBUG_RDPGFX(gfx->log, "RecvCacheImportReplyPdu: importedEntriesCount: %" PRIu16 "",
|
||||
pdu.importedEntriesCount);
|
||||
|
||||
error = rdpgfx_load_cache_import_reply(gfx, &pdu);
|
||||
|
||||
if (error) {
|
||||
WLog_Print(gfx->log, WLOG_ERROR,
|
||||
"rdpgfx_load_cache_import_reply failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (context)
|
||||
{
|
||||
IFCALLRET(context->CacheImportReply, error, context, &pdu);
|
||||
@ -714,7 +981,6 @@ static UINT rdpgfx_recv_cache_import_reply_pdu(RDPGFX_CHANNEL_CALLBACK* callback
|
||||
"context->CacheImportReply failed with error %" PRIu32 "", error);
|
||||
}
|
||||
|
||||
free(pdu.cacheSlots);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1026,6 +1292,7 @@ static UINT rdpgfx_recv_wire_to_surface_2_pdu(RDPGFX_CHANNEL_CALLBACK* callback,
|
||||
Stream_Read_UINT32(s, pdu.bitmapDataLength); /* bitmapDataLength (4 bytes) */
|
||||
pdu.bitmapData = Stream_Pointer(s);
|
||||
Stream_Seek(s, pdu.bitmapDataLength);
|
||||
|
||||
DEBUG_RDPGFX(gfx->log,
|
||||
"RecvWireToSurface2Pdu: surfaceId: %" PRIu16 " codecId: %s (0x%04" PRIX16 ") "
|
||||
"codecContextId: %" PRIu32 " pixelFormat: 0x%02" PRIX8
|
||||
@ -1643,6 +1910,10 @@ static UINT rdpgfx_recv_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
WLog_Print(gfx->log, WLOG_ERROR,
|
||||
"rdpgfx_recv_caps_confirm_pdu failed with error %" PRIu32 "!", error);
|
||||
|
||||
if ((error = rdpgfx_send_cache_offer(gfx)))
|
||||
WLog_Print(gfx->log, WLOG_ERROR,
|
||||
"rdpgfx_send_cache_offer failed with error %" PRIu32 "!", error);
|
||||
|
||||
break;
|
||||
|
||||
case RDPGFX_CMDID_MAPSURFACETOWINDOW:
|
||||
@ -1680,7 +1951,8 @@ static UINT rdpgfx_recv_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
WLog_Print(gfx->log, WLOG_ERROR, "Error while processing GFX cmdId: %s (0x%04" PRIX16 ")",
|
||||
rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId);
|
||||
return error;
|
||||
Stream_SetPosition(s, (beg + header.pduLength));
|
||||
return 0;
|
||||
}
|
||||
|
||||
end = Stream_GetPosition(s);
|
||||
@ -1710,7 +1982,8 @@ static UINT rdpgfx_on_data_received(IWTSVirtualChannelCallback* pChannelCallback
|
||||
RDPGFX_CHANNEL_CALLBACK* callback = (RDPGFX_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
status = zgfx_decompress(gfx->zgfx, Stream_Pointer(data), Stream_GetRemainingLength(data),
|
||||
status = zgfx_decompress(gfx->zgfx, Stream_Pointer(data),
|
||||
(UINT32) Stream_GetRemainingLength(data),
|
||||
&pDstData, &DstSize, 0);
|
||||
|
||||
if (status < 0)
|
||||
@ -1777,11 +2050,20 @@ static UINT rdpgfx_on_open(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
*/
|
||||
static UINT rdpgfx_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
RDPGFX_CHANNEL_CALLBACK* callback = (RDPGFX_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*)gfx->iface.pInterface;
|
||||
|
||||
DEBUG_RDPGFX(gfx->log, "OnClose");
|
||||
error = rdpgfx_save_persistent_cache(gfx);
|
||||
|
||||
if (error) {
|
||||
// print error, but don't consider this a hard failure
|
||||
WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_save_persistent_cache failed with error %" PRIu32 "",
|
||||
error);
|
||||
}
|
||||
|
||||
free_surfaces(context, gfx->SurfaceTable);
|
||||
evict_cache_slots(context, gfx->MaxCacheSlots, gfx->CacheSlots);
|
||||
|
||||
@ -1936,7 +2218,7 @@ static UINT rdpgfx_get_surface_ids(RdpgfxClientContext* context, UINT16** ppSurf
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
pSurfaceIds[index] = pKeys[index] - 1;
|
||||
pSurfaceIds[index] = (UINT16) (pKeys[index] - 1);
|
||||
}
|
||||
|
||||
free(pKeys);
|
||||
@ -1963,7 +2245,7 @@ static void* rdpgfx_get_surface_data(RdpgfxClientContext* context, UINT16 surfac
|
||||
static UINT rdpgfx_set_cache_slot_data(RdpgfxClientContext* context, UINT16 cacheSlot, void* pData)
|
||||
{
|
||||
RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
|
||||
|
||||
|
||||
/* Microsoft uses 1-based indexing for the egfx bitmap cache ! */
|
||||
if (cacheSlot == 0 || cacheSlot > gfx->MaxCacheSlots)
|
||||
{
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <freerdp/client/rdpgfx.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/codec/zgfx.h>
|
||||
#include <freerdp/cache/persistent.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
typedef struct
|
||||
@ -78,6 +79,8 @@ typedef struct
|
||||
|
||||
UINT16 MaxCacheSlots;
|
||||
void* CacheSlots[25600];
|
||||
rdpPersistentCache* persistent;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
|
||||
wLog* log;
|
||||
|
@ -1125,7 +1125,7 @@ static UINT rdpgfx_recv_cache_import_offer_pdu(RdpgfxServerContext* context, wSt
|
||||
{
|
||||
UINT16 index;
|
||||
RDPGFX_CACHE_IMPORT_OFFER_PDU pdu = { 0 };
|
||||
RDPGFX_CACHE_ENTRY_METADATA* cacheEntries;
|
||||
RDPGFX_CACHE_ENTRY_METADATA* cacheEntry;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
|
||||
@ -1144,23 +1144,11 @@ static UINT rdpgfx_recv_cache_import_offer_pdu(RdpgfxServerContext* context, wSt
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 12ull * pdu.cacheEntriesCount))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (pdu.cacheEntriesCount > 0)
|
||||
{
|
||||
pdu.cacheEntries = (RDPGFX_CACHE_ENTRY_METADATA*)calloc(
|
||||
pdu.cacheEntriesCount, sizeof(RDPGFX_CACHE_ENTRY_METADATA));
|
||||
|
||||
if (!pdu.cacheEntries)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
for (index = 0; index < pdu.cacheEntriesCount; index++)
|
||||
{
|
||||
cacheEntries = &(pdu.cacheEntries[index]);
|
||||
Stream_Read_UINT64(s, cacheEntries->cacheKey); /* cacheKey (8 bytes) */
|
||||
Stream_Read_UINT32(s, cacheEntries->bitmapLength); /* bitmapLength (4 bytes) */
|
||||
cacheEntry = &(pdu.cacheEntries[index]);
|
||||
Stream_Read_UINT64(s, cacheEntry->cacheKey); /* cacheKey (8 bytes) */
|
||||
Stream_Read_UINT32(s, cacheEntry->bitmapLength); /* bitmapLength (4 bytes) */
|
||||
}
|
||||
|
||||
if (context)
|
||||
@ -1171,7 +1159,6 @@ static UINT rdpgfx_recv_cache_import_offer_pdu(RdpgfxServerContext* context, wSt
|
||||
WLog_ERR(TAG, "context->CacheImportOffer failed with error %" PRIu32 "", error);
|
||||
}
|
||||
|
||||
free(pdu.cacheEntries);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -3082,6 +3082,17 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
|
||||
{
|
||||
settings->BitmapCacheEnabled = enable;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "persist-cache")
|
||||
{
|
||||
settings->BitmapCachePersistEnabled = enable;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "persist-cache-file")
|
||||
{
|
||||
if (!freerdp_settings_set_string(settings, FreeRDP_BitmapCachePersistFile, arg->Value))
|
||||
return COMMAND_LINE_ERROR_MEMORY;
|
||||
|
||||
settings->BitmapCachePersistEnabled = TRUE;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "offscreen-cache")
|
||||
{
|
||||
settings->OffscreenSupportLevel = (UINT32)enable;
|
||||
|
@ -66,6 +66,10 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = {
|
||||
"Automatic reconnection maximum retries, 0 for unlimited [0,1000]" },
|
||||
{ "bitmap-cache", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
|
||||
"bitmap cache" },
|
||||
{ "persist-cache", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
|
||||
"persistent bitmap cache" },
|
||||
{ "persist-cache-file", COMMAND_LINE_VALUE_REQUIRED, "<filename>", NULL, NULL, -1, NULL,
|
||||
"persistent bitmap cache file" },
|
||||
{ "bpp", COMMAND_LINE_VALUE_REQUIRED, "<depth>", "16", NULL, -1, NULL,
|
||||
"Session bpp (color depth)" },
|
||||
{ "buildconfig", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_BUILDCONFIG, NULL, NULL, NULL, -1,
|
||||
|
2
include/freerdp/cache/bitmap.h
vendored
2
include/freerdp/cache/bitmap.h
vendored
@ -24,6 +24,7 @@
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/update.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/cache/persistent.h>
|
||||
|
||||
#include <winpr/stream.h>
|
||||
|
||||
@ -49,6 +50,7 @@ typedef struct
|
||||
|
||||
/* internal */
|
||||
rdpContext* context;
|
||||
rdpPersistentCache* persistent;
|
||||
} rdpBitmapCache;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
103
include/freerdp/cache/persistent.h
vendored
Normal file
103
include/freerdp/cache/persistent.h
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Persistent Bitmap Cache
|
||||
*
|
||||
* Copyright 2016 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_PERSISTENT_CACHE_H
|
||||
#define FREERDP_PERSISTENT_CACHE_H
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/update.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
typedef struct rdp_persistent_cache rdpPersistentCache;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
/* 12 bytes */
|
||||
|
||||
struct _PERSISTENT_CACHE_HEADER_V3
|
||||
{
|
||||
BYTE sig[8];
|
||||
UINT32 flags; /* 0x00000003, 0x00000006 */
|
||||
};
|
||||
typedef struct _PERSISTENT_CACHE_HEADER_V3 PERSISTENT_CACHE_HEADER_V3;
|
||||
|
||||
/* 12 bytes */
|
||||
|
||||
struct _PERSISTENT_CACHE_ENTRY_V3
|
||||
{
|
||||
UINT64 key64;
|
||||
UINT16 width;
|
||||
UINT16 height;
|
||||
};
|
||||
typedef struct _PERSISTENT_CACHE_ENTRY_V3 PERSISTENT_CACHE_ENTRY_V3;
|
||||
|
||||
/* 20 bytes */
|
||||
|
||||
struct _PERSISTENT_CACHE_ENTRY_V2
|
||||
{
|
||||
UINT64 key64;
|
||||
UINT16 width;
|
||||
UINT16 height;
|
||||
UINT32 size;
|
||||
UINT32 flags; /* 0x00000011 */
|
||||
};
|
||||
typedef struct _PERSISTENT_CACHE_ENTRY_V2 PERSISTENT_CACHE_ENTRY_V2;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
struct _PERSISTENT_CACHE_ENTRY
|
||||
{
|
||||
UINT64 key64;
|
||||
UINT16 width;
|
||||
UINT16 height;
|
||||
UINT32 size;
|
||||
UINT32 flags;
|
||||
BYTE* data;
|
||||
};
|
||||
typedef struct _PERSISTENT_CACHE_ENTRY PERSISTENT_CACHE_ENTRY;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
FREERDP_API int persistent_cache_get_version(rdpPersistentCache* persistent);
|
||||
FREERDP_API int persistent_cache_get_count(rdpPersistentCache* persistent);
|
||||
|
||||
FREERDP_API int persistent_cache_read_entry(rdpPersistentCache* persistent,
|
||||
PERSISTENT_CACHE_ENTRY* entry);
|
||||
FREERDP_API int persistent_cache_write_entry(rdpPersistentCache* persistent,
|
||||
const PERSISTENT_CACHE_ENTRY* entry);
|
||||
|
||||
FREERDP_API int persistent_cache_open(rdpPersistentCache* persistent, const char* filename,
|
||||
BOOL write, UINT32 version);
|
||||
FREERDP_API int persistent_cache_close(rdpPersistentCache* persistent);
|
||||
|
||||
FREERDP_API rdpPersistentCache* persistent_cache_new(void);
|
||||
FREERDP_API void persistent_cache_free(rdpPersistentCache* persistent);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_PERSISTENT_CACHE_H */
|
@ -309,16 +309,18 @@ typedef struct
|
||||
UINT32 bitmapLength;
|
||||
} RDPGFX_CACHE_ENTRY_METADATA;
|
||||
|
||||
#define RDPGFX_CACHE_ENTRY_MAX_COUNT 5462
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT16 cacheEntriesCount;
|
||||
RDPGFX_CACHE_ENTRY_METADATA* cacheEntries;
|
||||
RDPGFX_CACHE_ENTRY_METADATA cacheEntries[RDPGFX_CACHE_ENTRY_MAX_COUNT];
|
||||
} RDPGFX_CACHE_IMPORT_OFFER_PDU;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT16 importedEntriesCount;
|
||||
UINT16* cacheSlots;
|
||||
UINT16 cacheSlots[RDPGFX_CACHE_ENTRY_MAX_COUNT];
|
||||
} RDPGFX_CACHE_IMPORT_REPLY_PDU;
|
||||
|
||||
typedef struct
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include <freerdp/channels/rdpgfx.h>
|
||||
#include <freerdp/utils/profiler.h>
|
||||
|
||||
#include <freerdp/cache/persistent.h>
|
||||
|
||||
/**
|
||||
* Client Interface
|
||||
*/
|
||||
@ -60,6 +62,10 @@ typedef UINT (*pcRdpgfxCacheImportReply)(RdpgfxClientContext* context,
|
||||
const RDPGFX_CACHE_IMPORT_REPLY_PDU* cacheImportReply);
|
||||
typedef UINT (*pcRdpgfxEvictCacheEntry)(RdpgfxClientContext* context,
|
||||
const RDPGFX_EVICT_CACHE_ENTRY_PDU* evictCacheEntry);
|
||||
typedef UINT (*pcRdpgfxImportCacheEntry)(RdpgfxClientContext* context, UINT16 cacheSlot,
|
||||
PERSISTENT_CACHE_ENTRY* importCacheEntry);
|
||||
typedef UINT (*pcRdpgfxExportCacheEntry)(RdpgfxClientContext* context, UINT16 cacheSlot,
|
||||
PERSISTENT_CACHE_ENTRY* importCacheEntry);
|
||||
typedef UINT (*pcRdpgfxMapSurfaceToOutput)(RdpgfxClientContext* context,
|
||||
const RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* surfaceToOutput);
|
||||
typedef UINT (*pcRdpgfxMapSurfaceToScaledOutput)(
|
||||
@ -116,6 +122,8 @@ struct s_rdpgfx_client_context
|
||||
pcRdpgfxCacheToSurface CacheToSurface;
|
||||
pcRdpgfxCacheImportOffer CacheImportOffer;
|
||||
pcRdpgfxCacheImportReply CacheImportReply;
|
||||
pcRdpgfxImportCacheEntry ImportCacheEntry;
|
||||
pcRdpgfxExportCacheEntry ExportCacheEntry;
|
||||
pcRdpgfxEvictCacheEntry EvictCacheEntry;
|
||||
pcRdpgfxMapSurfaceToOutput MapSurfaceToOutput;
|
||||
pcRdpgfxMapSurfaceToScaledOutput MapSurfaceToScaledOutput;
|
||||
|
@ -65,7 +65,8 @@ extern "C"
|
||||
UINT32 flags; /* 23 */
|
||||
UINT32 length; /* 24 */
|
||||
BYTE* data; /* 25 */
|
||||
UINT32 paddingB[32 - 26]; /* 26 */
|
||||
UINT64 key64; /* 26 */
|
||||
UINT32 paddingB[32 - 27]; /* 27 */
|
||||
|
||||
BOOL compressed; /* 32 */
|
||||
BOOL ephemeral; /* 33 */
|
||||
|
@ -803,6 +803,7 @@ typedef struct
|
||||
#define FreeRDP_BitmapCachePersistEnabled (2500)
|
||||
#define FreeRDP_BitmapCacheV2NumCells (2501)
|
||||
#define FreeRDP_BitmapCacheV2CellInfo (2502)
|
||||
#define FreeRDP_BitmapCachePersistFile (2503)
|
||||
#define FreeRDP_ColorPointerFlag (2560)
|
||||
#define FreeRDP_PointerCacheSize (2561)
|
||||
#define FreeRDP_KeyboardRemappingList (2622)
|
||||
@ -1374,7 +1375,8 @@ struct rdp_settings
|
||||
ALIGN64 BOOL BitmapCachePersistEnabled; /* 2500 */
|
||||
ALIGN64 UINT32 BitmapCacheV2NumCells; /* 2501 */
|
||||
ALIGN64 BITMAP_CACHE_V2_CELL_INFO* BitmapCacheV2CellInfo; /* 2502 */
|
||||
UINT64 padding2560[2560 - 2503]; /* 2503 */
|
||||
ALIGN64 char* BitmapCachePersistFile; /* 2503 */
|
||||
UINT64 padding2560[2560 - 2504]; /* 2504 */
|
||||
|
||||
/* Pointer Capabilities */
|
||||
ALIGN64 BOOL ColorPointerFlag; /* 2560 */
|
||||
|
1
libfreerdp/cache/CMakeLists.txt
vendored
1
libfreerdp/cache/CMakeLists.txt
vendored
@ -25,6 +25,7 @@ freerdp_module_add(
|
||||
pointer.h
|
||||
bitmap.c
|
||||
bitmap.h
|
||||
persistent.c
|
||||
nine_grid.c
|
||||
offscreen.c
|
||||
palette.c
|
||||
|
105
libfreerdp/cache/bitmap.c
vendored
105
libfreerdp/cache/bitmap.c
vendored
@ -142,6 +142,8 @@ static BOOL update_gdi_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDE
|
||||
return FALSE;
|
||||
|
||||
const UINT32 ColorDepth = freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
|
||||
bitmap->key64 = ((UINT64)cacheBitmapV2->key1 | (((UINT64)cacheBitmapV2->key2) << 32));
|
||||
|
||||
if (!cacheBitmapV2->bitmapBpp)
|
||||
cacheBitmapV2->bitmapBpp = ColorDepth;
|
||||
|
||||
@ -184,6 +186,8 @@ static BOOL update_gdi_cache_bitmap_v3(rdpContext* context, CACHE_BITMAP_V3_ORDE
|
||||
return FALSE;
|
||||
|
||||
const UINT32 ColorDepth = freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
|
||||
bitmap->key64 = ((UINT64)cacheBitmapV3->key1 | (((UINT64)cacheBitmapV3->key2) << 32));
|
||||
|
||||
if (!cacheBitmapV3->bpp)
|
||||
cacheBitmapV3->bpp = ColorDepth;
|
||||
|
||||
@ -278,6 +282,67 @@ void bitmap_cache_register_callbacks(rdpUpdate* update)
|
||||
}
|
||||
}
|
||||
|
||||
int bitmap_cache_save_persistent(rdpBitmapCache* bitmapCache)
|
||||
{
|
||||
int status;
|
||||
UINT32 i, j;
|
||||
UINT32 version;
|
||||
rdpPersistentCache* persistent;
|
||||
rdpContext* context = bitmapCache->context;
|
||||
rdpSettings* settings = context->settings;
|
||||
|
||||
version = settings->BitmapCacheVersion;
|
||||
|
||||
if (version != 2)
|
||||
return 0; /* persistent bitmap cache already saved in egfx channel */
|
||||
|
||||
if (!settings->BitmapCachePersistEnabled)
|
||||
return 0;
|
||||
|
||||
if (!settings->BitmapCachePersistFile)
|
||||
return 0;
|
||||
|
||||
persistent = persistent_cache_new();
|
||||
|
||||
if (!persistent)
|
||||
return -1;
|
||||
|
||||
status = persistent_cache_open(persistent, settings->BitmapCachePersistFile, TRUE, version);
|
||||
|
||||
if (status < 1)
|
||||
goto end;
|
||||
|
||||
for (i = 0; i < bitmapCache->maxCells; i++)
|
||||
{
|
||||
for (j = 0; j < bitmapCache->cells[i].number + 1; j++)
|
||||
{
|
||||
PERSISTENT_CACHE_ENTRY cacheEntry;
|
||||
rdpBitmap* bitmap = bitmapCache->cells[i].entries[j];
|
||||
|
||||
if (!bitmap || !bitmap->key64)
|
||||
continue;
|
||||
|
||||
cacheEntry.key64 = bitmap->key64;
|
||||
cacheEntry.width = bitmap->width;
|
||||
cacheEntry.height = bitmap->height;
|
||||
cacheEntry.size = (UINT32)(bitmap->width * bitmap->height * 4);
|
||||
cacheEntry.flags = 0;
|
||||
cacheEntry.data = bitmap->data;
|
||||
|
||||
if (persistent_cache_write_entry(persistent, &cacheEntry) < 1) {
|
||||
status = -1;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status = 1;
|
||||
|
||||
end:
|
||||
persistent_cache_free(persistent);
|
||||
return status;
|
||||
}
|
||||
|
||||
rdpBitmapCache* bitmap_cache_new(rdpContext* context)
|
||||
{
|
||||
UINT32 i;
|
||||
@ -326,27 +391,33 @@ fail:
|
||||
|
||||
void bitmap_cache_free(rdpBitmapCache* bitmapCache)
|
||||
{
|
||||
if (bitmapCache)
|
||||
{
|
||||
UINT32 i;
|
||||
for (i = 0; i < bitmapCache->maxCells; i++)
|
||||
{
|
||||
UINT32 j;
|
||||
BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
|
||||
if (!cell->entries)
|
||||
continue;
|
||||
for (j = 0; j < cell->number + 1; j++)
|
||||
{
|
||||
rdpBitmap* bitmap = cell->entries[j];
|
||||
Bitmap_Free(bitmapCache->context, bitmap);
|
||||
}
|
||||
if (!bitmapCache)
|
||||
return;
|
||||
|
||||
free(bitmapCache->cells[i].entries);
|
||||
bitmap_cache_save_persistent(bitmapCache);
|
||||
|
||||
UINT32 i;
|
||||
for (i = 0; i < bitmapCache->maxCells; i++)
|
||||
{
|
||||
UINT32 j;
|
||||
BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
|
||||
|
||||
if (!cell->entries)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < cell->number + 1; j++)
|
||||
{
|
||||
rdpBitmap* bitmap = cell->entries[j];
|
||||
Bitmap_Free(bitmapCache->context, bitmap);
|
||||
}
|
||||
|
||||
free(bitmapCache->cells);
|
||||
free(bitmapCache);
|
||||
free(bitmapCache->cells[i].entries);
|
||||
}
|
||||
|
||||
free(bitmapCache->cells);
|
||||
persistent_cache_free(bitmapCache->persistent);
|
||||
|
||||
free(bitmapCache);
|
||||
}
|
||||
|
||||
static void free_bitmap_data(BITMAP_DATA* data, size_t count)
|
||||
|
342
libfreerdp/cache/persistent.c
vendored
Normal file
342
libfreerdp/cache/persistent.c
vendored
Normal file
@ -0,0 +1,342 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Persistent Bitmap Cache
|
||||
*
|
||||
* Copyright 2016 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/constants.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#include <freerdp/cache/persistent.h>
|
||||
|
||||
#define TAG FREERDP_TAG("cache.persistent")
|
||||
|
||||
struct rdp_persistent_cache
|
||||
{
|
||||
FILE* fp;
|
||||
BOOL write;
|
||||
UINT32 version;
|
||||
int count;
|
||||
char* filename;
|
||||
BYTE* bmpData;
|
||||
UINT32 bmpSize;
|
||||
};
|
||||
|
||||
int persistent_cache_get_version(rdpPersistentCache* persistent)
|
||||
{
|
||||
return persistent->version;
|
||||
}
|
||||
|
||||
int persistent_cache_get_count(rdpPersistentCache* persistent)
|
||||
{
|
||||
return persistent->count;
|
||||
}
|
||||
|
||||
int persistent_cache_read_entry_v2(rdpPersistentCache* persistent, PERSISTENT_CACHE_ENTRY* entry)
|
||||
{
|
||||
PERSISTENT_CACHE_ENTRY_V2 entry2;
|
||||
|
||||
if (fread((void*)&entry2, 1, sizeof(PERSISTENT_CACHE_ENTRY_V2), persistent->fp) != sizeof(PERSISTENT_CACHE_ENTRY_V2))
|
||||
return -1;
|
||||
|
||||
entry->key64 = entry2.key64;
|
||||
entry->width = entry2.width;
|
||||
entry->height = entry2.height;
|
||||
entry->size = entry2.width * entry2.height * 4;
|
||||
entry->flags = entry2.flags;
|
||||
|
||||
entry->data = persistent->bmpData;
|
||||
|
||||
if (fread((void*)entry->data, 1, 0x4000, persistent->fp) != 0x4000)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_cache_write_entry_v2(rdpPersistentCache* persistent, const PERSISTENT_CACHE_ENTRY* entry)
|
||||
{
|
||||
int padding;
|
||||
PERSISTENT_CACHE_ENTRY_V2 entry2;
|
||||
|
||||
entry2.key64 = entry->key64;
|
||||
entry2.width = entry->width;
|
||||
entry2.height = entry->height;
|
||||
entry2.size = entry->size;
|
||||
entry2.flags = entry->flags;
|
||||
|
||||
if (!entry2.flags)
|
||||
entry2.flags = 0x00000011;
|
||||
|
||||
if (fwrite((void*)&entry2, 1, sizeof(PERSISTENT_CACHE_ENTRY_V2), persistent->fp) != sizeof(PERSISTENT_CACHE_ENTRY_V2))
|
||||
return -1;
|
||||
|
||||
if (fwrite((void*)entry->data, 1, entry->size, persistent->fp) != entry->size)
|
||||
return -1;
|
||||
|
||||
if (0x4000 > entry->size)
|
||||
{
|
||||
padding = 0x4000 - entry->size;
|
||||
|
||||
if (fwrite((void*)persistent->bmpData, 1, padding, persistent->fp) != padding)
|
||||
return -1;
|
||||
}
|
||||
|
||||
persistent->count++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_cache_read_v2(rdpPersistentCache* persistent)
|
||||
{
|
||||
PERSISTENT_CACHE_ENTRY_V2 entry;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (fread((void*)&entry, 1, sizeof(PERSISTENT_CACHE_ENTRY_V2), persistent->fp) != sizeof(PERSISTENT_CACHE_ENTRY_V2))
|
||||
break;
|
||||
|
||||
if (fseek(persistent->fp, 0x4000, SEEK_CUR) != 0)
|
||||
break;
|
||||
|
||||
persistent->count++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_cache_read_entry_v3(rdpPersistentCache* persistent, PERSISTENT_CACHE_ENTRY* entry)
|
||||
{
|
||||
PERSISTENT_CACHE_ENTRY_V3 entry3;
|
||||
|
||||
if (fread((void*)&entry3, 1, sizeof(PERSISTENT_CACHE_ENTRY_V3), persistent->fp) != sizeof(PERSISTENT_CACHE_ENTRY_V3))
|
||||
return -1;
|
||||
|
||||
entry->key64 = entry3.key64;
|
||||
entry->width = entry3.width;
|
||||
entry->height = entry3.height;
|
||||
entry->size = entry3.width * entry3.height * 4;
|
||||
entry->flags = 0;
|
||||
|
||||
if (entry->size > persistent->bmpSize)
|
||||
{
|
||||
persistent->bmpSize = entry->size;
|
||||
BYTE* bmpData = (BYTE*)realloc(persistent->bmpData, persistent->bmpSize);
|
||||
|
||||
if (!bmpData) {
|
||||
free(persistent->bmpData);
|
||||
persistent->bmpData = NULL;
|
||||
persistent->bmpSize = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
persistent->bmpData = bmpData;
|
||||
}
|
||||
|
||||
entry->data = persistent->bmpData;
|
||||
|
||||
if (fread((void*)entry->data, 1, entry->size, persistent->fp) != entry->size)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_cache_write_entry_v3(rdpPersistentCache* persistent, const PERSISTENT_CACHE_ENTRY* entry)
|
||||
{
|
||||
PERSISTENT_CACHE_ENTRY_V3 entry3;
|
||||
|
||||
entry3.key64 = entry->key64;
|
||||
entry3.width = entry->width;
|
||||
entry3.height = entry->height;
|
||||
|
||||
if (fwrite((void*)&entry3, 1, sizeof(PERSISTENT_CACHE_ENTRY_V3), persistent->fp) != sizeof(PERSISTENT_CACHE_ENTRY_V3))
|
||||
return -1;
|
||||
|
||||
if (fwrite((void*)entry->data, 1, entry->size, persistent->fp) != entry->size)
|
||||
return -1;
|
||||
|
||||
persistent->count++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_cache_read_v3(rdpPersistentCache* persistent)
|
||||
{
|
||||
PERSISTENT_CACHE_ENTRY_V3 entry;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (fread((void*)&entry, 1, sizeof(PERSISTENT_CACHE_ENTRY_V3), persistent->fp) != sizeof(PERSISTENT_CACHE_ENTRY_V3))
|
||||
break;
|
||||
|
||||
if (fseek(persistent->fp, (entry.width * entry.height * 4), SEEK_CUR) != 0)
|
||||
break;
|
||||
|
||||
persistent->count++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_cache_read_entry(rdpPersistentCache* persistent, PERSISTENT_CACHE_ENTRY* entry)
|
||||
{
|
||||
if (persistent->version == 3)
|
||||
return persistent_cache_read_entry_v3(persistent, entry);
|
||||
else if (persistent->version == 2)
|
||||
return persistent_cache_read_entry_v2(persistent, entry);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int persistent_cache_write_entry(rdpPersistentCache* persistent, const PERSISTENT_CACHE_ENTRY* entry)
|
||||
{
|
||||
if (persistent->version == 3)
|
||||
return persistent_cache_write_entry_v3(persistent, entry);
|
||||
else if (persistent->version == 2)
|
||||
return persistent_cache_write_entry_v2(persistent, entry);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int persistent_cache_open_read(rdpPersistentCache* persistent)
|
||||
{
|
||||
BYTE sig[8];
|
||||
int status = 1;
|
||||
PERSISTENT_CACHE_HEADER_V3 header;
|
||||
|
||||
persistent->fp = fopen(persistent->filename, "rb");
|
||||
|
||||
if (!persistent->fp)
|
||||
return -1;
|
||||
|
||||
if (fread(sig, 1, 8, persistent->fp) != 8)
|
||||
return -1;
|
||||
|
||||
if (!strncmp((const char*) sig, "RDP8bmp", 8))
|
||||
persistent->version = 3;
|
||||
else
|
||||
persistent->version = 2;
|
||||
|
||||
fseek(persistent->fp, 0, SEEK_SET);
|
||||
|
||||
if (persistent->version == 3)
|
||||
{
|
||||
if (fread(&header, 1, sizeof(PERSISTENT_CACHE_HEADER_V3), persistent->fp) != sizeof(PERSISTENT_CACHE_HEADER_V3))
|
||||
return -1;
|
||||
|
||||
status = persistent_cache_read_v3(persistent);
|
||||
|
||||
fseek(persistent->fp, sizeof(PERSISTENT_CACHE_HEADER_V3), SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = persistent_cache_read_v2(persistent);
|
||||
|
||||
fseek(persistent->fp, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int persistent_cache_open_write(rdpPersistentCache* persistent)
|
||||
{
|
||||
PERSISTENT_CACHE_HEADER_V3 header;
|
||||
|
||||
persistent->fp = fopen(persistent->filename, "w+b");
|
||||
|
||||
if (!persistent->fp)
|
||||
return -1;
|
||||
|
||||
if (persistent->version == 3)
|
||||
{
|
||||
strncpy((char*) header.sig, "RDP8bmp", 8);
|
||||
header.flags = 0x00000006;
|
||||
|
||||
if (fwrite(&header, 1, sizeof(PERSISTENT_CACHE_HEADER_V3), persistent->fp) != sizeof(PERSISTENT_CACHE_HEADER_V3))
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZeroMemory(persistent->bmpData, persistent->bmpSize);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_cache_open(rdpPersistentCache* persistent, const char* filename, BOOL write,
|
||||
UINT32 version)
|
||||
{
|
||||
persistent->write = write;
|
||||
|
||||
persistent->filename = _strdup(filename);
|
||||
|
||||
if (!persistent->filename)
|
||||
return -1;
|
||||
|
||||
if (persistent->write)
|
||||
{
|
||||
persistent->version = version;
|
||||
return persistent_cache_open_write(persistent);
|
||||
}
|
||||
|
||||
return persistent_cache_open_read(persistent);
|
||||
}
|
||||
|
||||
int persistent_cache_close(rdpPersistentCache* persistent)
|
||||
{
|
||||
if (persistent->fp)
|
||||
{
|
||||
fclose(persistent->fp);
|
||||
persistent->fp = NULL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
rdpPersistentCache* persistent_cache_new(void)
|
||||
{
|
||||
rdpPersistentCache* persistent;
|
||||
|
||||
persistent = (rdpPersistentCache*)calloc(1, sizeof(rdpPersistentCache));
|
||||
|
||||
if (!persistent)
|
||||
return NULL;
|
||||
|
||||
persistent->bmpSize = 0x4000;
|
||||
persistent->bmpData = (BYTE*)malloc(persistent->bmpSize);
|
||||
|
||||
if (!persistent->bmpData)
|
||||
return NULL;
|
||||
|
||||
return persistent;
|
||||
}
|
||||
|
||||
void persistent_cache_free(rdpPersistentCache* persistent)
|
||||
{
|
||||
if (!persistent)
|
||||
return;
|
||||
|
||||
persistent_cache_close(persistent);
|
||||
|
||||
free(persistent->filename);
|
||||
|
||||
free(persistent->bmpData);
|
||||
|
||||
free(persistent);
|
||||
}
|
@ -2361,6 +2361,9 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id)
|
||||
case FreeRDP_AuthenticationServiceClass:
|
||||
return settings->AuthenticationServiceClass;
|
||||
|
||||
case FreeRDP_BitmapCachePersistFile:
|
||||
return settings->BitmapCachePersistFile;
|
||||
|
||||
case FreeRDP_CardName:
|
||||
return settings->CardName;
|
||||
|
||||
@ -2622,6 +2625,9 @@ char* freerdp_settings_get_string_writable(rdpSettings* settings, size_t id)
|
||||
case FreeRDP_AuthenticationServiceClass:
|
||||
return settings->AuthenticationServiceClass;
|
||||
|
||||
case FreeRDP_BitmapCachePersistFile:
|
||||
return settings->BitmapCachePersistFile;
|
||||
|
||||
case FreeRDP_CardName:
|
||||
return settings->CardName;
|
||||
|
||||
@ -2893,6 +2899,9 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char*
|
||||
case FreeRDP_AuthenticationServiceClass:
|
||||
return update_string(&settings->AuthenticationServiceClass, cnv.cc, len, cleanup);
|
||||
|
||||
case FreeRDP_BitmapCachePersistFile:
|
||||
return update_string(&settings->BitmapCachePersistFile, cnv.cc, len, cleanup);
|
||||
|
||||
case FreeRDP_CardName:
|
||||
return update_string(&settings->CardName, cnv.cc, len, cleanup);
|
||||
|
||||
|
@ -316,6 +316,7 @@ static const struct settings_str_entry settings_map[] = {
|
||||
{ FreeRDP_AlternateShell, 7, "FreeRDP_AlternateShell" },
|
||||
{ FreeRDP_AssistanceFile, 7, "FreeRDP_AssistanceFile" },
|
||||
{ FreeRDP_AuthenticationServiceClass, 7, "FreeRDP_AuthenticationServiceClass" },
|
||||
{ FreeRDP_BitmapCachePersistFile, 7, "FreeRDP_BitmapCachePersistFile" },
|
||||
{ FreeRDP_CardName, 7, "FreeRDP_CardName" },
|
||||
{ FreeRDP_CertificateAcceptedFingerprints, 7, "FreeRDP_CertificateAcceptedFingerprints" },
|
||||
{ FreeRDP_CertificateContent, 7, "FreeRDP_CertificateContent" },
|
||||
|
@ -236,53 +236,173 @@ BOOL rdp_send_client_control_pdu(rdpRdp* rdp, UINT16 action)
|
||||
return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_CONTROL, rdp->mcs->userId);
|
||||
}
|
||||
|
||||
static BOOL rdp_write_persistent_list_entry(wStream* s, UINT32 key1, UINT32 key2)
|
||||
static BOOL rdp_write_client_persistent_key_list_pdu(wStream* s, RDP_BITMAP_PERSISTENT_INFO* info)
|
||||
{
|
||||
WINPR_ASSERT(s);
|
||||
int index;
|
||||
UINT32 key1;
|
||||
UINT32 key2;
|
||||
|
||||
if (Stream_GetRemainingCapacity(s) < 8)
|
||||
return FALSE;
|
||||
Stream_Write_UINT32(s, key1); /* key1 (4 bytes) */
|
||||
Stream_Write_UINT32(s, key2); /* key2 (4 bytes) */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdp_write_client_persistent_key_list_pdu(wStream* s, const rdpSettings* settings)
|
||||
{
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(settings);
|
||||
WINPR_ASSERT(info);
|
||||
|
||||
if (Stream_GetRemainingCapacity(s) < 24)
|
||||
return FALSE;
|
||||
Stream_Write_UINT16(s, 0); /* numEntriesCache0 (2 bytes) */
|
||||
Stream_Write_UINT16(s, 0); /* numEntriesCache1 (2 bytes) */
|
||||
Stream_Write_UINT16(s, 0); /* numEntriesCache2 (2 bytes) */
|
||||
Stream_Write_UINT16(s, 0); /* numEntriesCache3 (2 bytes) */
|
||||
Stream_Write_UINT16(s, 0); /* numEntriesCache4 (2 bytes) */
|
||||
Stream_Write_UINT16(s, 0); /* totalEntriesCache0 (2 bytes) */
|
||||
Stream_Write_UINT16(s, 0); /* totalEntriesCache1 (2 bytes) */
|
||||
Stream_Write_UINT16(s, 0); /* totalEntriesCache2 (2 bytes) */
|
||||
Stream_Write_UINT16(s, 0); /* totalEntriesCache3 (2 bytes) */
|
||||
Stream_Write_UINT16(s, 0); /* totalEntriesCache4 (2 bytes) */
|
||||
|
||||
Stream_Write_UINT16(s, info->numEntriesCache0); /* numEntriesCache0 (2 bytes) */
|
||||
Stream_Write_UINT16(s, info->numEntriesCache1); /* numEntriesCache1 (2 bytes) */
|
||||
Stream_Write_UINT16(s, info->numEntriesCache2); /* numEntriesCache2 (2 bytes) */
|
||||
Stream_Write_UINT16(s, info->numEntriesCache3); /* numEntriesCache3 (2 bytes) */
|
||||
Stream_Write_UINT16(s, info->numEntriesCache4); /* numEntriesCache4 (2 bytes) */
|
||||
Stream_Write_UINT16(s, info->totalEntriesCache0); /* totalEntriesCache0 (2 bytes) */
|
||||
Stream_Write_UINT16(s, info->totalEntriesCache1); /* totalEntriesCache1 (2 bytes) */
|
||||
Stream_Write_UINT16(s, info->totalEntriesCache2); /* totalEntriesCache2 (2 bytes) */
|
||||
Stream_Write_UINT16(s, info->totalEntriesCache3); /* totalEntriesCache3 (2 bytes) */
|
||||
Stream_Write_UINT16(s, info->totalEntriesCache4); /* totalEntriesCache4 (2 bytes) */
|
||||
Stream_Write_UINT8(s, PERSIST_FIRST_PDU | PERSIST_LAST_PDU); /* bBitMask (1 byte) */
|
||||
Stream_Write_UINT8(s, 0); /* pad1 (1 byte) */
|
||||
Stream_Write_UINT16(s, 0); /* pad3 (2 bytes) */
|
||||
/* entries */
|
||||
|
||||
Stream_EnsureRemainingCapacity(s, info->keyCount * 8);
|
||||
|
||||
for (index = 0; index < info->keyCount; index++)
|
||||
{
|
||||
key1 = (UINT32)info->keyList[index];
|
||||
key2 = (UINT32)(info->keyList[index] >> 32);
|
||||
Stream_Write_UINT32(s, key1);
|
||||
Stream_Write_UINT32(s, key2);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UINT32 rdp_load_persistent_key_list(rdpRdp* rdp, UINT64** pKeyList)
|
||||
{
|
||||
int index;
|
||||
int count;
|
||||
int status;
|
||||
UINT32 keyCount;
|
||||
UINT64* keyList = NULL;
|
||||
rdpPersistentCache* persistent;
|
||||
PERSISTENT_CACHE_ENTRY cacheEntry;
|
||||
rdpSettings* settings = rdp->settings;
|
||||
|
||||
*pKeyList = NULL;
|
||||
|
||||
if (!settings->BitmapCachePersistEnabled)
|
||||
return 0;
|
||||
|
||||
if (!settings->BitmapCachePersistFile)
|
||||
return 0;
|
||||
|
||||
persistent = persistent_cache_new();
|
||||
|
||||
if (!persistent)
|
||||
return 0;
|
||||
|
||||
status = persistent_cache_open(persistent, settings->BitmapCachePersistFile, FALSE, 0);
|
||||
|
||||
if (status < 1)
|
||||
goto error;
|
||||
|
||||
count = persistent_cache_get_count(persistent);
|
||||
|
||||
keyCount = (UINT32)count;
|
||||
keyList = (UINT64*)malloc(keyCount * sizeof(UINT64));
|
||||
|
||||
if (!keyList)
|
||||
goto error;
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
if (persistent_cache_read_entry(persistent, &cacheEntry) < 1)
|
||||
continue;
|
||||
|
||||
keyList[index] = cacheEntry.key64;
|
||||
}
|
||||
|
||||
*pKeyList = keyList;
|
||||
|
||||
persistent_cache_free(persistent);
|
||||
return keyCount;
|
||||
error:
|
||||
persistent_cache_free(persistent);
|
||||
free(keyList);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL rdp_send_client_persistent_key_list_pdu(rdpRdp* rdp)
|
||||
{
|
||||
UINT32 keyCount;
|
||||
UINT32 keyMaxFrag = 2042;
|
||||
UINT64* keyList = NULL;
|
||||
RDP_BITMAP_PERSISTENT_INFO info;
|
||||
rdpSettings* settings = rdp->settings;
|
||||
|
||||
keyCount = rdp_load_persistent_key_list(rdp, &keyList);
|
||||
|
||||
WLog_DBG(TAG, "Persistent Key List: TotalKeyCount: %d MaxKeyFrag: %d", keyCount, keyMaxFrag);
|
||||
|
||||
// MS-RDPBCGR recommends sending no more than 169 entries at once.
|
||||
// In practice, sending more than 2042 entries at once triggers an error.
|
||||
// It should be possible to advertise the entire client bitmap cache
|
||||
// by sending multiple persistent key list PDUs, but the current code
|
||||
// only bothers sending a single, smaller list of entries instead.
|
||||
|
||||
if (keyCount > keyMaxFrag)
|
||||
keyCount = keyMaxFrag;
|
||||
|
||||
info.totalEntriesCache0 = settings->BitmapCacheV2CellInfo[0].numEntries;
|
||||
info.totalEntriesCache1 = settings->BitmapCacheV2CellInfo[1].numEntries;
|
||||
info.totalEntriesCache2 = settings->BitmapCacheV2CellInfo[2].numEntries;
|
||||
info.totalEntriesCache3 = settings->BitmapCacheV2CellInfo[3].numEntries;
|
||||
info.totalEntriesCache4 = settings->BitmapCacheV2CellInfo[4].numEntries;
|
||||
|
||||
info.numEntriesCache0 = MIN(keyCount, info.totalEntriesCache0);
|
||||
keyCount -= info.numEntriesCache0;
|
||||
info.numEntriesCache1 = MIN(keyCount, info.totalEntriesCache1);
|
||||
keyCount -= info.numEntriesCache1;
|
||||
info.numEntriesCache2 = MIN(keyCount, info.totalEntriesCache2);
|
||||
keyCount -= info.numEntriesCache2;
|
||||
info.numEntriesCache3 = MIN(keyCount, info.totalEntriesCache3);
|
||||
keyCount -= info.numEntriesCache3;
|
||||
info.numEntriesCache4 = MIN(keyCount, info.totalEntriesCache4);
|
||||
keyCount -= info.numEntriesCache4;
|
||||
|
||||
info.totalEntriesCache0 = info.numEntriesCache0;
|
||||
info.totalEntriesCache1 = info.numEntriesCache1;
|
||||
info.totalEntriesCache2 = info.numEntriesCache2;
|
||||
info.totalEntriesCache3 = info.numEntriesCache3;
|
||||
info.totalEntriesCache4 = info.numEntriesCache4;
|
||||
|
||||
keyCount = info.totalEntriesCache0 + info.totalEntriesCache1 + info.totalEntriesCache2 +
|
||||
info.totalEntriesCache3 + info.totalEntriesCache4;
|
||||
|
||||
info.keyCount = keyCount;
|
||||
info.keyList = keyList;
|
||||
|
||||
WLog_DBG(TAG, "persistentKeyList count: %d", info.keyCount);
|
||||
|
||||
WLog_DBG(TAG, "numEntriesCache: 0: %d 1: %d 2: %d 3: %d 4: %d",
|
||||
info.numEntriesCache0, info.numEntriesCache1, info.numEntriesCache2, info.numEntriesCache3, info.numEntriesCache4);
|
||||
|
||||
WLog_DBG(TAG, "totalEntriesCache: 0: %d 1: %d 2: %d 3: %d 4: %d",
|
||||
info.totalEntriesCache0, info.totalEntriesCache1, info.totalEntriesCache2, info.totalEntriesCache3, info.totalEntriesCache4);
|
||||
|
||||
wStream* s = rdp_data_pdu_init(rdp);
|
||||
|
||||
if (!s)
|
||||
return FALSE;
|
||||
if (!rdp_write_client_persistent_key_list_pdu(s, rdp->settings))
|
||||
|
||||
if (!rdp_write_client_persistent_key_list_pdu(s, &info))
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WINPR_ASSERT(rdp->mcs);
|
||||
free(keyList);
|
||||
|
||||
return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_BITMAP_CACHE_PERSISTENT_LIST, rdp->mcs->userId);
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/settings.h>
|
||||
#include <freerdp/cache/persistent.h>
|
||||
|
||||
#define SYNCMSGTYPE_SYNC 0x0001
|
||||
|
||||
@ -32,6 +33,24 @@
|
||||
#define CTRLACTION_DETACH 0x0003
|
||||
#define CTRLACTION_COOPERATE 0x0004
|
||||
|
||||
struct _RDP_BITMAP_PERSISTENT_INFO
|
||||
{
|
||||
UINT16 numEntriesCache0;
|
||||
UINT16 numEntriesCache1;
|
||||
UINT16 numEntriesCache2;
|
||||
UINT16 numEntriesCache3;
|
||||
UINT16 numEntriesCache4;
|
||||
UINT16 totalEntriesCache0;
|
||||
UINT16 totalEntriesCache1;
|
||||
UINT16 totalEntriesCache2;
|
||||
UINT16 totalEntriesCache3;
|
||||
UINT16 totalEntriesCache4;
|
||||
UINT32 keyCount;
|
||||
UINT64* keyList;
|
||||
};
|
||||
typedef struct _RDP_BITMAP_PERSISTENT_INFO RDP_BITMAP_PERSISTENT_INFO;
|
||||
|
||||
|
||||
#define PERSIST_FIRST_PDU 0x01
|
||||
#define PERSIST_LAST_PDU 0x02
|
||||
|
||||
|
@ -1866,7 +1866,14 @@ static BOOL rdp_write_bitmap_cache_v2_capability_set(wStream* s, const rdpSettin
|
||||
cacheFlags = ALLOW_CACHE_WAITING_LIST_FLAG;
|
||||
|
||||
if (settings->BitmapCachePersistEnabled)
|
||||
{
|
||||
cacheFlags |= PERSISTENT_KEYS_EXPECTED_FLAG;
|
||||
settings->BitmapCacheV2CellInfo[0].persistent = 1;
|
||||
settings->BitmapCacheV2CellInfo[1].persistent = 1;
|
||||
settings->BitmapCacheV2CellInfo[2].persistent = 1;
|
||||
settings->BitmapCacheV2CellInfo[3].persistent = 1;
|
||||
settings->BitmapCacheV2CellInfo[4].persistent = 1;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(s, cacheFlags); /* cacheFlags (2 bytes) */
|
||||
Stream_Write_UINT8(s, 0); /* pad2 (1 byte) */
|
||||
|
@ -325,6 +325,7 @@ static const size_t string_list_indices[] = {
|
||||
FreeRDP_AlternateShell,
|
||||
FreeRDP_AssistanceFile,
|
||||
FreeRDP_AuthenticationServiceClass,
|
||||
FreeRDP_BitmapCachePersistFile,
|
||||
FreeRDP_CardName,
|
||||
FreeRDP_CertificateAcceptedFingerprints,
|
||||
FreeRDP_CertificateContent,
|
||||
|
@ -1349,6 +1349,7 @@ static UINT gdi_SurfaceToCache(RdpgfxClientContext* context,
|
||||
if (!cacheEntry)
|
||||
goto fail;
|
||||
|
||||
cacheEntry->cacheKey = surfaceToCache->cacheKey;
|
||||
cacheEntry->width = (UINT32)(rect->right - rect->left);
|
||||
cacheEntry->height = (UINT32)(rect->bottom - rect->top);
|
||||
cacheEntry->format = surface->format;
|
||||
@ -1445,9 +1446,112 @@ fail:
|
||||
static UINT gdi_CacheImportReply(RdpgfxClientContext* context,
|
||||
const RDPGFX_CACHE_IMPORT_REPLY_PDU* cacheImportReply)
|
||||
{
|
||||
return CHANNEL_RC_OK;
|
||||
UINT16 index;
|
||||
UINT16 count;
|
||||
const UINT16* slots;
|
||||
gdiGfxCacheEntry* cacheEntry;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
slots = cacheImportReply->cacheSlots;
|
||||
count = cacheImportReply->importedEntriesCount;
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
UINT16 cacheSlot = slots[index];
|
||||
|
||||
if (cacheSlot == 0)
|
||||
continue;
|
||||
|
||||
cacheEntry = (gdiGfxCacheEntry*)context->GetCacheSlotData(context, cacheSlot);
|
||||
|
||||
if (cacheEntry)
|
||||
continue;
|
||||
|
||||
cacheEntry = (gdiGfxCacheEntry*)calloc(1, sizeof(gdiGfxCacheEntry));
|
||||
|
||||
if (!cacheEntry)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
cacheEntry->width = 0;
|
||||
cacheEntry->height = 0;
|
||||
cacheEntry->format = PIXEL_FORMAT_BGRX32;
|
||||
cacheEntry->scanline = (cacheEntry->width + (cacheEntry->width % 4)) * 4;
|
||||
cacheEntry->data = NULL;
|
||||
|
||||
error = context->SetCacheSlotData(context, cacheSlot, (void*)cacheEntry);
|
||||
|
||||
if (error) {
|
||||
WLog_ERR(TAG, "CacheImportReply: SetCacheSlotData failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
UINT gdi_ImportCacheEntry(RdpgfxClientContext* context, UINT16 cacheSlot,
|
||||
PERSISTENT_CACHE_ENTRY* importCacheEntry)
|
||||
{
|
||||
UINT error;
|
||||
gdiGfxCacheEntry* cacheEntry;
|
||||
|
||||
if (cacheSlot == 0)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
cacheEntry = (gdiGfxCacheEntry*)calloc(1, sizeof(gdiGfxCacheEntry));
|
||||
|
||||
if (!cacheEntry)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
cacheEntry->cacheKey = importCacheEntry->key64;
|
||||
cacheEntry->width = (UINT32)importCacheEntry->width;
|
||||
cacheEntry->height = (UINT32)importCacheEntry->height;
|
||||
cacheEntry->format = PIXEL_FORMAT_BGRX32;
|
||||
cacheEntry->scanline = (cacheEntry->width + (cacheEntry->width % 4)) * 4;
|
||||
cacheEntry->data = (BYTE*)calloc(cacheEntry->height, cacheEntry->scanline);
|
||||
|
||||
if (!cacheEntry->data)
|
||||
{
|
||||
free(cacheEntry);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!freerdp_image_copy(cacheEntry->data, cacheEntry->format, cacheEntry->scanline, 0, 0,
|
||||
cacheEntry->width, cacheEntry->height, importCacheEntry->data,
|
||||
PIXEL_FORMAT_BGRX32, 0, 0, 0, NULL, FREERDP_FLIP_NONE)) {
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
error = context->SetCacheSlotData(context, cacheSlot, (void*)cacheEntry);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ImportCacheEntry: SetCacheSlotData failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
UINT gdi_ExportCacheEntry(RdpgfxClientContext* context, UINT16 cacheSlot,
|
||||
PERSISTENT_CACHE_ENTRY* exportCacheEntry)
|
||||
{
|
||||
gdiGfxCacheEntry* cacheEntry;
|
||||
|
||||
cacheEntry = (gdiGfxCacheEntry*)context->GetCacheSlotData(context, cacheSlot);
|
||||
|
||||
if (cacheEntry)
|
||||
{
|
||||
exportCacheEntry->key64 = cacheEntry->cacheKey;
|
||||
exportCacheEntry->width = cacheEntry->width;
|
||||
exportCacheEntry->height = cacheEntry->height;
|
||||
exportCacheEntry->size = cacheEntry->width * cacheEntry->height * 4;
|
||||
exportCacheEntry->flags = 0;
|
||||
exportCacheEntry->data = cacheEntry->data;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -1465,9 +1569,9 @@ static UINT gdi_EvictCacheEntry(RdpgfxClientContext* context,
|
||||
{
|
||||
free(cacheEntry->data);
|
||||
free(cacheEntry);
|
||||
rc = context->SetCacheSlotData(context, evictCacheEntry->cacheSlot, NULL);
|
||||
}
|
||||
|
||||
rc = context->SetCacheSlotData(context, evictCacheEntry->cacheSlot, NULL);
|
||||
LeaveCriticalSection(&context->mux);
|
||||
return rc;
|
||||
}
|
||||
@ -1621,6 +1725,8 @@ BOOL gdi_graphics_pipeline_init_ex(rdpGdi* gdi, RdpgfxClientContext* gfx,
|
||||
gfx->SurfaceToCache = gdi_SurfaceToCache;
|
||||
gfx->CacheToSurface = gdi_CacheToSurface;
|
||||
gfx->CacheImportReply = gdi_CacheImportReply;
|
||||
gfx->ImportCacheEntry = gdi_ImportCacheEntry;
|
||||
gfx->ExportCacheEntry = gdi_ExportCacheEntry;
|
||||
gfx->EvictCacheEntry = gdi_EvictCacheEntry;
|
||||
gfx->MapSurfaceToOutput = gdi_MapSurfaceToOutput;
|
||||
gfx->MapSurfaceToWindow = gdi_MapSurfaceToWindow;
|
||||
|
Loading…
Reference in New Issue
Block a user