24#include <freerdp/config.h> 
   26#include <winpr/assert.h> 
   27#include <winpr/cast.h> 
   30#include <winpr/wlog.h> 
   31#include <winpr/print.h> 
   32#include <winpr/synch.h> 
   33#include <winpr/thread.h> 
   34#include <winpr/stream.h> 
   35#include <winpr/sysinfo.h> 
   36#include <winpr/cmdline.h> 
   37#include <winpr/collections.h> 
   39#include <freerdp/addin.h> 
   40#include <freerdp/channels/log.h> 
   42#include "rdpgfx_common.h" 
   43#include "rdpgfx_codec.h" 
   45#include "rdpgfx_main.h" 
   47#define TAG CHANNELS_TAG("rdpgfx.client") 
   49static BOOL delete_surface(
const void* key, 
void* value, 
void* arg)
 
   51  const UINT16 
id = (UINT16)(uintptr_t)(key);
 
   52  RdpgfxClientContext* context = arg;
 
   56  pdu.surfaceId = 
id - 1;
 
   60    UINT error = CHANNEL_RC_OK;
 
   61    IFCALLRET(context->DeleteSurface, error, context, &pdu);
 
   65      WLog_ERR(TAG, 
"context->DeleteSurface failed with error %" PRIu32 
"", error);
 
   71static void free_surfaces(RdpgfxClientContext* context, wHashTable* SurfaceTable)
 
   73  HashTable_Foreach(SurfaceTable, delete_surface, context);
 
   76static void evict_cache_slots(RdpgfxClientContext* context, UINT16 MaxCacheSlots, 
void** CacheSlots)
 
   78  WINPR_ASSERT(CacheSlots);
 
   79  for (UINT16 index = 0; index < MaxCacheSlots; index++)
 
   81    if (CacheSlots[index])
 
   84      pdu.cacheSlot = index + 1;
 
   86      if (context && context->EvictCacheEntry)
 
   88        context->EvictCacheEntry(context, &pdu);
 
   91      CacheSlots[index] = NULL;
 
  101static UINT rdpgfx_send_caps_advertise_pdu(RdpgfxClientContext* context,
 
  104  UINT error = CHANNEL_RC_OK;
 
  111  WINPR_ASSERT(context);
 
  115  if (!gfx || !gfx->base.listener_callback)
 
  116    return ERROR_BAD_ARGUMENTS;
 
  118  callback = gfx->base.listener_callback->channel_callback;
 
  121  header.cmdId = RDPGFX_CMDID_CAPSADVERTISE;
 
  122  header.pduLength = RDPGFX_HEADER_SIZE + 2;
 
  124  for (UINT16 index = 0; index < pdu->capsSetCount; index++)
 
  127    header.pduLength += RDPGFX_CAPSET_BASE_SIZE + capsSet->length;
 
  130  DEBUG_RDPGFX(gfx->log, 
"SendCapsAdvertisePdu %" PRIu16 
"", pdu->capsSetCount);
 
  131  s = Stream_New(NULL, header.pduLength);
 
  135    WLog_ERR(TAG, 
"Stream_New failed!");
 
  136    return CHANNEL_RC_NO_MEMORY;
 
  139  if ((error = rdpgfx_write_header(s, &header)))
 
  143  Stream_Write_UINT16(s, pdu->capsSetCount); 
 
  145  for (UINT16 index = 0; index < pdu->capsSetCount; index++)
 
  149    DEBUG_RDPGFX(gfx->log, 
"Sending %s [0x%08" PRIx32 
"] flags=0x%08" PRIx32,
 
  150                 rdpgfx_caps_version_str(capsSet->version), capsSet->version, capsSet->flags);
 
  152    Stream_Write_UINT32(s, capsSet->version); 
 
  153    Stream_Write_UINT32(s, capsSet->length);  
 
  154    Stream_Write_UINT32(s, capsSet->flags);   
 
  155    Stream_Zero(s, capsSet->length - 4);
 
  158  Stream_SealLength(s);
 
  159  error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
 
  162  Stream_Free(s, TRUE);
 
  166static BOOL rdpgfx_is_capability_filtered(
RDPGFX_PLUGIN* gfx, UINT32 caps)
 
  169  const UINT32 filter =
 
  171  const UINT32 capList[] = { RDPGFX_CAPVERSION_8,   RDPGFX_CAPVERSION_81,
 
  172                           RDPGFX_CAPVERSION_10,  RDPGFX_CAPVERSION_101,
 
  173                           RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103,
 
  174                           RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105,
 
  175                           RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR,
 
  176                           RDPGFX_CAPVERSION_107 };
 
  178  for (
size_t x = 0; x < ARRAYSIZE(capList); x++)
 
  180    if (caps == capList[x])
 
  181      return (filter & (1 << x)) != 0;
 
  195  RdpgfxClientContext* context = NULL;
 
  201    return ERROR_BAD_ARGUMENTS;
 
  206    return ERROR_BAD_CONFIGURATION;
 
  208  context = gfx->context;
 
  211    return ERROR_BAD_CONFIGURATION;
 
  213  pdu.capsSetCount = 0;
 
  216  if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_8))
 
  218    capsSet = &capsSets[pdu.capsSetCount++];
 
  219    capsSet->version = RDPGFX_CAPVERSION_8;
 
  224      capsSet->flags |= RDPGFX_CAPS_FLAG_THINCLIENT;
 
  231      capsSet->flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE;
 
  234  if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_81))
 
  236    capsSet = &capsSets[pdu.capsSetCount++];
 
  237    capsSet->version = RDPGFX_CAPVERSION_81;
 
  242      capsSet->flags |= RDPGFX_CAPS_FLAG_THINCLIENT;
 
  245      capsSet->flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE;
 
  250      capsSet->flags |= RDPGFX_CAPS_FLAG_AVC420_ENABLED;
 
  258    UINT32 caps10Flags = 0;
 
  261      caps10Flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE;
 
  266      caps10Flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED;
 
  269    caps10Flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED;
 
  272    if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_10))
 
  274      capsSet = &capsSets[pdu.capsSetCount++];
 
  275      capsSet->version = RDPGFX_CAPVERSION_10;
 
  277      capsSet->flags = caps10Flags;
 
  280    if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_101))
 
  282      capsSet = &capsSets[pdu.capsSetCount++];
 
  283      capsSet->version = RDPGFX_CAPVERSION_101;
 
  284      capsSet->length = 0x10;
 
  288    if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_102))
 
  290      capsSet = &capsSets[pdu.capsSetCount++];
 
  291      capsSet->version = RDPGFX_CAPVERSION_102;
 
  292      capsSet->length = 0x4;
 
  293      capsSet->flags = caps10Flags;
 
  298      if ((caps10Flags & RDPGFX_CAPS_FLAG_AVC_DISABLED) == 0)
 
  299        caps10Flags |= RDPGFX_CAPS_FLAG_AVC_THINCLIENT;
 
  302    if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_103))
 
  304      capsSet = &capsSets[pdu.capsSetCount++];
 
  305      capsSet->version = RDPGFX_CAPVERSION_103;
 
  306      capsSet->length = 0x4;
 
  307      capsSet->flags = caps10Flags & ~RDPGFX_CAPS_FLAG_SMALL_CACHE;
 
  310    if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_104))
 
  312      capsSet = &capsSets[pdu.capsSetCount++];
 
  313      capsSet->version = RDPGFX_CAPVERSION_104;
 
  314      capsSet->length = 0x4;
 
  315      capsSet->flags = caps10Flags;
 
  321#if defined(WITH_CAIRO) || defined(WITH_SWSCALE) 
  322    if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_105))
 
  324      capsSet = &capsSets[pdu.capsSetCount++];
 
  325      capsSet->version = RDPGFX_CAPVERSION_105;
 
  326      capsSet->length = 0x4;
 
  327      capsSet->flags = caps10Flags;
 
  330    if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106))
 
  332      capsSet = &capsSets[pdu.capsSetCount++];
 
  333      capsSet->version = RDPGFX_CAPVERSION_106;
 
  334      capsSet->length = 0x4;
 
  335      capsSet->flags = caps10Flags;
 
  338    if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106_ERR))
 
  340      capsSet = &capsSets[pdu.capsSetCount++];
 
  341      capsSet->version = RDPGFX_CAPVERSION_106_ERR;
 
  342      capsSet->length = 0x4;
 
  343      capsSet->flags = caps10Flags;
 
  347    if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_107))
 
  349      capsSet = &capsSets[pdu.capsSetCount++];
 
  350      capsSet->version = RDPGFX_CAPVERSION_107;
 
  351      capsSet->length = 0x4;
 
  352      capsSet->flags = caps10Flags;
 
  353#if !defined(WITH_CAIRO) && !defined(WITH_SWSCALE) 
  354      capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE;
 
  359  return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu);
 
  371  WINPR_ASSERT(callback);
 
  375  RdpgfxClientContext* context = gfx->context;
 
  377  pdu.capsSet = &capsSet;
 
  379  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
 
  380    return ERROR_INVALID_DATA;
 
  382  Stream_Read_UINT32(s, capsSet.version); 
 
  383  Stream_Read_UINT32(s, capsSet.length);  
 
  384  Stream_Read_UINT32(s, capsSet.flags);   
 
  385  gfx->TotalDecodedFrames = 0;
 
  386  gfx->ConnectionCaps = capsSet;
 
  387  WLog_Print(gfx->log, WLOG_DEBUG,
 
  388             "RecvCapsConfirmPdu: version: %s [0x%08" PRIX32 
"] flags: 0x%08" PRIX32 
"",
 
  389             rdpgfx_caps_version_str(capsSet.version), capsSet.version, capsSet.flags);
 
  392    return ERROR_BAD_CONFIGURATION;
 
  394  return IFCALLRESULT(CHANNEL_RC_OK, context->CapsConfirm, context, &pdu);
 
  402static UINT rdpgfx_send_frame_acknowledge_pdu(RdpgfxClientContext* context,
 
  411  if (!context || !pdu)
 
  412    return ERROR_BAD_ARGUMENTS;
 
  416  if (!gfx || !gfx->base.listener_callback)
 
  417    return ERROR_BAD_CONFIGURATION;
 
  419  callback = gfx->base.listener_callback->channel_callback;
 
  422    return ERROR_BAD_CONFIGURATION;
 
  425  header.cmdId = RDPGFX_CMDID_FRAMEACKNOWLEDGE;
 
  426  header.pduLength = RDPGFX_HEADER_SIZE + 12;
 
  427  DEBUG_RDPGFX(gfx->log, 
"SendFrameAcknowledgePdu: %" PRIu32 
"", pdu->frameId);
 
  428  s = Stream_New(NULL, header.pduLength);
 
  432    WLog_ERR(TAG, 
"Stream_New failed!");
 
  433    return CHANNEL_RC_NO_MEMORY;
 
  436  if ((error = rdpgfx_write_header(s, &header)))
 
  440  Stream_Write_UINT32(s, pdu->queueDepth);         
 
  441  Stream_Write_UINT32(s, pdu->frameId);            
 
  442  Stream_Write_UINT32(s, pdu->totalFramesDecoded); 
 
  443  error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
 
  446  if (error == CHANNEL_RC_OK) 
 
  447    gfx->UnacknowledgedFrames--;
 
  450  Stream_Free(s, TRUE);
 
  454static UINT rdpgfx_send_qoe_frame_acknowledge_pdu(RdpgfxClientContext* context,
 
  464  header.cmdId = RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE;
 
  465  header.pduLength = RDPGFX_HEADER_SIZE + 12;
 
  467  if (!context || !pdu)
 
  468    return ERROR_BAD_ARGUMENTS;
 
  472  if (!gfx || !gfx->base.listener_callback)
 
  473    return ERROR_BAD_CONFIGURATION;
 
  475  callback = gfx->base.listener_callback->channel_callback;
 
  478    return ERROR_BAD_CONFIGURATION;
 
  480  DEBUG_RDPGFX(gfx->log, 
"SendQoeFrameAcknowledgePdu: %" PRIu32 
"", pdu->frameId);
 
  481  s = Stream_New(NULL, header.pduLength);
 
  485    WLog_ERR(TAG, 
"Stream_New failed!");
 
  486    return CHANNEL_RC_NO_MEMORY;
 
  489  if ((error = rdpgfx_write_header(s, &header)))
 
  493  Stream_Write_UINT32(s, pdu->frameId);
 
  494  Stream_Write_UINT32(s, pdu->timestamp);
 
  495  Stream_Write_UINT16(s, pdu->timeDiffSE);
 
  496  Stream_Write_UINT16(s, pdu->timeDiffEDR);
 
  497  error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
 
  500  Stream_Free(s, TRUE);
 
  512  WINPR_ASSERT(callback);
 
  518  RdpgfxClientContext* context = gfx->context;
 
  519  UINT error = CHANNEL_RC_OK;
 
  520  GraphicsResetEventArgs graphicsReset = { 0 };
 
  522  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
 
  523    return ERROR_INVALID_DATA;
 
  525  Stream_Read_UINT32(s, pdu.width);        
 
  526  Stream_Read_UINT32(s, pdu.height);       
 
  527  Stream_Read_UINT32(s, pdu.monitorCount); 
 
  529  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.monitorCount, 20ull))
 
  530    return ERROR_INVALID_DATA;
 
  534  if (!pdu.monitorDefArray)
 
  536    WLog_Print(gfx->log, WLOG_ERROR, 
"calloc failed!");
 
  537    return CHANNEL_RC_NO_MEMORY;
 
  540  for (UINT32 index = 0; index < pdu.monitorCount; index++)
 
  542    MONITOR_DEF* monitor = &(pdu.monitorDefArray[index]);
 
  543    Stream_Read_INT32(s, monitor->left);   
 
  544    Stream_Read_INT32(s, monitor->top);    
 
  545    Stream_Read_INT32(s, monitor->right);  
 
  546    Stream_Read_INT32(s, monitor->bottom); 
 
  547    Stream_Read_UINT32(s, monitor->flags); 
 
  550  const size_t size = (RDPGFX_HEADER_SIZE + 12ULL + (pdu.monitorCount * 20ULL));
 
  553    free(pdu.monitorDefArray);
 
  554    return CHANNEL_RC_NULL_DATA;
 
  556  const size_t pad = 340ULL - size;
 
  558  if (!Stream_CheckAndLogRequiredLength(TAG, s, (
size_t)pad))
 
  560    free(pdu.monitorDefArray);
 
  561    return CHANNEL_RC_NO_MEMORY;
 
  565  WLog_Print(gfx->log, WLOG_DEBUG,
 
  566             "RecvResetGraphicsPdu: width: %" PRIu32 
" height: %" PRIu32 
" count: %" PRIu32 
"",
 
  567             pdu.width, pdu.height, pdu.monitorCount);
 
  569#if defined(WITH_DEBUG_RDPGFX) 
  570  for (UINT32 index = 0; index < pdu.monitorCount; index++)
 
  572    MONITOR_DEF* monitor = &(pdu.monitorDefArray[index]);
 
  573    DEBUG_RDPGFX(gfx->log,
 
  574                 "RecvResetGraphicsPdu: monitor left:%" PRIi32 
" top:%" PRIi32 
" right:%" PRIi32
 
  575                 " bottom:%" PRIi32 
" flags:0x%" PRIx32 
"",
 
  576                 monitor->left, monitor->top, monitor->right, monitor->bottom, monitor->flags);
 
  582    IFCALLRET(context->ResetGraphics, error, context, &pdu);
 
  585      WLog_Print(gfx->log, WLOG_ERROR, 
"context->ResetGraphics failed with error %" PRIu32 
"",
 
  590  EventArgsInit(&graphicsReset, 
"libfreerdp");
 
  591  graphicsReset.width = pdu.width;
 
  592  graphicsReset.height = pdu.height;
 
  593  PubSub_OnGraphicsReset(gfx->rdpcontext->pubSub, gfx->rdpcontext, &graphicsReset);
 
  594  free(pdu.monitorDefArray);
 
  606  WINPR_ASSERT(callback);
 
  609  RdpgfxClientContext* context = gfx->context;
 
  610  UINT error = CHANNEL_RC_OK;
 
  612  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
 
  613    return ERROR_INVALID_DATA;
 
  615  Stream_Read_UINT16(s, pdu.cacheSlot); 
 
  616  WLog_Print(gfx->log, WLOG_DEBUG, 
"RecvEvictCacheEntryPdu: cacheSlot: %" PRIu16 
"",
 
  621    IFCALLRET(context->EvictCacheEntry, error, context, &pdu);
 
  624      WLog_Print(gfx->log, WLOG_ERROR,
 
  625                 "context->EvictCacheEntry failed with error %" PRIu32 
"", error);
 
  638  UINT error = CHANNEL_RC_OK;
 
  640  rdpPersistentCache* persistent = NULL;
 
  642  WINPR_ASSERT(gfx->rdpcontext);
 
  643  rdpSettings* settings = gfx->rdpcontext->settings;
 
  644  RdpgfxClientContext* context = gfx->context;
 
  646  WINPR_ASSERT(context);
 
  647  WINPR_ASSERT(settings);
 
  650    return CHANNEL_RC_OK;
 
  652  const char* BitmapCachePersistFile =
 
  654  if (!BitmapCachePersistFile)
 
  655    return CHANNEL_RC_OK;
 
  657  if (!context->ExportCacheEntry)
 
  658    return CHANNEL_RC_INITIALIZATION_ERROR;
 
  660  persistent = persistent_cache_new();
 
  663    return CHANNEL_RC_NO_MEMORY;
 
  665  if (persistent_cache_open(persistent, BitmapCachePersistFile, TRUE, 3) < 1)
 
  667    error = CHANNEL_RC_INITIALIZATION_ERROR;
 
  671  for (UINT16 idx = 0; idx < gfx->MaxCacheSlots; idx++)
 
  673    if (gfx->CacheSlots[idx])
 
  675      UINT16 cacheSlot = idx;
 
  677      if (context->ExportCacheEntry(context, cacheSlot, &cacheEntry) != CHANNEL_RC_OK)
 
  680      persistent_cache_write_entry(persistent, &cacheEntry);
 
  684  persistent_cache_free(persistent);
 
  688  persistent_cache_free(persistent);
 
  697static UINT rdpgfx_send_cache_import_offer_pdu(RdpgfxClientContext* context,
 
  700  UINT error = CHANNEL_RC_OK;
 
  705  if (!context || !pdu)
 
  706    return ERROR_BAD_ARGUMENTS;
 
  710  if (!gfx || !gfx->base.listener_callback)
 
  711    return ERROR_BAD_CONFIGURATION;
 
  713  callback = gfx->base.listener_callback->channel_callback;
 
  716    return ERROR_BAD_CONFIGURATION;
 
  719  header.cmdId = RDPGFX_CMDID_CACHEIMPORTOFFER;
 
  720  header.pduLength = RDPGFX_HEADER_SIZE + 2ul + pdu->cacheEntriesCount * 12ul;
 
  721  WLog_Print(gfx->log, WLOG_DEBUG, 
"SendCacheImportOfferPdu: cacheEntriesCount: %" PRIu16 
"",
 
  722             pdu->cacheEntriesCount);
 
  723  s = Stream_New(NULL, header.pduLength);
 
  727    WLog_ERR(TAG, 
"Stream_New failed!");
 
  728    return CHANNEL_RC_NO_MEMORY;
 
  731  if ((error = rdpgfx_write_header(s, &header)))
 
  734  if (pdu->cacheEntriesCount <= 0)
 
  736    WLog_ERR(TAG, 
"Invalid cacheEntriesCount: %" PRIu16 
"", pdu->cacheEntriesCount);
 
  737    error = ERROR_INVALID_DATA;
 
  742  Stream_Write_UINT16(s, pdu->cacheEntriesCount);
 
  744  for (UINT16 index = 0; index < pdu->cacheEntriesCount; index++)
 
  747    Stream_Write_UINT64(s, cacheEntry->cacheKey);     
 
  748    Stream_Write_UINT32(s, cacheEntry->bitmapLength); 
 
  751  error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
 
  755  Stream_Free(s, TRUE);
 
  767  UINT error = CHANNEL_RC_OK;
 
  770  rdpPersistentCache* persistent = NULL;
 
  773  WINPR_ASSERT(gfx->rdpcontext);
 
  775  RdpgfxClientContext* context = gfx->context;
 
  776  rdpSettings* settings = gfx->rdpcontext->settings;
 
  779    return CHANNEL_RC_OK;
 
  781  const char* BitmapCachePersistFile =
 
  783  if (!BitmapCachePersistFile)
 
  784    return CHANNEL_RC_OK;
 
  786  persistent = persistent_cache_new();
 
  789    return CHANNEL_RC_NO_MEMORY;
 
  791  if (persistent_cache_open(persistent, BitmapCachePersistFile, FALSE, 3) < 1)
 
  793    error = CHANNEL_RC_INITIALIZATION_ERROR;
 
  797  if (persistent_cache_get_version(persistent) != 3)
 
  799    error = ERROR_INVALID_DATA;
 
  803  count = persistent_cache_get_count(persistent);
 
  806    error = ERROR_INVALID_DATA;
 
  810  if (count >= RDPGFX_CACHE_ENTRY_MAX_COUNT)
 
  811    count = RDPGFX_CACHE_ENTRY_MAX_COUNT - 1;
 
  813  if (count > gfx->MaxCacheSlots)
 
  814    count = gfx->MaxCacheSlots;
 
  819    error = CHANNEL_RC_NO_MEMORY;
 
  823  WINPR_ASSERT(count <= UINT16_MAX);
 
  824  offer->cacheEntriesCount = (UINT16)count;
 
  826  WLog_DBG(TAG, 
"Sending Cache Import Offer: %d", count);
 
  828  for (
int idx = 0; idx < count; idx++)
 
  830    if (persistent_cache_read_entry(persistent, &entry) < 1)
 
  832      error = ERROR_INVALID_DATA;
 
  836    offer->cacheEntries[idx].cacheKey = entry.key64;
 
  837    offer->cacheEntries[idx].bitmapLength = entry.size;
 
  840  if (offer->cacheEntriesCount > 0)
 
  842    error = rdpgfx_send_cache_import_offer_pdu(context, offer);
 
  843    if (error != CHANNEL_RC_OK)
 
  845      WLog_Print(gfx->log, WLOG_ERROR, 
"Failed to send cache import offer PDU");
 
  851  persistent_cache_free(persistent);
 
  861static UINT rdpgfx_load_cache_import_reply(
RDPGFX_PLUGIN* gfx,
 
  865  UINT error = CHANNEL_RC_OK;
 
  866  rdpPersistentCache* persistent = NULL;
 
  868  WINPR_ASSERT(gfx->rdpcontext);
 
  869  rdpSettings* settings = gfx->rdpcontext->settings;
 
  870  RdpgfxClientContext* context = gfx->context;
 
  872  WINPR_ASSERT(settings);
 
  875    return CHANNEL_RC_OK;
 
  877  const char* BitmapCachePersistFile =
 
  879  if (!BitmapCachePersistFile)
 
  880    return CHANNEL_RC_OK;
 
  882  persistent = persistent_cache_new();
 
  885    return CHANNEL_RC_NO_MEMORY;
 
  887  if (persistent_cache_open(persistent, BitmapCachePersistFile, FALSE, 3) < 1)
 
  889    error = CHANNEL_RC_INITIALIZATION_ERROR;
 
  893  if (persistent_cache_get_version(persistent) != 3)
 
  895    error = ERROR_INVALID_DATA;
 
  899  count = persistent_cache_get_count(persistent);
 
  901  count = (count < reply->importedEntriesCount) ? count : reply->importedEntriesCount;
 
  903  WLog_DBG(TAG, 
"Receiving Cache Import Reply: %d", count);
 
  905  for (
int idx = 0; idx < count; idx++)
 
  908    if (persistent_cache_read_entry(persistent, &entry) < 1)
 
  910      error = ERROR_INVALID_DATA;
 
  914    const UINT16 cacheSlot = reply->cacheSlots[idx];
 
  915    if (context && context->ImportCacheEntry)
 
  916      context->ImportCacheEntry(context, cacheSlot, &entry);
 
  919  persistent_cache_free(persistent);
 
  923  persistent_cache_free(persistent);
 
  935  WINPR_ASSERT(callback);
 
  938  RdpgfxClientContext* context = gfx->context;
 
  939  UINT error = CHANNEL_RC_OK;
 
  941  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
 
  942    return ERROR_INVALID_DATA;
 
  944  Stream_Read_UINT16(s, pdu.importedEntriesCount); 
 
  946  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.importedEntriesCount, 2ull))
 
  947    return ERROR_INVALID_DATA;
 
  949  if (pdu.importedEntriesCount > RDPGFX_CACHE_ENTRY_MAX_COUNT)
 
  950    return ERROR_INVALID_DATA;
 
  952  for (UINT16 idx = 0; idx < pdu.importedEntriesCount; idx++)
 
  954    Stream_Read_UINT16(s, pdu.cacheSlots[idx]); 
 
  957  DEBUG_RDPGFX(gfx->log, 
"RecvCacheImportReplyPdu: importedEntriesCount: %" PRIu16 
"",
 
  958               pdu.importedEntriesCount);
 
  960  error = rdpgfx_load_cache_import_reply(gfx, &pdu);
 
  964    WLog_Print(gfx->log, WLOG_ERROR,
 
  965               "rdpgfx_load_cache_import_reply failed with error %" PRIu32 
"", error);
 
  971    IFCALLRET(context->CacheImportReply, error, context, &pdu);
 
  974      WLog_Print(gfx->log, WLOG_ERROR,
 
  975                 "context->CacheImportReply failed with error %" PRIu32 
"", error);
 
  989  WINPR_ASSERT(callback);
 
  992  RdpgfxClientContext* context = gfx->context;
 
  993  UINT error = CHANNEL_RC_OK;
 
  995  if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
 
  996    return ERROR_INVALID_DATA;
 
  998  Stream_Read_UINT16(s, pdu.surfaceId);  
 
  999  Stream_Read_UINT16(s, pdu.width);      
 
 1000  Stream_Read_UINT16(s, pdu.height);     
 
 1001  Stream_Read_UINT8(s, pdu.pixelFormat); 
 
 1002  DEBUG_RDPGFX(gfx->log,
 
 1003               "RecvCreateSurfacePdu: surfaceId: %" PRIu16 
" width: %" PRIu16 
" height: %" PRIu16
 
 1004               " pixelFormat: 0x%02" PRIX8 
"",
 
 1005               pdu.surfaceId, pdu.width, pdu.height, pdu.pixelFormat);
 
 1014    IFCALL(context->DeleteSurface, context, &deletePdu);
 
 1016    IFCALLRET(context->CreateSurface, error, context, &pdu);
 
 1019      WLog_Print(gfx->log, WLOG_ERROR, 
"context->CreateSurface failed with error %" PRIu32 
"",
 
 1034  WINPR_ASSERT(callback);
 
 1037  RdpgfxClientContext* context = gfx->context;
 
 1038  UINT error = CHANNEL_RC_OK;
 
 1040  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
 
 1041    return ERROR_INVALID_DATA;
 
 1043  Stream_Read_UINT16(s, pdu.surfaceId); 
 
 1044  DEBUG_RDPGFX(gfx->log, 
"RecvDeleteSurfacePdu: surfaceId: %" PRIu16 
"", pdu.surfaceId);
 
 1048    IFCALLRET(context->DeleteSurface, error, context, &pdu);
 
 1051      WLog_Print(gfx->log, WLOG_ERROR, 
"context->DeleteSurface failed with error %" PRIu32 
"",
 
 1066  WINPR_ASSERT(callback);
 
 1069  RdpgfxClientContext* context = gfx->context;
 
 1070  UINT error = CHANNEL_RC_OK;
 
 1072  if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPGFX_START_FRAME_PDU_SIZE))
 
 1073    return ERROR_INVALID_DATA;
 
 1075  Stream_Read_UINT32(s, pdu.timestamp); 
 
 1076  Stream_Read_UINT32(s, pdu.frameId);   
 
 1077  DEBUG_RDPGFX(gfx->log, 
"RecvStartFramePdu: frameId: %" PRIu32 
" timestamp: 0x%08" PRIX32 
"",
 
 1078               pdu.frameId, pdu.timestamp);
 
 1079  gfx->StartDecodingTime = GetTickCount64();
 
 1083    IFCALLRET(context->StartFrame, error, context, &pdu);
 
 1086      WLog_Print(gfx->log, WLOG_ERROR, 
"context->StartFrame failed with error %" PRIu32 
"",
 
 1090  gfx->UnacknowledgedFrames++;
 
 1103  WINPR_ASSERT(callback);
 
 1106  RdpgfxClientContext* context = gfx->context;
 
 1107  UINT error = CHANNEL_RC_OK;
 
 1109  if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPGFX_END_FRAME_PDU_SIZE))
 
 1110    return ERROR_INVALID_DATA;
 
 1112  Stream_Read_UINT32(s, pdu.frameId); 
 
 1113  DEBUG_RDPGFX(gfx->log, 
"RecvEndFramePdu: frameId: %" PRIu32 
"", pdu.frameId);
 
 1115  const UINT64 start = GetTickCount64();
 
 1118    IFCALLRET(context->EndFrame, error, context, &pdu);
 
 1122      WLog_Print(gfx->log, WLOG_ERROR, 
"context->EndFrame failed with error %" PRIu32 
"",
 
 1127  const UINT64 end = GetTickCount64();
 
 1128  const UINT64 EndFrameTime = end - start;
 
 1129  gfx->TotalDecodedFrames++;
 
 1131  if (!gfx->sendFrameAcks)
 
 1134  ack.frameId = pdu.frameId;
 
 1135  ack.totalFramesDecoded = gfx->TotalDecodedFrames;
 
 1137  if (gfx->suspendFrameAcks)
 
 1139    ack.queueDepth = SUSPEND_FRAME_ACKNOWLEDGEMENT;
 
 1141    if (gfx->TotalDecodedFrames == 1)
 
 1142      if ((error = rdpgfx_send_frame_acknowledge_pdu(context, &ack)))
 
 1143        WLog_Print(gfx->log, WLOG_ERROR,
 
 1144                   "rdpgfx_send_frame_acknowledge_pdu failed with error %" PRIu32 
"",
 
 1149    ack.queueDepth = QUEUE_DEPTH_UNAVAILABLE;
 
 1151    if ((error = rdpgfx_send_frame_acknowledge_pdu(context, &ack)))
 
 1152      WLog_Print(gfx->log, WLOG_ERROR,
 
 1153                 "rdpgfx_send_frame_acknowledge_pdu failed with error %" PRIu32 
"", error);
 
 1156  switch (gfx->ConnectionCaps.version)
 
 1158    case RDPGFX_CAPVERSION_10:
 
 1159    case RDPGFX_CAPVERSION_102:
 
 1160    case RDPGFX_CAPVERSION_103:
 
 1161    case RDPGFX_CAPVERSION_104:
 
 1162    case RDPGFX_CAPVERSION_105:
 
 1163    case RDPGFX_CAPVERSION_106:
 
 1164    case RDPGFX_CAPVERSION_106_ERR:
 
 1165    case RDPGFX_CAPVERSION_107:
 
 1169        UINT64 diff = (GetTickCount64() - gfx->StartDecodingTime);
 
 1174        qoe.frameId = pdu.frameId;
 
 1175        qoe.timestamp = gfx->StartDecodingTime % UINT32_MAX;
 
 1176        qoe.timeDiffSE = WINPR_ASSERTING_INT_CAST(UINT16, diff);
 
 1177        qoe.timeDiffEDR = WINPR_ASSERTING_INT_CAST(UINT16, EndFrameTime);
 
 1179        if ((error = rdpgfx_send_qoe_frame_acknowledge_pdu(context, &qoe)))
 
 1180          WLog_Print(gfx->log, WLOG_ERROR,
 
 1181                     "rdpgfx_send_qoe_frame_acknowledge_pdu failed with error %" PRIu32
 
 1204  WINPR_ASSERT(callback);
 
 1209  if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPGFX_WIRE_TO_SURFACE_PDU_1_SIZE))
 
 1210    return ERROR_INVALID_DATA;
 
 1212  Stream_Read_UINT16(s, pdu.surfaceId);  
 
 1213  Stream_Read_UINT16(s, pdu.codecId);    
 
 1214  Stream_Read_UINT8(s, pdu.pixelFormat); 
 
 1216  if ((error = rdpgfx_read_rect16(s, &(pdu.destRect)))) 
 
 1218    WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_read_rect16 failed with error %" PRIu32 
"", error);
 
 1222  Stream_Read_UINT32(s, pdu.bitmapDataLength); 
 
 1224  if (!Stream_CheckAndLogRequiredLength(TAG, s, pdu.bitmapDataLength))
 
 1225    return ERROR_INVALID_DATA;
 
 1227  pdu.bitmapData = Stream_Pointer(s);
 
 1228  Stream_Seek(s, pdu.bitmapDataLength);
 
 1230  DEBUG_RDPGFX(gfx->log,
 
 1231               "RecvWireToSurface1Pdu: surfaceId: %" PRIu16 
" codecId: %s (0x%04" PRIX16
 
 1232               ") pixelFormat: 0x%02" PRIX8 
" " 
 1233               "destRect: left: %" PRIu16 
" top: %" PRIu16 
" right: %" PRIu16 
" bottom: %" PRIu16
 
 1234               " bitmapDataLength: %" PRIu32 
"",
 
 1235               pdu.surfaceId, rdpgfx_get_codec_id_string(pdu.codecId), pdu.codecId,
 
 1236               pdu.pixelFormat, pdu.destRect.left, pdu.destRect.top, pdu.destRect.right,
 
 1237               pdu.destRect.bottom, pdu.bitmapDataLength);
 
 1238  cmd.surfaceId = pdu.surfaceId;
 
 1239  cmd.codecId = pdu.codecId;
 
 1242  switch (pdu.pixelFormat)
 
 1244    case GFX_PIXEL_FORMAT_XRGB_8888:
 
 1245      cmd.format = PIXEL_FORMAT_BGRX32;
 
 1248    case GFX_PIXEL_FORMAT_ARGB_8888:
 
 1249      cmd.format = PIXEL_FORMAT_BGRA32;
 
 1253      return ERROR_INVALID_DATA;
 
 1256  cmd.left = pdu.destRect.left;
 
 1257  cmd.top = pdu.destRect.top;
 
 1258  cmd.right = pdu.destRect.right;
 
 1259  cmd.bottom = pdu.destRect.bottom;
 
 1260  cmd.width = cmd.right - cmd.left;
 
 1261  cmd.height = cmd.bottom - cmd.top;
 
 1262  cmd.length = pdu.bitmapDataLength;
 
 1263  cmd.data = pdu.bitmapData;
 
 1266  if (cmd.right < cmd.left)
 
 1268    WLog_Print(gfx->log, WLOG_ERROR, 
"RecvWireToSurface1Pdu right=%" PRIu32 
" < left=%" PRIu32,
 
 1269               cmd.right, cmd.left);
 
 1270    return ERROR_INVALID_DATA;
 
 1272  if (cmd.bottom < cmd.top)
 
 1274    WLog_Print(gfx->log, WLOG_ERROR, 
"RecvWireToSurface1Pdu bottom=%" PRIu32 
" < top=%" PRIu32,
 
 1275               cmd.bottom, cmd.top);
 
 1276    return ERROR_INVALID_DATA;
 
 1279  if ((error = rdpgfx_decode(gfx, &cmd)))
 
 1280    WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_decode failed with error %" PRIu32 
"!", error);
 
 1294  WINPR_ASSERT(callback);
 
 1297  RdpgfxClientContext* context = gfx->context;
 
 1298  UINT error = CHANNEL_RC_OK;
 
 1300  if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPGFX_WIRE_TO_SURFACE_PDU_2_SIZE))
 
 1301    return ERROR_INVALID_DATA;
 
 1303  Stream_Read_UINT16(s, pdu.surfaceId);        
 
 1304  Stream_Read_UINT16(s, pdu.codecId);          
 
 1305  Stream_Read_UINT32(s, pdu.codecContextId);   
 
 1306  Stream_Read_UINT8(s, pdu.pixelFormat);       
 
 1307  Stream_Read_UINT32(s, pdu.bitmapDataLength); 
 
 1308  pdu.bitmapData = Stream_Pointer(s);
 
 1309  Stream_Seek(s, pdu.bitmapDataLength);
 
 1311  DEBUG_RDPGFX(gfx->log,
 
 1312               "RecvWireToSurface2Pdu: surfaceId: %" PRIu16 
" codecId: %s (0x%04" PRIX16 
") " 
 1313               "codecContextId: %" PRIu32 
" pixelFormat: 0x%02" PRIX8
 
 1314               " bitmapDataLength: %" PRIu32 
"",
 
 1315               pdu.surfaceId, rdpgfx_get_codec_id_string(pdu.codecId), pdu.codecId,
 
 1316               pdu.codecContextId, pdu.pixelFormat, pdu.bitmapDataLength);
 
 1318  cmd.surfaceId = pdu.surfaceId;
 
 1319  cmd.codecId = pdu.codecId;
 
 1320  cmd.contextId = pdu.codecContextId;
 
 1322  switch (pdu.pixelFormat)
 
 1324    case GFX_PIXEL_FORMAT_XRGB_8888:
 
 1325      cmd.format = PIXEL_FORMAT_BGRX32;
 
 1328    case GFX_PIXEL_FORMAT_ARGB_8888:
 
 1329      cmd.format = PIXEL_FORMAT_BGRA32;
 
 1333      return ERROR_INVALID_DATA;
 
 1342  cmd.length = pdu.bitmapDataLength;
 
 1343  cmd.data = pdu.bitmapData;
 
 1348    IFCALLRET(context->SurfaceCommand, error, context, &cmd);
 
 1351      WLog_Print(gfx->log, WLOG_ERROR,
 
 1352                 "context->SurfaceCommand failed with error %" PRIu32 
"", error);
 
 1366  WINPR_ASSERT(callback);
 
 1369  RdpgfxClientContext* context = gfx->context;
 
 1370  UINT error = CHANNEL_RC_OK;
 
 1372  if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
 
 1373    return ERROR_INVALID_DATA;
 
 1375  Stream_Read_UINT16(s, pdu.surfaceId);      
 
 1376  Stream_Read_UINT32(s, pdu.codecContextId); 
 
 1378  DEBUG_RDPGFX(gfx->log,
 
 1379               "RecvDeleteEncodingContextPdu: surfaceId: %" PRIu16 
" codecContextId: %" PRIu32 
"",
 
 1380               pdu.surfaceId, pdu.codecContextId);
 
 1384    IFCALLRET(context->DeleteEncodingContext, error, context, &pdu);
 
 1387      WLog_Print(gfx->log, WLOG_ERROR,
 
 1388                 "context->DeleteEncodingContext failed with error %" PRIu32 
"", error);
 
 1403  WINPR_ASSERT(callback);
 
 1406  RdpgfxClientContext* context = gfx->context;
 
 1409  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
 
 1410    return ERROR_INVALID_DATA;
 
 1412  Stream_Read_UINT16(s, pdu.surfaceId); 
 
 1414  if ((error = rdpgfx_read_color32(s, &(pdu.fillPixel)))) 
 
 1416    WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_read_color32 failed with error %" PRIu32 
"!",
 
 1421  Stream_Read_UINT16(s, pdu.fillRectCount); 
 
 1423  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.fillRectCount, 8ull))
 
 1424    return ERROR_INVALID_DATA;
 
 1430    WLog_Print(gfx->log, WLOG_ERROR, 
"calloc failed!");
 
 1431    return CHANNEL_RC_NO_MEMORY;
 
 1434  for (UINT16 index = 0; index < pdu.fillRectCount; index++)
 
 1436    fillRect = &(pdu.fillRects[index]);
 
 1438    if ((error = rdpgfx_read_rect16(s, fillRect)))
 
 1440      WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_read_rect16 failed with error %" PRIu32 
"!",
 
 1442      free(pdu.fillRects);
 
 1446  DEBUG_RDPGFX(gfx->log, 
"RecvSolidFillPdu: surfaceId: %" PRIu16 
" fillRectCount: %" PRIu16 
"",
 
 1447               pdu.surfaceId, pdu.fillRectCount);
 
 1451    IFCALLRET(context->SolidFill, error, context, &pdu);
 
 1454      WLog_Print(gfx->log, WLOG_ERROR, 
"context->SolidFill failed with error %" PRIu32 
"",
 
 1458  free(pdu.fillRects);
 
 1471  WINPR_ASSERT(callback);
 
 1474  RdpgfxClientContext* context = gfx->context;
 
 1477  if (!Stream_CheckAndLogRequiredLength(TAG, s, 14))
 
 1478    return ERROR_INVALID_DATA;
 
 1480  Stream_Read_UINT16(s, pdu.surfaceIdSrc);  
 
 1481  Stream_Read_UINT16(s, pdu.surfaceIdDest); 
 
 1483  if ((error = rdpgfx_read_rect16(s, &(pdu.rectSrc)))) 
 
 1485    WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_read_rect16 failed with error %" PRIu32 
"!",
 
 1490  Stream_Read_UINT16(s, pdu.destPtsCount); 
 
 1492  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.destPtsCount, 4ull))
 
 1493    return ERROR_INVALID_DATA;
 
 1499    WLog_Print(gfx->log, WLOG_ERROR, 
"calloc failed!");
 
 1500    return CHANNEL_RC_NO_MEMORY;
 
 1503  for (UINT16 index = 0; index < pdu.destPtsCount; index++)
 
 1505    destPt = &(pdu.destPts[index]);
 
 1507    if ((error = rdpgfx_read_point16(s, destPt)))
 
 1509      WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_read_point16 failed with error %" PRIu32 
"!",
 
 1516  DEBUG_RDPGFX(gfx->log,
 
 1517               "RecvSurfaceToSurfacePdu: surfaceIdSrc: %" PRIu16 
" surfaceIdDest: %" PRIu16 
" " 
 1518               "left: %" PRIu16 
" top: %" PRIu16 
" right: %" PRIu16 
" bottom: %" PRIu16
 
 1519               " destPtsCount: %" PRIu16 
"",
 
 1520               pdu.surfaceIdSrc, pdu.surfaceIdDest, pdu.rectSrc.left, pdu.rectSrc.top,
 
 1521               pdu.rectSrc.right, pdu.rectSrc.bottom, pdu.destPtsCount);
 
 1525    IFCALLRET(context->SurfaceToSurface, error, context, &pdu);
 
 1528      WLog_Print(gfx->log, WLOG_ERROR,
 
 1529                 "context->SurfaceToSurface failed with error %" PRIu32 
"", error);
 
 1544  WINPR_ASSERT(callback);
 
 1548  RdpgfxClientContext* context = gfx->context;
 
 1551  if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
 
 1552    return ERROR_INVALID_DATA;
 
 1554  Stream_Read_UINT16(s, pdu.surfaceId); 
 
 1555  Stream_Read_UINT64(s, pdu.cacheKey);  
 
 1556  Stream_Read_UINT16(s, pdu.cacheSlot); 
 
 1558  if ((error = rdpgfx_read_rect16(s, &(pdu.rectSrc)))) 
 
 1560    WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_read_rect16 failed with error %" PRIu32 
"!",
 
 1565  DEBUG_RDPGFX(gfx->log,
 
 1566               "RecvSurfaceToCachePdu: surfaceId: %" PRIu16 
" cacheKey: 0x%016" PRIX64
 
 1567               " cacheSlot: %" PRIu16 
" " 
 1568               "left: %" PRIu16 
" top: %" PRIu16 
" right: %" PRIu16 
" bottom: %" PRIu16 
"",
 
 1569               pdu.surfaceId, pdu.cacheKey, pdu.cacheSlot, pdu.rectSrc.left, pdu.rectSrc.top,
 
 1570               pdu.rectSrc.right, pdu.rectSrc.bottom);
 
 1574    IFCALLRET(context->SurfaceToCache, error, context, &pdu);
 
 1577      WLog_Print(gfx->log, WLOG_ERROR,
 
 1578                 "context->SurfaceToCache failed with error %" PRIu32 
"", error);
 
 1593  WINPR_ASSERT(callback);
 
 1597  RdpgfxClientContext* context = gfx->context;
 
 1598  UINT error = CHANNEL_RC_OK;
 
 1600  if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
 
 1601    return ERROR_INVALID_DATA;
 
 1603  Stream_Read_UINT16(s, pdu.cacheSlot);    
 
 1604  Stream_Read_UINT16(s, pdu.surfaceId);    
 
 1605  Stream_Read_UINT16(s, pdu.destPtsCount); 
 
 1607  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.destPtsCount, 4ull))
 
 1608    return ERROR_INVALID_DATA;
 
 1614    WLog_Print(gfx->log, WLOG_ERROR, 
"calloc failed!");
 
 1615    return CHANNEL_RC_NO_MEMORY;
 
 1618  for (UINT16 index = 0; index < pdu.destPtsCount; index++)
 
 1620    destPt = &(pdu.destPts[index]);
 
 1622    if ((error = rdpgfx_read_point16(s, destPt)))
 
 1624      WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_read_point16 failed with error %" PRIu32 
"",
 
 1631  DEBUG_RDPGFX(gfx->log,
 
 1632               "RdpGfxRecvCacheToSurfacePdu: cacheSlot: %" PRIu16 
" surfaceId: %" PRIu16
 
 1633               " destPtsCount: %" PRIu16 
"",
 
 1634               pdu.cacheSlot, pdu.surfaceId, pdu.destPtsCount);
 
 1638    IFCALLRET(context->CacheToSurface, error, context, &pdu);
 
 1641      WLog_Print(gfx->log, WLOG_ERROR,
 
 1642                 "context->CacheToSurface failed with error %" PRIu32 
"", error);
 
 1657  WINPR_ASSERT(callback);
 
 1661  RdpgfxClientContext* context = gfx->context;
 
 1662  UINT error = CHANNEL_RC_OK;
 
 1664  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
 
 1665    return ERROR_INVALID_DATA;
 
 1667  Stream_Read_UINT16(s, pdu.surfaceId);     
 
 1668  Stream_Read_UINT16(s, pdu.reserved);      
 
 1669  Stream_Read_UINT32(s, pdu.outputOriginX); 
 
 1670  Stream_Read_UINT32(s, pdu.outputOriginY); 
 
 1671  DEBUG_RDPGFX(gfx->log,
 
 1672               "RecvMapSurfaceToOutputPdu: surfaceId: %" PRIu16 
" outputOriginX: %" PRIu32
 
 1673               " outputOriginY: %" PRIu32 
"",
 
 1674               pdu.surfaceId, pdu.outputOriginX, pdu.outputOriginY);
 
 1678    IFCALLRET(context->MapSurfaceToOutput, error, context, &pdu);
 
 1681      WLog_Print(gfx->log, WLOG_ERROR,
 
 1682                 "context->MapSurfaceToOutput failed with error %" PRIu32 
"", error);
 
 1692  WINPR_ASSERT(callback);
 
 1696  RdpgfxClientContext* context = gfx->context;
 
 1697  UINT error = CHANNEL_RC_OK;
 
 1699  if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
 
 1700    return ERROR_INVALID_DATA;
 
 1702  Stream_Read_UINT16(s, pdu.surfaceId);     
 
 1703  Stream_Read_UINT16(s, pdu.reserved);      
 
 1704  Stream_Read_UINT32(s, pdu.outputOriginX); 
 
 1705  Stream_Read_UINT32(s, pdu.outputOriginY); 
 
 1706  Stream_Read_UINT32(s, pdu.targetWidth);   
 
 1707  Stream_Read_UINT32(s, pdu.targetHeight);  
 
 1708  DEBUG_RDPGFX(gfx->log,
 
 1709               "RecvMapSurfaceToScaledOutputPdu: surfaceId: %" PRIu16 
" outputOriginX: %" PRIu32
 
 1710               " outputOriginY: %" PRIu32 
" targetWidth: %" PRIu32 
" targetHeight: %" PRIu32,
 
 1711               pdu.surfaceId, pdu.outputOriginX, pdu.outputOriginY, pdu.targetWidth,
 
 1716    IFCALLRET(context->MapSurfaceToScaledOutput, error, context, &pdu);
 
 1719      WLog_Print(gfx->log, WLOG_ERROR,
 
 1720                 "context->MapSurfaceToScaledOutput failed with error %" PRIu32 
"", error);
 
 1734  WINPR_ASSERT(callback);
 
 1738  RdpgfxClientContext* context = gfx->context;
 
 1739  UINT error = CHANNEL_RC_OK;
 
 1741  if (!Stream_CheckAndLogRequiredLength(TAG, s, 18))
 
 1742    return ERROR_INVALID_DATA;
 
 1744  Stream_Read_UINT16(s, pdu.surfaceId);    
 
 1745  Stream_Read_UINT64(s, pdu.windowId);     
 
 1746  Stream_Read_UINT32(s, pdu.mappedWidth);  
 
 1747  Stream_Read_UINT32(s, pdu.mappedHeight); 
 
 1748  DEBUG_RDPGFX(gfx->log,
 
 1749               "RecvMapSurfaceToWindowPdu: surfaceId: %" PRIu16 
" windowId: 0x%016" PRIX64
 
 1750               " mappedWidth: %" PRIu32 
" mappedHeight: %" PRIu32 
"",
 
 1751               pdu.surfaceId, pdu.windowId, pdu.mappedWidth, pdu.mappedHeight);
 
 1753  if (context && context->MapSurfaceToWindow)
 
 1755    IFCALLRET(context->MapSurfaceToWindow, error, context, &pdu);
 
 1758      WLog_Print(gfx->log, WLOG_ERROR,
 
 1759                 "context->MapSurfaceToWindow failed with error %" PRIu32 
"", error);
 
 1769  WINPR_ASSERT(callback);
 
 1772  RdpgfxClientContext* context = gfx->context;
 
 1773  UINT error = CHANNEL_RC_OK;
 
 1775  if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
 
 1776    return ERROR_INVALID_DATA;
 
 1778  Stream_Read_UINT16(s, pdu.surfaceId);    
 
 1779  Stream_Read_UINT64(s, pdu.windowId);     
 
 1780  Stream_Read_UINT32(s, pdu.mappedWidth);  
 
 1781  Stream_Read_UINT32(s, pdu.mappedHeight); 
 
 1782  Stream_Read_UINT32(s, pdu.targetWidth);  
 
 1783  Stream_Read_UINT32(s, pdu.targetHeight); 
 
 1784  DEBUG_RDPGFX(gfx->log,
 
 1785               "RecvMapSurfaceToScaledWindowPdu: surfaceId: %" PRIu16 
" windowId: 0x%016" PRIX64
 
 1786               " mappedWidth: %" PRIu32 
" mappedHeight: %" PRIu32 
" targetWidth: %" PRIu32
 
 1787               " targetHeight: %" PRIu32 
"",
 
 1788               pdu.surfaceId, pdu.windowId, pdu.mappedWidth, pdu.mappedHeight, pdu.targetWidth,
 
 1791  if (context && context->MapSurfaceToScaledWindow)
 
 1793    IFCALLRET(context->MapSurfaceToScaledWindow, error, context, &pdu);
 
 1796      WLog_Print(gfx->log, WLOG_ERROR,
 
 1797                 "context->MapSurfaceToScaledWindow failed with error %" PRIu32 
"", error);
 
 1813  WINPR_ASSERT(callback);
 
 1815  const size_t beg = Stream_GetPosition(s);
 
 1818  if ((error = rdpgfx_read_header(s, &header)))
 
 1820    WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_read_header failed with error %" PRIu32 
"!",
 
 1826      gfx->log, 
"cmdId: %s (0x%04" PRIX16 
") flags: 0x%04" PRIX16 
" pduLength: %" PRIu32 
"",
 
 1827      rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId, header.flags, header.pduLength);
 
 1829  switch (header.cmdId)
 
 1831    case RDPGFX_CMDID_WIRETOSURFACE_1:
 
 1832      if ((error = rdpgfx_recv_wire_to_surface_1_pdu(callback, s)))
 
 1833        WLog_Print(gfx->log, WLOG_ERROR,
 
 1834                   "rdpgfx_recv_wire_to_surface_1_pdu failed with error %" PRIu32 
"!",
 
 1839    case RDPGFX_CMDID_WIRETOSURFACE_2:
 
 1840      if ((error = rdpgfx_recv_wire_to_surface_2_pdu(callback, s)))
 
 1841        WLog_Print(gfx->log, WLOG_ERROR,
 
 1842                   "rdpgfx_recv_wire_to_surface_2_pdu failed with error %" PRIu32 
"!",
 
 1847    case RDPGFX_CMDID_DELETEENCODINGCONTEXT:
 
 1848      if ((error = rdpgfx_recv_delete_encoding_context_pdu(callback, s)))
 
 1849        WLog_Print(gfx->log, WLOG_ERROR,
 
 1850                   "rdpgfx_recv_delete_encoding_context_pdu failed with error %" PRIu32 
"!",
 
 1855    case RDPGFX_CMDID_SOLIDFILL:
 
 1856      if ((error = rdpgfx_recv_solid_fill_pdu(callback, s)))
 
 1857        WLog_Print(gfx->log, WLOG_ERROR,
 
 1858                   "rdpgfx_recv_solid_fill_pdu failed with error %" PRIu32 
"!", error);
 
 1862    case RDPGFX_CMDID_SURFACETOSURFACE:
 
 1863      if ((error = rdpgfx_recv_surface_to_surface_pdu(callback, s)))
 
 1864        WLog_Print(gfx->log, WLOG_ERROR,
 
 1865                   "rdpgfx_recv_surface_to_surface_pdu failed with error %" PRIu32 
"!",
 
 1870    case RDPGFX_CMDID_SURFACETOCACHE:
 
 1871      if ((error = rdpgfx_recv_surface_to_cache_pdu(callback, s)))
 
 1872        WLog_Print(gfx->log, WLOG_ERROR,
 
 1873                   "rdpgfx_recv_surface_to_cache_pdu failed with error %" PRIu32 
"!",
 
 1878    case RDPGFX_CMDID_CACHETOSURFACE:
 
 1879      if ((error = rdpgfx_recv_cache_to_surface_pdu(callback, s)))
 
 1880        WLog_Print(gfx->log, WLOG_ERROR,
 
 1881                   "rdpgfx_recv_cache_to_surface_pdu failed with error %" PRIu32 
"!",
 
 1886    case RDPGFX_CMDID_EVICTCACHEENTRY:
 
 1887      if ((error = rdpgfx_recv_evict_cache_entry_pdu(callback, s)))
 
 1888        WLog_Print(gfx->log, WLOG_ERROR,
 
 1889                   "rdpgfx_recv_evict_cache_entry_pdu failed with error %" PRIu32 
"!",
 
 1894    case RDPGFX_CMDID_CREATESURFACE:
 
 1895      if ((error = rdpgfx_recv_create_surface_pdu(callback, s)))
 
 1896        WLog_Print(gfx->log, WLOG_ERROR,
 
 1897                   "rdpgfx_recv_create_surface_pdu failed with error %" PRIu32 
"!", error);
 
 1901    case RDPGFX_CMDID_DELETESURFACE:
 
 1902      if ((error = rdpgfx_recv_delete_surface_pdu(callback, s)))
 
 1903        WLog_Print(gfx->log, WLOG_ERROR,
 
 1904                   "rdpgfx_recv_delete_surface_pdu failed with error %" PRIu32 
"!", error);
 
 1908    case RDPGFX_CMDID_STARTFRAME:
 
 1909      if ((error = rdpgfx_recv_start_frame_pdu(callback, s)))
 
 1910        WLog_Print(gfx->log, WLOG_ERROR,
 
 1911                   "rdpgfx_recv_start_frame_pdu failed with error %" PRIu32 
"!", error);
 
 1915    case RDPGFX_CMDID_ENDFRAME:
 
 1916      if ((error = rdpgfx_recv_end_frame_pdu(callback, s)))
 
 1917        WLog_Print(gfx->log, WLOG_ERROR,
 
 1918                   "rdpgfx_recv_end_frame_pdu failed with error %" PRIu32 
"!", error);
 
 1922    case RDPGFX_CMDID_RESETGRAPHICS:
 
 1923      if ((error = rdpgfx_recv_reset_graphics_pdu(callback, s)))
 
 1924        WLog_Print(gfx->log, WLOG_ERROR,
 
 1925                   "rdpgfx_recv_reset_graphics_pdu failed with error %" PRIu32 
"!", error);
 
 1929    case RDPGFX_CMDID_MAPSURFACETOOUTPUT:
 
 1930      if ((error = rdpgfx_recv_map_surface_to_output_pdu(callback, s)))
 
 1931        WLog_Print(gfx->log, WLOG_ERROR,
 
 1932                   "rdpgfx_recv_map_surface_to_output_pdu failed with error %" PRIu32 
"!",
 
 1937    case RDPGFX_CMDID_CACHEIMPORTREPLY:
 
 1938      if ((error = rdpgfx_recv_cache_import_reply_pdu(callback, s)))
 
 1939        WLog_Print(gfx->log, WLOG_ERROR,
 
 1940                   "rdpgfx_recv_cache_import_reply_pdu failed with error %" PRIu32 
"!",
 
 1945    case RDPGFX_CMDID_CAPSCONFIRM:
 
 1946      if ((error = rdpgfx_recv_caps_confirm_pdu(callback, s)))
 
 1947        WLog_Print(gfx->log, WLOG_ERROR,
 
 1948                   "rdpgfx_recv_caps_confirm_pdu failed with error %" PRIu32 
"!", error);
 
 1950      if ((error = rdpgfx_send_cache_offer(gfx)))
 
 1951        WLog_Print(gfx->log, WLOG_ERROR,
 
 1952                   "rdpgfx_send_cache_offer failed with error %" PRIu32 
"!", error);
 
 1956    case RDPGFX_CMDID_MAPSURFACETOWINDOW:
 
 1957      if ((error = rdpgfx_recv_map_surface_to_window_pdu(callback, s)))
 
 1958        WLog_Print(gfx->log, WLOG_ERROR,
 
 1959                   "rdpgfx_recv_map_surface_to_window_pdu failed with error %" PRIu32 
"!",
 
 1964    case RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW:
 
 1965      if ((error = rdpgfx_recv_map_surface_to_scaled_window_pdu(callback, s)))
 
 1966        WLog_Print(gfx->log, WLOG_ERROR,
 
 1967                   "rdpgfx_recv_map_surface_to_scaled_window_pdu failed with error %" PRIu32
 
 1973    case RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT:
 
 1974      if ((error = rdpgfx_recv_map_surface_to_scaled_output_pdu(callback, s)))
 
 1975        WLog_Print(gfx->log, WLOG_ERROR,
 
 1976                   "rdpgfx_recv_map_surface_to_scaled_output_pdu failed with error %" PRIu32
 
 1983      error = CHANNEL_RC_BAD_PROC;
 
 1989    WLog_Print(gfx->log, WLOG_ERROR, 
"Error while processing GFX cmdId: %s (0x%04" PRIX16 
")",
 
 1990               rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId);
 
 1991    Stream_SetPosition(s, (beg + header.pduLength));
 
 1995  end = Stream_GetPosition(s);
 
 1997  if (end != (beg + header.pduLength))
 
 1999    WLog_Print(gfx->log, WLOG_ERROR,
 
 2000               "Unexpected gfx pdu end: Actual: %" PRIuz 
", Expected: %" PRIuz, end,
 
 2001               (beg + header.pduLength));
 
 2002    Stream_SetPosition(s, (beg + header.pduLength));
 
 2013static UINT rdpgfx_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, 
wStream* data)
 
 2017  BYTE* pDstData = NULL;
 
 2019  WINPR_ASSERT(callback);
 
 2021  UINT error = CHANNEL_RC_OK;
 
 2024  status = zgfx_decompress(gfx->zgfx, Stream_ConstPointer(data),
 
 2025                           (UINT32)Stream_GetRemainingLength(data), &pDstData, &DstSize, 0);
 
 2029    WLog_Print(gfx->log, WLOG_ERROR, 
"zgfx_decompress failure! status: %d", status);
 
 2031    return ERROR_INTERNAL_ERROR;
 
 2035  wStream* s = Stream_StaticConstInit(&sbuffer, pDstData, DstSize);
 
 2039    WLog_Print(gfx->log, WLOG_ERROR, 
"calloc failed!");
 
 2041    return CHANNEL_RC_NO_MEMORY;
 
 2044  while (Stream_GetPosition(s) < Stream_Length(s))
 
 2046    if ((error = rdpgfx_recv_pdu(callback, s)))
 
 2048      WLog_Print(gfx->log, WLOG_ERROR, 
"rdpgfx_recv_pdu failed with error %" PRIu32 
"!",
 
 2063static UINT rdpgfx_on_open(IWTSVirtualChannelCallback* pChannelCallback)
 
 2066  WINPR_ASSERT(callback);
 
 2069  RdpgfxClientContext* context = gfx->context;
 
 2070  UINT error = CHANNEL_RC_OK;
 
 2071  BOOL do_caps_advertise = TRUE;
 
 2072  gfx->sendFrameAcks = TRUE;
 
 2076    IFCALLRET(context->OnOpen, error, context, &do_caps_advertise, &gfx->sendFrameAcks);
 
 2079      WLog_Print(gfx->log, WLOG_ERROR, 
"context->OnOpen failed with error %" PRIu32 
"",
 
 2083  if (do_caps_advertise)
 
 2084    error = rdpgfx_send_supported_caps(callback);
 
 2094static UINT rdpgfx_on_close(IWTSVirtualChannelCallback* pChannelCallback)
 
 2096  UINT error = CHANNEL_RC_OK;
 
 2098  WINPR_ASSERT(callback);
 
 2104  RdpgfxClientContext* context = gfx->context;
 
 2106  DEBUG_RDPGFX(gfx->log, 
"OnClose");
 
 2107  error = rdpgfx_save_persistent_cache(gfx);
 
 2112    WLog_Print(gfx->log, WLOG_ERROR,
 
 2113               "rdpgfx_save_persistent_cache failed with error %" PRIu32 
"", error);
 
 2116  free_surfaces(context, gfx->SurfaceTable);
 
 2117  evict_cache_slots(context, gfx->MaxCacheSlots, gfx->CacheSlots);
 
 2120  gfx->UnacknowledgedFrames = 0;
 
 2121  gfx->TotalDecodedFrames = 0;
 
 2125    IFCALL(context->OnClose, context);
 
 2129  return CHANNEL_RC_OK;
 
 2136  RdpgfxClientContext* context = gfx->context;
 
 2138  DEBUG_RDPGFX(gfx->log, 
"Terminated");
 
 2139  rdpgfx_client_context_free(context);
 
 2147static UINT rdpgfx_set_surface_data(RdpgfxClientContext* context, UINT16 surfaceId, 
void* pData)
 
 2150  WINPR_ASSERT(context);
 
 2153  key = ((ULONG_PTR)surfaceId) + 1;
 
 2157    if (!HashTable_Insert(gfx->SurfaceTable, (
void*)key, pData))
 
 2158      return ERROR_BAD_ARGUMENTS;
 
 2161    HashTable_Remove(gfx->SurfaceTable, (
void*)key);
 
 2163  return CHANNEL_RC_OK;
 
 2171static UINT rdpgfx_get_surface_ids(RdpgfxClientContext* context, UINT16** ppSurfaceIds,
 
 2175  UINT16* pSurfaceIds = NULL;
 
 2176  ULONG_PTR* pKeys = NULL;
 
 2177  WINPR_ASSERT(context);
 
 2180  count = HashTable_GetKeys(gfx->SurfaceTable, &pKeys);
 
 2182  WINPR_ASSERT(ppSurfaceIds);
 
 2183  WINPR_ASSERT(count_out);
 
 2187    return CHANNEL_RC_OK;
 
 2190  pSurfaceIds = (UINT16*)calloc(count, 
sizeof(UINT16));
 
 2194    WLog_Print(gfx->log, WLOG_ERROR, 
"calloc failed!");
 
 2196    return CHANNEL_RC_NO_MEMORY;
 
 2199  for (
size_t index = 0; index < count; index++)
 
 2201    pSurfaceIds[index] = (UINT16)(pKeys[index] - 1);
 
 2205  *ppSurfaceIds = pSurfaceIds;
 
 2206  *count_out = (UINT16)count;
 
 2207  return CHANNEL_RC_OK;
 
 2210static void* rdpgfx_get_surface_data(RdpgfxClientContext* context, UINT16 surfaceId)
 
 2214  WINPR_ASSERT(context);
 
 2217  key = ((ULONG_PTR)surfaceId) + 1;
 
 2218  pData = HashTable_GetItemValue(gfx->SurfaceTable, (
void*)key);
 
 2227static UINT rdpgfx_set_cache_slot_data(RdpgfxClientContext* context, UINT16 cacheSlot, 
void* pData)
 
 2229  WINPR_ASSERT(context);
 
 2234  if (cacheSlot == 0 || cacheSlot > gfx->MaxCacheSlots)
 
 2236    WLog_ERR(TAG, 
"invalid cache slot %" PRIu16 
", must be between 1 and %" PRIu16 
"",
 
 2237             cacheSlot, gfx->MaxCacheSlots);
 
 2238    return ERROR_INVALID_INDEX;
 
 2241  gfx->CacheSlots[cacheSlot - 1] = pData;
 
 2242  return CHANNEL_RC_OK;
 
 2245static void* rdpgfx_get_cache_slot_data(RdpgfxClientContext* context, UINT16 cacheSlot)
 
 2248  WINPR_ASSERT(context);
 
 2252  if (cacheSlot == 0 || cacheSlot > gfx->MaxCacheSlots)
 
 2254    WLog_ERR(TAG, 
"invalid cache slot %" PRIu16 
", must be between 1 and %" PRIu16 
"",
 
 2255             cacheSlot, gfx->MaxCacheSlots);
 
 2259  pData = gfx->CacheSlots[cacheSlot - 1];
 
 2264                           WINPR_ATTR_UNUSED rdpSettings* settings)
 
 2266  RdpgfxClientContext* context = NULL;
 
 2270  gfx->rdpcontext = rcontext;
 
 2271  gfx->log = WLog_Get(TAG);
 
 2273  gfx->SurfaceTable = HashTable_New(TRUE);
 
 2274  if (!gfx->SurfaceTable)
 
 2276    WLog_ERR(TAG, 
"HashTable_New for surfaces failed !");
 
 2277    return CHANNEL_RC_NO_MEMORY;
 
 2280  gfx->suspendFrameAcks =
 
 2282  gfx->MaxCacheSlots =
 
 2285  context = (RdpgfxClientContext*)calloc(1, 
sizeof(RdpgfxClientContext));
 
 2288    WLog_ERR(TAG, 
"context calloc failed!");
 
 2289    HashTable_Free(gfx->SurfaceTable);
 
 2290    gfx->SurfaceTable = NULL;
 
 2291    return CHANNEL_RC_NO_MEMORY;
 
 2294  gfx->zgfx = zgfx_context_new(FALSE);
 
 2297    WLog_ERR(TAG, 
"zgfx_context_new failed!");
 
 2298    HashTable_Free(gfx->SurfaceTable);
 
 2299    gfx->SurfaceTable = NULL;
 
 2301    return CHANNEL_RC_NO_MEMORY;
 
 2304  context->handle = (
void*)gfx;
 
 2305  context->GetSurfaceIds = rdpgfx_get_surface_ids;
 
 2306  context->SetSurfaceData = rdpgfx_set_surface_data;
 
 2307  context->GetSurfaceData = rdpgfx_get_surface_data;
 
 2308  context->SetCacheSlotData = rdpgfx_set_cache_slot_data;
 
 2309  context->GetCacheSlotData = rdpgfx_get_cache_slot_data;
 
 2310  context->CapsAdvertise = rdpgfx_send_caps_advertise_pdu;
 
 2311  context->FrameAcknowledge = rdpgfx_send_frame_acknowledge_pdu;
 
 2312  context->CacheImportOffer = rdpgfx_send_cache_import_offer_pdu;
 
 2313  context->QoeFrameAcknowledge = rdpgfx_send_qoe_frame_acknowledge_pdu;
 
 2315  gfx->base.iface.pInterface = (
void*)context;
 
 2316  gfx->context = context;
 
 2317  return CHANNEL_RC_OK;
 
 2320void rdpgfx_client_context_free(RdpgfxClientContext* context)
 
 2330  free_surfaces(context, gfx->SurfaceTable);
 
 2331  evict_cache_slots(context, gfx->MaxCacheSlots, gfx->CacheSlots);
 
 2335    zgfx_context_free(gfx->zgfx);
 
 2339  HashTable_Free(gfx->SurfaceTable);
 
 2343static const IWTSVirtualChannelCallback rdpgfx_callbacks = { rdpgfx_on_data_received,
 
 2344                                                           rdpgfx_on_open, rdpgfx_on_close,
 
 2352FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpgfx_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
 
 2354  return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, RDPGFX_DVC_CHANNEL_NAME,
 
 2356                                        &rdpgfx_callbacks, init_plugin_cb, terminate_plugin_cb);
 
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
 
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
 
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.