|
1 // Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // Window redraw code, three sorts of redrawing are supported |
|
15 // CRedrawMsgWindow handles it via sending a redraw message to the client |
|
16 // |
|
17 // |
|
18 |
|
19 #include "redrawmsgwindow.h" |
|
20 #include "gc.h" |
|
21 #include "playbackgc.h" |
|
22 #include "inifile.h" |
|
23 #include "rootwin.h" |
|
24 #include "wstop.h" |
|
25 #include "ANIM.H" |
|
26 #include "EVQUEUE.H" |
|
27 #include <s32mem.h> |
|
28 #include <gdi.h> |
|
29 #include "panics.h" |
|
30 #include "rootwin.h" |
|
31 #include "EVENT.H" |
|
32 #include "wsfont.h" |
|
33 #include <graphics/wsgraphicdrawerinterface.h> |
|
34 #include "../debuglog/DEBUGLOG.H" |
|
35 |
|
36 const TUint KDrawBufferGranularity = 240; |
|
37 const TInt KRedrawRegionGranularity = 8; |
|
38 const TInt KReadBufferMaxLen=0x100; |
|
39 const TInt KRegionCompressThreshold = 6; // number of rectangles in region at which we try to compress it |
|
40 |
|
41 /** Max number of non-redraw segments allowed before starting to throw away the oldest */ |
|
42 const TInt KNonRedrawSegMaxLimit = 16; |
|
43 /** The number of non-redraw segments to spare from deletion once their number |
|
44 have grown beyond KNonRedrawSegMaxLimit */ |
|
45 const TInt KNonRedrawSegThreshold = 8; |
|
46 |
|
47 TInt CWsRedrawMsgWindow::iNonRedrawAgeLimit = 0; |
|
48 CWsRedrawMsgWindow::TAtomicityType CWsRedrawMsgWindow::iAtomicity = ENoAtomicity; |
|
49 |
|
50 #if defined(__WINS__) && defined(_DEBUG) |
|
51 # include "offscreenbitmap.h" |
|
52 # define DEBUGOSB { CWsOffScreenBitmap * ofb = Screen()->OffScreenBitmap(); if (ofb) ofb->Update(); } |
|
53 #else |
|
54 # define DEBUGOSB |
|
55 #endif |
|
56 |
|
57 #ifndef _DEBUG |
|
58 |
|
59 #define LOG_WINDOW_REDRAW_START(wswin,region) |
|
60 #define LOG_WINDOW_REDRAW_END(wswin) |
|
61 #define LOG_REDRAW_SEGMENT(segmentIndex,segmentType) |
|
62 #define LOG_REDRAW_SEGMENT_REGION(region) |
|
63 #define LOG_PLAYBACK_GC_COMMAND(opcode,data) |
|
64 |
|
65 #else |
|
66 |
|
67 #define LOG_WINDOW_REDRAW_START(wswin,region) LogDrawCommandsStart(wswin,region) |
|
68 #define LOG_WINDOW_REDRAW_END(wswin) LogDrawCommandsEnd(wswin) |
|
69 #define LOG_REDRAW_SEGMENT(segmentIndex,segmentType) LogRedrawSegment(segmentIndex, segmentType) |
|
70 #define LOG_REDRAW_SEGMENT_REGION(region) {if(wsDebugLog){ LogRegion(region);}} |
|
71 #define LOG_PLAYBACK_GC_COMMAND(opcode,data) {if (wsDebugLog) {wsDebugLog->Command(WS_HANDLE_GC, opcode, data, NULL);}} |
|
72 |
|
73 extern CDebugLogBase *wsDebugLog; |
|
74 |
|
75 class TTruncateOverflow : public TDesOverflow |
|
76 { |
|
77 public: |
|
78 virtual void Overflow(TDes&) {}; |
|
79 }; |
|
80 |
|
81 LOCAL_C void LogRedrawSegment(TUint aSegmentIndex, CWsRedrawMsgWindow::TRedrawSegmentType aSegmentType) |
|
82 { |
|
83 if (wsDebugLog) |
|
84 { |
|
85 TBuf<LogTBufSize> log; |
|
86 TTruncateOverflow overflow; |
|
87 _LIT(KLogRedrawSegment, ">> CRedrawSegment[%d] "); |
|
88 log.AppendFormat(KLogRedrawSegment, &overflow, aSegmentIndex); |
|
89 _LIT(KLogRedrawSegmentPending, "Pending"); |
|
90 _LIT(KLogRedrawSegmentRedraw, "Redraw"); |
|
91 _LIT(KLogRedrawSegmentNonRedraw, "NonRedraw"); |
|
92 switch(aSegmentType) |
|
93 { |
|
94 case CWsRedrawMsgWindow::ESegmentTypePendingRedraw : |
|
95 log.AppendFormat(KLogRedrawSegmentPending, &overflow); |
|
96 break; |
|
97 case CWsRedrawMsgWindow::ESegmentTypeRedraw : |
|
98 log.AppendFormat(KLogRedrawSegmentRedraw, &overflow); |
|
99 break; |
|
100 case CWsRedrawMsgWindow::ESegmentTypeNonRedraw : |
|
101 log.AppendFormat(KLogRedrawSegmentNonRedraw, &overflow); |
|
102 break; |
|
103 default : |
|
104 { |
|
105 } |
|
106 } |
|
107 wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log); |
|
108 } |
|
109 } |
|
110 |
|
111 LOCAL_C void LogRegion(const TRegion* aRegion) |
|
112 { |
|
113 TBuf<LogTBufSize> log; |
|
114 TTruncateOverflow overflow; |
|
115 TInt rectCount = (aRegion == NULL ? 0 : aRegion->Count()); |
|
116 log.AppendFormat(_L("region [%d,"), &overflow, rectCount); |
|
117 if (rectCount > 0) |
|
118 { |
|
119 const TRect* rectangles = aRegion->RectangleList(); |
|
120 TBuf<1> comma; |
|
121 for (TInt ii = 0; ii < rectCount; ii++) |
|
122 { |
|
123 TRect current = rectangles[ii]; |
|
124 log.AppendFormat(_L("%S{{%d,%d},{%d,%d}}"), &overflow, &comma, |
|
125 current.iTl.iX,current.iTl.iY,current.iBr.iX,current.iBr.iY); |
|
126 comma = _L(","); |
|
127 } |
|
128 } |
|
129 else |
|
130 { |
|
131 log.AppendFormat(_L("NULL"), &overflow); |
|
132 } |
|
133 log.AppendFormat(_L("]"), &overflow); |
|
134 wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log); |
|
135 } |
|
136 |
|
137 LOCAL_C void LogDrawCommandsStart(const CWsWindow* aWsWin, const TRegion* aRegion) |
|
138 { |
|
139 if (wsDebugLog) |
|
140 { |
|
141 _LIT(KLogDrawCommandsStart, ">> CWsRedrawMsgWindow::DrawCommandsL() [%S][app %d] RWindow[%d]"); |
|
142 const TDesC& clientName = aWsWin->WsOwner()->Client().FullName(); |
|
143 TBuf<LogTBufSize> log; |
|
144 TTruncateOverflow overflow; |
|
145 log.AppendFormat(KLogDrawCommandsStart, &overflow, &clientName, aWsWin->WsOwner()->ConnectionHandle(), aWsWin->LogHandle()); |
|
146 wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log); |
|
147 LogRegion(aRegion); |
|
148 } |
|
149 } |
|
150 |
|
151 LOCAL_C void LogDrawCommandsEnd(const CWsWindow* aWsWin) |
|
152 { |
|
153 if (wsDebugLog) |
|
154 { |
|
155 _LIT(KLogDrawCommandsEnd, "<< CWsRedrawMsgWindow::DrawCommandsL() [%S][app %d] RWindow[%d]"); |
|
156 const TDesC& clientName = aWsWin->WsOwner()->Client().FullName(); |
|
157 TBuf<LogTBufSize> log; |
|
158 TTruncateOverflow overflow; |
|
159 log.AppendFormat(KLogDrawCommandsEnd, &overflow, &clientName, aWsWin->WsOwner()->ConnectionHandle(), aWsWin->LogHandle()); |
|
160 wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log); |
|
161 } |
|
162 } |
|
163 |
|
164 #endif |
|
165 |
|
166 // |
|
167 // Redraw windows // |
|
168 // |
|
169 |
|
170 CWsRedrawMsgWindow::CRedrawSegment::CRedrawSegment() |
|
171 { |
|
172 } |
|
173 |
|
174 CWsRedrawMsgWindow::CRedrawSegment* CWsRedrawMsgWindow::CRedrawSegment::NewLC(const TRect& aRect, TRedrawSegmentType aNewRegionType) |
|
175 { |
|
176 CRedrawSegment* self = new (ELeave) CRedrawSegment(); |
|
177 CleanupStack::PushL(self); |
|
178 self->ConstructL(aRect, aNewRegionType); |
|
179 return self; |
|
180 } |
|
181 |
|
182 void CWsRedrawMsgWindow::StaticInitL() |
|
183 { |
|
184 _LIT(KNonRedrawAgeLimit, "NONREDRAWAGELIMIT"); |
|
185 const TInt KDefaultNonRedrawAgeLimit = 1000000; |
|
186 if (!WsIniFile->FindVar(KNonRedrawAgeLimit, iNonRedrawAgeLimit)) |
|
187 iNonRedrawAgeLimit = KDefaultNonRedrawAgeLimit; |
|
188 |
|
189 _LIT(KAtomicRedraws,"ATOMICREDRAWS"); |
|
190 _LIT(KAtomicSegment,"SEGMENT"); |
|
191 _LIT(KAtomicWindow,"WINDOW"); |
|
192 |
|
193 TPtrC atomicityTypeString; |
|
194 if (WsIniFile->FindVar(KAtomicRedraws,atomicityTypeString)) |
|
195 { |
|
196 if(atomicityTypeString.CompareF(KAtomicSegment)==0 || atomicityTypeString.Length()==0) |
|
197 iAtomicity = ESegment; |
|
198 else if(atomicityTypeString.CompareF(KAtomicWindow)==0) |
|
199 iAtomicity = EWindow; |
|
200 } |
|
201 } |
|
202 |
|
203 void CWsRedrawMsgWindow::CRedrawSegment::ReleaseFontsAndBitmaps() |
|
204 { |
|
205 // release Bitmap and Font handles |
|
206 TInt count = iWsBitmapArray.Count(); |
|
207 TInt ii; |
|
208 for (ii = count - 1; ii >= 0; ii--) |
|
209 { |
|
210 iWsBitmapArray[ii]->DecRefCount(); |
|
211 iWsBitmapArray.Remove(ii); |
|
212 } |
|
213 |
|
214 count = iWsFontArray.Count(); |
|
215 for (ii = count - 1; ii >= 0; --ii) |
|
216 { |
|
217 CWsFontCache::Instance()->ReleaseFont(iWsFontArray[ii]); |
|
218 iWsFontArray.Remove(ii); |
|
219 } |
|
220 |
|
221 count = iFbsBitmapArray.Count(); |
|
222 for(ii = count - 1 ; ii >= 0; ii--) |
|
223 { |
|
224 delete iFbsBitmapArray[ii]; |
|
225 iFbsBitmapArray.Remove(ii); |
|
226 } |
|
227 } |
|
228 |
|
229 /* Set new rectangle and region type for initial or reset redraw region |
|
230 @leave KErrNoMemory no memory to update region details |
|
231 */ |
|
232 void CWsRedrawMsgWindow::CRedrawSegment::ConstructL(const TRect& aRect, TRedrawSegmentType aNewRegionType) |
|
233 { |
|
234 iDrawCommands = CBufSeg::NewL(KDrawBufferGranularity); |
|
235 iCreationTime.UniversalTime(); |
|
236 |
|
237 iRegion.AddRect(aRect); |
|
238 if (iRegion.CheckError()) |
|
239 { |
|
240 User::Leave(KErrNoMemory); |
|
241 } |
|
242 iRedrawSegmentType = aNewRegionType; |
|
243 } |
|
244 |
|
245 CWsRedrawMsgWindow::CRedrawSegment::~CRedrawSegment() |
|
246 { |
|
247 delete iDrawCommands; |
|
248 |
|
249 iRegion.Close(); |
|
250 |
|
251 // Release Font and Bitmap handles, close arrays |
|
252 ReleaseFontsAndBitmaps(); |
|
253 iFbsBitmapArray.Close(); |
|
254 iWsBitmapArray.Close(); |
|
255 iWsFontArray.Close(); |
|
256 iDrawerArray.Close(); |
|
257 } |
|
258 |
|
259 void CWsRedrawMsgWindow::WindowClosing() |
|
260 { |
|
261 iWsWin->WsOwner()->RedrawQueue()->RemoveInvalid(this); |
|
262 } |
|
263 |
|
264 |
|
265 TInt CWsRedrawMsgWindow::CRedrawSegment::SizeInBytes() const |
|
266 { |
|
267 TInt size = sizeof(CWsRedrawMsgWindow::CRedrawSegment); |
|
268 size += iDrawCommands->Size(); |
|
269 size += iFbsBitmapArray.Count() * sizeof(CFbsBitmap); |
|
270 size += iWsBitmapArray.Count() * sizeof(DWsBitmap); |
|
271 size += iWsFontArray.Count() * sizeof(CWsFbsFont); |
|
272 size += iDrawerArray.Count() * sizeof(TGraphicDrawerId); |
|
273 return size; |
|
274 } |
|
275 |
|
276 CWsRedrawMsgWindow::CWsRedrawMsgWindow(CWsWindow *aWin) |
|
277 :CWsWindowRedraw(aWin), iBackColor(aWin->RootWindow()->DefaultBackgroundColor()), iFlags(EBackgroundClear|EStoringEntireWindow), |
|
278 iRedrawSegments(KRedrawRegionGranularity), |
|
279 iCurrentSegment(0), |
|
280 iOSBStatus(ETrue) |
|
281 { |
|
282 } |
|
283 |
|
284 void CWsRedrawMsgWindow::ConstructL() |
|
285 { |
|
286 CWsWindowRedraw::ConstructL(); |
|
287 Invalidate(&WsWin()->Rel()); |
|
288 iWsWin->WsOwner()->RedrawQueue()->ReCalcOrder(); |
|
289 } |
|
290 |
|
291 CWsRedrawMsgWindow::~CWsRedrawMsgWindow() |
|
292 { |
|
293 WS_ASSERT_DEBUG(iMemoryLock == 0, EWsPanicMemoryLock); |
|
294 RemoveFromRedrawQueueIfEmpty(); |
|
295 iInvalid.Close(); |
|
296 iLocalRedrawRegion.Close(); |
|
297 iRedrawSegments.ResetAndDestroy(); |
|
298 iCurrentSegment=NULL; |
|
299 } |
|
300 |
|
301 /** |
|
302 These three functions actually check for a value they have already asserted on. This is intentional. |
|
303 */ |
|
304 void CWsRedrawMsgWindow::ExpandCommandBufferL(TInt aLength) |
|
305 { |
|
306 WS_ASSERT_DEBUG(iCurrentSegment != NULL, EWsPanicRedrawSegmentsInvalidState); |
|
307 |
|
308 if (iCurrentSegment) |
|
309 { |
|
310 // need more space? |
|
311 if (iCurrentSegment->iCurrentCommandBufferWritePos + aLength > iCurrentSegment->iDrawCommands->Size()) |
|
312 { |
|
313 iCurrentSegment->iDrawCommands->ResizeL(iCurrentSegment->iCurrentCommandBufferWritePos + aLength); |
|
314 } |
|
315 } |
|
316 } |
|
317 |
|
318 void CWsRedrawMsgWindow::CommandBufferWrite(const TDesC8& aDes, TInt aLength) |
|
319 { |
|
320 WS_ASSERT_DEBUG(iCurrentSegment != NULL, EWsPanicRedrawSegmentsInvalidState); |
|
321 WS_ASSERT_DEBUG(iCurrentSegment->iCurrentCommandBufferWritePos + aLength <= iCurrentSegment->iDrawCommands->Size(), EWsPanicDrawCommandsInvalidState); |
|
322 if (iCurrentSegment) |
|
323 { |
|
324 iCurrentSegment->iDrawCommands->Write(iCurrentSegment->iCurrentCommandBufferWritePos, aDes, aLength); |
|
325 iCurrentSegment->iCurrentCommandBufferWritePos += aLength; |
|
326 } |
|
327 } |
|
328 |
|
329 void CWsRedrawMsgWindow::CommandBufferWrite(const TAny* aPtr,TInt aLength) |
|
330 { |
|
331 WS_ASSERT_DEBUG(iCurrentSegment != NULL, EWsPanicRedrawSegmentsInvalidState); |
|
332 WS_ASSERT_DEBUG(iCurrentSegment->iCurrentCommandBufferWritePos + aLength <= iCurrentSegment->iDrawCommands->Size(), EWsPanicDrawCommandsInvalidState); |
|
333 if (iCurrentSegment) |
|
334 { |
|
335 iCurrentSegment->iDrawCommands->Write(iCurrentSegment->iCurrentCommandBufferWritePos, aPtr, aLength); |
|
336 iCurrentSegment->iCurrentCommandBufferWritePos += aLength; |
|
337 } |
|
338 } |
|
339 |
|
340 /*------------------------------------------------------------------------------ |
|
341 Description: Processes draw commands. These are received as opcodes. |
|
342 -----------------------------------------------------------------------------*/ |
|
343 TBool CWsRedrawMsgWindow::CommandL(TInt aOpcode, TWsWinCmdUnion &aCmd) |
|
344 { |
|
345 switch(aOpcode) |
|
346 { |
|
347 case EWsWinOpEnableOSB: |
|
348 iOSBStatus = ETrue; |
|
349 break; |
|
350 case EWsWinOpDisableOSB: |
|
351 iOSBStatus = EFalse; |
|
352 break; |
|
353 case EWsWinOpSetBackgroundColor: |
|
354 iBackColor = *aCmd.rgb; |
|
355 iFlags |= EBackgroundClear; |
|
356 break; |
|
357 case EWsWinOpSetNoBackgroundColor: |
|
358 iFlags &= ~EBackgroundClear; |
|
359 break; |
|
360 case EWsWinOpInvalidate: |
|
361 Invalidate(aCmd.rect); |
|
362 break; |
|
363 case EWsWinOpInvalidateFull: |
|
364 Invalidate(); |
|
365 break; |
|
366 case EWsWinOpBeginRedraw: |
|
367 BeginRedraw(aCmd.rect); |
|
368 ValidateRect(aCmd.rect); |
|
369 break; |
|
370 case EWsWinOpBeginRedrawFull: |
|
371 BeginRedraw(NULL); |
|
372 ValidateRect(NULL); |
|
373 break; |
|
374 case EWsWinOpEndRedraw: |
|
375 EndRedraw(); |
|
376 break; |
|
377 case EWsWinOpGetInvalidRegionCount: |
|
378 { |
|
379 SetReply(iInvalid.Count()); |
|
380 } |
|
381 break; |
|
382 case EWsWinOpGetInvalidRegion: |
|
383 { |
|
384 if ((*aCmd.Int) <= 0) |
|
385 OwnerPanic(EWservPanicInvalidRegionCount); |
|
386 if ((!iInvalid.CheckError()) && iInvalid.Count() == (*aCmd.Int)) |
|
387 { |
|
388 CWsClient::ReplyBuf(iInvalid.RectangleList(),(*aCmd.Int) * sizeof(TRect)); |
|
389 SetReply(EFalse); |
|
390 } |
|
391 else |
|
392 SetReply(ETrue); |
|
393 } |
|
394 break; |
|
395 case EWsWinOpStoreDrawCommands: |
|
396 /* If the client asks us not to store commands, we still store the commands |
|
397 for the region of the window which can be seen through the parent, but |
|
398 won't attempt to obtain the entire window. |
|
399 */ |
|
400 if (*aCmd.Bool) |
|
401 { |
|
402 SetScope(EStoreEntireWindow); |
|
403 } |
|
404 else |
|
405 { |
|
406 // Clients that turn their redraw store off will still get one, |
|
407 // but it will only attempt to store the current viewport. |
|
408 SetScope(EStoreViewport); |
|
409 } |
|
410 break; |
|
411 case EWsWinOpHandleTransparencyUpdate: // deprecated |
|
412 case EWsWinOpSetTransparencyBitmap: // deprecated |
|
413 case EWsWinOpSetTransparencyFactor: // deprecated |
|
414 case EWsWinOpSetTransparencyBitmapCWs: // deprecated |
|
415 break; // do nothing. |
|
416 case EWsWinOpIsRedrawStoreEnabled: |
|
417 SetReply(ETrue); |
|
418 break; |
|
419 case EWsWinOpClearRedrawStore: |
|
420 DiscardStoredCommands(); |
|
421 break; |
|
422 default: |
|
423 return(EFalse); |
|
424 } |
|
425 return(ETrue); |
|
426 } |
|
427 |
|
428 /** |
|
429 */ |
|
430 void CWsRedrawMsgWindow::BeginRedraw(const TRect* aRect) |
|
431 { |
|
432 if(InRedraw()) |
|
433 OwnerPanic(EWservPanicDrawCommandsInvalidState); |
|
434 iFlags|=EBeginEndRedraw; |
|
435 TRAPD(err,DoBeginRedrawL(aRect)); |
|
436 DiscardStoredCommandsIfError(err); |
|
437 } |
|
438 |
|
439 void CWsRedrawMsgWindow::DoBeginRedrawL(const TRect* aRect) |
|
440 { |
|
441 const TRect redrawRect = (aRect ? *aRect : TRect(WsWin()->Size())); |
|
442 if (redrawRect.IsEmpty()) |
|
443 { |
|
444 //Skip empty rects since they are not added to the region |
|
445 iCurrentSegment = NULL; |
|
446 } |
|
447 else |
|
448 { |
|
449 CreateNewSegmentL(redrawRect, CWsRedrawMsgWindow::ESegmentTypePendingRedraw); |
|
450 if (iAtomicity==ENoAtomicity) |
|
451 PromoteLastPendingSegment(); |
|
452 } |
|
453 } |
|
454 |
|
455 void CWsRedrawMsgWindow::Invalidate(const TRect * aRect) |
|
456 { |
|
457 //The memory allocation in this function can trigger a call to ReleaseMemory(), which would |
|
458 //recursively call this function again. This would cause a memory leak. To avoid this |
|
459 //we call Lock() to block ReleaseMemory() from this object while executing this function. |
|
460 Lock(); |
|
461 if (!aRect) |
|
462 { |
|
463 iInvalid.Clear(); |
|
464 iInvalid.Copy(iWsWin->WindowArea()); |
|
465 iInvalid.Offset(-iWsWin->Origin()); |
|
466 } |
|
467 else if((!aRect->IsEmpty()) && aRect->IsNormalized()) |
|
468 { |
|
469 iInvalid.AddRect(*aRect); |
|
470 iInvalid.Tidy(); |
|
471 } |
|
472 if (iWsWin->IsVisible()) |
|
473 { |
|
474 QueueRedraw(); |
|
475 iWsWin->WsOwner()->TriggerRedraw(); //wtf isn't the redrawq already scheduling itself? |
|
476 } |
|
477 Unlock(); |
|
478 } |
|
479 |
|
480 /** |
|
481 If a draw command is received outside a begin/end redraw pair, then it is stored in a non-redraw |
|
482 segment. This function creates such a segment if it isn't already available. |
|
483 */ |
|
484 void CWsRedrawMsgWindow::HandleNonRedrawCommand(TWsGcOpcodes aOpcode) |
|
485 { |
|
486 // calling code should check the Window State |
|
487 WS_ASSERT_DEBUG(!InRedraw(), EWsPanicDrawCommandsInvalidState); |
|
488 |
|
489 // Attempting to draw part of a polygon in a new segment - only a very bad client can do this: |
|
490 TBool canCreate = !(aOpcode == EWsGcOpSegmentedDrawPolygonData || aOpcode == EWsGcOpDrawSegmentedPolygon); |
|
491 if ((!iCurrentSegment) && (!canCreate)) |
|
492 { |
|
493 OwnerPanic(EWservPanicBadPolyData); |
|
494 } |
|
495 |
|
496 if (canCreate) |
|
497 AgeNonRedrawSegments(); |
|
498 |
|
499 // If current redraw region is not for Non-Redraw drawing, add a region corresponding to the |
|
500 // full window to the redraw store. Need to make sure that this is done |
|
501 // before the AppendCommand, or bitmap/font handles are recorded, in StoreDrawCommandL |
|
502 TInt err = KErrNone; |
|
503 if ((!iCurrentSegment) || iCurrentSegment->iRedrawSegmentType != ESegmentTypeNonRedraw) |
|
504 { |
|
505 TRAP(err,CreateNewSegmentL(TRect(WsWin()->Size()), ESegmentTypeNonRedraw)); |
|
506 } |
|
507 |
|
508 if (err != KErrNone) |
|
509 { |
|
510 Invalidate(); |
|
511 } |
|
512 |
|
513 if(iWsWin->VisibleRegion().CheckError()) |
|
514 iWsWin->Screen()->AddRedrawRegion(iWsWin->WindowArea()); |
|
515 else if(iWsWin->VisibleRegion().Count()) |
|
516 iWsWin->Screen()->AddRedrawRegion(iWsWin->VisibleRegion()); |
|
517 else if(!iWsWin->HasBeenDrawnToScreen()) |
|
518 CliWin()->ScheduleRegionUpdate(&iWsWin->VisibleRegion()); |
|
519 } |
|
520 |
|
521 /** |
|
522 This function attempts to prevent non-redraw segments from growing indefinitely by requesting redraws |
|
523 and throwing away old ones. |
|
524 */ |
|
525 void CWsRedrawMsgWindow::AgeNonRedrawSegments() |
|
526 { |
|
527 if (!iRedrawSegments.Count()) |
|
528 return; |
|
529 |
|
530 //Count the number of non-redraw segs |
|
531 TInt nonRedrawSegCount = 0; |
|
532 for (TInt i = iRedrawSegments.Count()-1; i >= 0; --i) |
|
533 { |
|
534 CRedrawSegment* segment = iRedrawSegments[i]; |
|
535 if (segment->iRedrawSegmentType == ESegmentTypeNonRedraw) |
|
536 { |
|
537 ++nonRedrawSegCount; |
|
538 } |
|
539 } |
|
540 |
|
541 TBool callInvalidate = EFalse; |
|
542 |
|
543 //To prevent the number of non redraw segements to grow indefinitely, |
|
544 //delete the oldest if their number exceeds KNonRedrawSegMaxLimit |
|
545 if (nonRedrawSegCount > KNonRedrawSegMaxLimit) |
|
546 { |
|
547 TInt keep = KNonRedrawSegThreshold; // keep this many, the most recent ones |
|
548 for (TInt i = iRedrawSegments.Count()-1; i >= 0; --i) |
|
549 { |
|
550 CRedrawSegment* segment = iRedrawSegments[i]; |
|
551 if (segment->iRedrawSegmentType == ESegmentTypeNonRedraw) |
|
552 { |
|
553 if (keep-- > 0) |
|
554 { |
|
555 continue; |
|
556 } |
|
557 else if (segment!=iCurrentSegment) //never delete the current segment |
|
558 { |
|
559 callInvalidate = ETrue; |
|
560 iRedrawSegments.Remove(i); |
|
561 delete segment; |
|
562 } |
|
563 } |
|
564 } |
|
565 } |
|
566 |
|
567 if(iCurrentSegment && iCurrentSegment->iRedrawSegmentType == ESegmentTypeNonRedraw) |
|
568 { |
|
569 // If the current segment is an old non-redraw segment, try to get rid of it |
|
570 TTime now; |
|
571 now.UniversalTime(); |
|
572 TTimeIntervalMicroSeconds age = now.MicroSecondsFrom(iCurrentSegment->iCreationTime); |
|
573 if (age > iNonRedrawAgeLimit) |
|
574 { |
|
575 // First, find any even older non redraw segments and discard them. |
|
576 for (TInt seg = iRedrawSegments.Count() - 2; seg >= 0; --seg) |
|
577 { |
|
578 CRedrawSegment * segment = iRedrawSegments[seg]; |
|
579 if (segment->iRedrawSegmentType == ESegmentTypeNonRedraw) |
|
580 { |
|
581 age = now.MicroSecondsFrom(segment->iCreationTime); |
|
582 if ((age > iNonRedrawAgeLimit * 2) && (segment!=iCurrentSegment)) |
|
583 { |
|
584 iRedrawSegments.Remove(seg); |
|
585 delete segment; |
|
586 } |
|
587 } |
|
588 } |
|
589 |
|
590 // Then force the creation of a new segment, so that the current one can be allowed to age and eventually vanish. |
|
591 iCurrentSegment->iCreationTime = now; |
|
592 iCurrentSegment = NULL; |
|
593 callInvalidate = ETrue; |
|
594 } |
|
595 } |
|
596 |
|
597 if(callInvalidate) |
|
598 Invalidate(); // Invalidate the window so that a complete redraw should occur |
|
599 } |
|
600 |
|
601 /** |
|
602 Obtains a region from the redraw store, intersects it with the global redraw region which |
|
603 we have been asked to draw to, and returns the intersection. |
|
604 */ |
|
605 const TRegion * CWsRedrawMsgWindow::ReadRegion(const TInt aRegionNum) |
|
606 { |
|
607 // Catch anyone calling this without first deciding where they want to draw |
|
608 WS_ASSERT_DEBUG(iWsWin->ScheduledRegion(), EWsPanicScheduledRedraw); |
|
609 |
|
610 // We are drawing to the global region, and we have a region in the redraw store to clip to. |
|
611 // We want the intersection of these to actually draw to. |
|
612 // Andy - this allocates memory during drawing, which is bad in OOM conditions. |
|
613 iLocalRedrawRegion.Copy(iRedrawSegments[aRegionNum]->iRegion); |
|
614 iLocalRedrawRegion.Offset(WsWin()->Origin()); |
|
615 iLocalRedrawRegion.Intersect(*iGlobalRedrawRegion); |
|
616 iLocalRedrawRegion.Tidy(); |
|
617 |
|
618 // If the resulting region is empty there is no point drawing its corresponding commands |
|
619 if (iLocalRedrawRegion.IsEmpty()) |
|
620 return NULL; |
|
621 else |
|
622 return &iLocalRedrawRegion; |
|
623 } |
|
624 |
|
625 TInt CWsRedrawMsgWindow::SubtractRectFromSegmentArray(const TRect& aRect) |
|
626 { |
|
627 TInt numOfRegionsRemoved =0; |
|
628 for (TInt regionNum = iRedrawSegments.Count() - 1; regionNum >= 0; --regionNum) |
|
629 { |
|
630 if (iRedrawSegments[regionNum]->iRedrawSegmentType != ESegmentTypePendingRedraw) |
|
631 { |
|
632 RWsRegion& region = iRedrawSegments[regionNum]->iRegion; |
|
633 region.SubRect(aRect); |
|
634 if (region.CheckError()) |
|
635 { |
|
636 // Ouch. Drop the now broken segment and ask for a full redraw |
|
637 // Andy - This is an error condition and needs to check for infinite loops |
|
638 delete iRedrawSegments[regionNum]; |
|
639 iRedrawSegments.Remove(regionNum); |
|
640 numOfRegionsRemoved++; |
|
641 Invalidate(); |
|
642 } |
|
643 else |
|
644 { |
|
645 // check if region has zero uncovered rectangles left |
|
646 if (region.IsEmpty()) |
|
647 { // delete draw commands, release bitmaps and fonts |
|
648 delete iRedrawSegments[regionNum]; |
|
649 iRedrawSegments.Remove(regionNum); |
|
650 numOfRegionsRemoved++; |
|
651 } |
|
652 else |
|
653 { |
|
654 if (region.Count() > KRegionCompressThreshold) |
|
655 { // tidy up the rectangles |
|
656 region.Tidy(); |
|
657 } |
|
658 } |
|
659 } |
|
660 } |
|
661 } |
|
662 return numOfRegionsRemoved; |
|
663 } |
|
664 |
|
665 /*------------------------------------------------------------------------------ |
|
666 Description: Clears out the command buffer if aError indicates an |
|
667 error has occurred whilst storing commands. |
|
668 -----------------------------------------------------------------------------*/ |
|
669 void CWsRedrawMsgWindow::DiscardStoredCommandsIfError(TInt aError) |
|
670 { |
|
671 if (aError != KErrNone) |
|
672 { |
|
673 // Discard stored commands by clearing out the command buffer |
|
674 DiscardStoredCommands(); |
|
675 |
|
676 if (!(iFlags&ENoRepeatRedraw)) |
|
677 Invalidate(); |
|
678 iFlags |= ENoRepeatRedraw; |
|
679 } |
|
680 } |
|
681 |
|
682 /*------------------------------------------------------------------------------ |
|
683 Description: If the graphics context has changed and we are currently storing |
|
684 commands, store the data given by aCmdData. |
|
685 |
|
686 -----------------------------------------------------------------------------*/ |
|
687 TBool CWsRedrawMsgWindow::DrawCommand(CWsGc* aGc,const TAny *aCmdData) |
|
688 { |
|
689 // Store commands, clearing out buffer if error occurs. |
|
690 TRAPD(err,StoreDrawCommandL(aGc,aCmdData)); |
|
691 DiscardStoredCommandsIfError(err); |
|
692 return EFalse; |
|
693 } |
|
694 |
|
695 void CWsRedrawMsgWindow::GcAttributeChange(CWsGc* aGc,const TAny *aCmdData) |
|
696 { |
|
697 if (iLastDrawGc == aGc && iCurrentSegment) |
|
698 { |
|
699 TInt err = KErrNone; |
|
700 if (IsWsFontOperation(CWsClient::iCurrentCommand.iOpcode)) |
|
701 { |
|
702 TWsGcCmdUnion pData; |
|
703 pData.any=aCmdData; |
|
704 TRAP(err,AddWsFontL(*pData.UInt)); |
|
705 } |
|
706 if (KErrNone == err) |
|
707 TRAP(err,AppendCommandL(aCmdData)); |
|
708 DiscardStoredCommandsIfError(err); |
|
709 } |
|
710 |
|
711 // INC135845: |
|
712 // Retain the bitmap handle for the lifetime of the redraw store |
|
713 // If the client destroys it, we will still have a reference to it |
|
714 if (iCurrentSegment && CWsClient::iCurrentCommand.iOpcode == EWsGcOpUseBrushPattern) |
|
715 { |
|
716 TInt err = KErrNone; |
|
717 TWsGcCmdUnion pData; |
|
718 pData.any=aCmdData; |
|
719 TRAP(err, AddFbsBitmapsL(*pData.handle, 0)); |
|
720 DiscardStoredCommandsIfError(err); |
|
721 } |
|
722 } |
|
723 |
|
724 void CWsRedrawMsgWindow::GcDeactivate(CWsGc* aGc) |
|
725 { |
|
726 if (iLastDrawGc==aGc) |
|
727 iLastDrawGc=NULL; |
|
728 } |
|
729 |
|
730 inline TBool CWsRedrawMsgWindow::IsFbsBitmapOperation(TInt aOpCode) const |
|
731 { |
|
732 WS_ASSERT_DEBUG((EWsGcOpGdiBlt3==EWsGcOpGdiBlt2+1)&&(EWsGcOpGdiBltMasked==EWsGcOpGdiBlt3+1) |
|
733 &&(EWsGcOpDrawBitmap2==EWsGcOpDrawBitmap+1)&&(EWsGcOpDrawBitmap3==EWsGcOpDrawBitmap2+1)&&(EWsGcOpDrawBitmapMasked==EWsGcOpDrawBitmap3+1),EWsPanicBitmapOpcodeInvalid); |
|
734 return (aOpCode>=EWsGcOpGdiBlt2&&aOpCode<=EWsGcOpGdiBltMasked)||(aOpCode>=EWsGcOpDrawBitmap&&aOpCode<=EWsGcOpDrawBitmapMasked)||(aOpCode==EWsGcOpGdiAlphaBlendBitmaps); |
|
735 } |
|
736 |
|
737 inline TBool CWsRedrawMsgWindow::IsWsBitmapOperation(TInt aOpCode) const |
|
738 { |
|
739 WS_ASSERT_DEBUG((EWsGcOpGdiBlt3==EWsGcOpGdiBlt2+1)&&(EWsGcOpGdiBltMasked==EWsGcOpGdiBlt3+1) |
|
740 &&(EWsGcOpDrawBitmap2==EWsGcOpDrawBitmap+1)&&(EWsGcOpDrawBitmap3==EWsGcOpDrawBitmap2+1)&&(EWsGcOpDrawBitmapMasked==EWsGcOpDrawBitmap3+1),EWsPanicBitmapOpcodeInvalid); |
|
741 return (aOpCode>=EWsGcOpGdiWsBlt2&&aOpCode<=EWsGcOpGdiWsBltMasked)||(aOpCode==EWsGcOpGdiWsAlphaBlendBitmaps)||(aOpCode==EWsGcOpWsDrawBitmapMasked); |
|
742 } |
|
743 |
|
744 inline TBool CWsRedrawMsgWindow::IsWsFontOperation(TInt aOpCode) const |
|
745 { |
|
746 return aOpCode==EWsGcOpUseFont; |
|
747 } |
|
748 |
|
749 inline TBool CWsRedrawMsgWindow::IsDrawWsGraphicOperation(TInt aOpCode) const |
|
750 { |
|
751 return (aOpCode == EWsGcOpDrawWsGraphic) || (aOpCode == EWsGcOpDrawWsGraphicPtr); |
|
752 } |
|
753 |
|
754 void CWsRedrawMsgWindow::ReplaceAndAppendCommandL(TInt aOpcode,const TAny* aCmdData) |
|
755 { |
|
756 const TInt KCharWidthInBytes = 2; // # of bytes for a Unicode character |
|
757 CWsClient* owner=iWsWin->WsOwner(); |
|
758 WS_ASSERT_DEBUG(owner,EWsPanicDrawCommandsNullSession); |
|
759 |
|
760 // aCmdData doesn't contain data, it should be retrieved from client space using remote read |
|
761 TWsGcCmdUnion cmd; |
|
762 cmd.any=aCmdData; |
|
763 TUint16 newOpcode=EWsGcOpDrawText; |
|
764 TInt strLen=0; |
|
765 switch (aOpcode) |
|
766 { |
|
767 case EWsGcOpDrawTextPtr: |
|
768 newOpcode=EWsGcOpDrawText; |
|
769 strLen=cmd.DrawText->length; |
|
770 break; |
|
771 case EWsGcOpDrawTextVerticalPtr: |
|
772 newOpcode=EWsGcOpDrawTextVertical; |
|
773 strLen=cmd.DrawTextVertical->length; |
|
774 break; |
|
775 case EWsGcOpDrawBoxTextPtr: |
|
776 newOpcode=EWsGcOpDrawBoxText; |
|
777 strLen=cmd.BoxText->length; |
|
778 break; |
|
779 case EWsGcOpDrawBoxTextVerticalPtr: |
|
780 newOpcode=EWsGcOpDrawBoxTextVertical; |
|
781 strLen=cmd.DrawBoxTextVertical->length; |
|
782 break; |
|
783 } |
|
784 TInt strSize = strLen * KCharWidthInBytes; |
|
785 TInt oldCmdLen=CWsClient::iCurrentCommand.iCmdLength; |
|
786 TInt newCmdLen=sizeof(TWsCmdHeaderBase)+oldCmdLen+strSize; |
|
787 // resize buffer |
|
788 ExpandCommandBufferL(newCmdLen); |
|
789 // update current command to reflect the new command and data |
|
790 CWsClient::iCurrentCommand.iOpcode=newOpcode; |
|
791 CWsClient::iCurrentCommand.iOpcode|=EWsGcOpFlagDrawOp; |
|
792 CWsClient::iCurrentCommand.iCmdLength=(TInt16)(oldCmdLen+strSize); |
|
793 // write command header |
|
794 CommandBufferWrite(&CWsClient::iCurrentCommand, sizeof(TWsCmdHeaderBase)); |
|
795 // write command |
|
796 CommandBufferWrite(aCmdData, oldCmdLen); |
|
797 |
|
798 // remote read |
|
799 TBuf<KReadBufferMaxLen> buf; |
|
800 TInt len=KReadBufferMaxLen; |
|
801 TInt bufOffset=0; |
|
802 TInt toGo=strLen; |
|
803 while(toGo>0) |
|
804 { |
|
805 if (len>toGo) |
|
806 len=toGo; |
|
807 owner->RemoteRead(buf,bufOffset); |
|
808 CommandBufferWrite(buf.Ptr(), len * KCharWidthInBytes); |
|
809 bufOffset+=len; |
|
810 toGo-=len; |
|
811 } |
|
812 } |
|
813 |
|
814 /*------------------------------------------------------------------------------ |
|
815 Description: Stores drawing related commands into the command buffer |
|
816 -----------------------------------------------------------------------------*/ |
|
817 void CWsRedrawMsgWindow::StoreDrawCommandL(CWsGc* aGc,const TAny *aCmdData) |
|
818 { |
|
819 TWsGcOpcodes currentOpcode = static_cast<TWsGcOpcodes>(CWsClient::iCurrentCommand.iOpcode); |
|
820 |
|
821 // If we get an extra command after the redraw has finished then redraw strategy needs to know |
|
822 if (!InRedraw()) |
|
823 { |
|
824 #ifdef __WINS__ |
|
825 TBool isDrawingCommand = (currentOpcode != EWsGcOpSegmentedDrawPolygonData) && (currentOpcode != EWsGcOpDrawSegmentedPolygon); |
|
826 |
|
827 if( CWsClient::DebugEnforceRedrawCallingConvention() && isDrawingCommand) |
|
828 CWsClient::PanicCurrentClient(EWservPanicWindowBeginRedrawNotCalled); |
|
829 #endif |
|
830 HandleNonRedrawCommand(currentOpcode); |
|
831 } |
|
832 |
|
833 // If there is no current segment then we have discarded it at some point |
|
834 // since beginning this redraw. |
|
835 if (iCurrentSegment) |
|
836 { |
|
837 TWsGcCmdUnion pData; |
|
838 pData.any = aCmdData; |
|
839 if (IsFbsBitmapOperation(currentOpcode)) |
|
840 { |
|
841 TInt maskHandle = 0; |
|
842 TInt handle = aGc->FbsBitmapHandle(currentOpcode, pData, maskHandle); |
|
843 AddFbsBitmapsL(handle, maskHandle); |
|
844 } |
|
845 else if (IsWsBitmapOperation(currentOpcode)) |
|
846 { |
|
847 TInt maskHandle = 0; |
|
848 TInt handle = aGc->WsBitmapHandle(currentOpcode, pData, maskHandle); |
|
849 AddWsBitmapsL(handle, maskHandle); |
|
850 } |
|
851 else if (IsDrawWsGraphicOperation(currentOpcode)) |
|
852 { |
|
853 TGraphicDrawerId drawerId; |
|
854 drawerId.iId = pData.WsGraphic->iId; |
|
855 drawerId.iIsUid = (pData.WsGraphic->iFlags & EWsGraphicIdUid); |
|
856 iCurrentSegment->AddDrawerL(drawerId); |
|
857 } |
|
858 |
|
859 // If the graphics context has changed since last time store the new graphics |
|
860 // context attributes. |
|
861 if (aGc != iLastDrawGc) |
|
862 { |
|
863 StoreAllGcAttributesL(aGc); |
|
864 iLastDrawGc = aGc; |
|
865 } |
|
866 |
|
867 // For operation which requires remote read from client space, we must retrieve that data and store |
|
868 // it in command buffer at server side and change opcode if necessary e.g EWsGcOpDrawTextPtr to EWsGcOpDrawText |
|
869 // to avoid remote read during DoDrawing operation |
|
870 if (IsRemoteReadRequired(currentOpcode)) |
|
871 ReplaceAndAppendCommandL(currentOpcode,aCmdData); |
|
872 else |
|
873 // Append the command data to the command buffer |
|
874 AppendCommandL(aCmdData, EWsGcOpFlagDrawOp); |
|
875 } |
|
876 } |
|
877 |
|
878 /*------------------------------------------------------------------------------ |
|
879 Description: Stores given drawing command data into the command buffer. |
|
880 -----------------------------------------------------------------------------*/ |
|
881 void CWsRedrawMsgWindow::AppendCommandL(const TAny* aCmdData, const TUint16 aOpcodeFlags) |
|
882 { |
|
883 if (CWsClient::iCurrentCommand.iOpcode == EWsGcOpSetClippingRegion) |
|
884 { |
|
885 // The client is defining a clipping region |
|
886 |
|
887 // make room for the header |
|
888 ExpandCommandBufferL(sizeof(TWsCmdHeaderBase)); |
|
889 |
|
890 // Externalize the clipping region data from position after the header |
|
891 RBufWriteStream bufWriteStream; |
|
892 bufWriteStream.Open(*CurrentDrawCommandBuffer(), CurrentCommandBufferWritePos() + sizeof(TWsCmdHeaderBase)); |
|
893 CleanupClosePushL(bufWriteStream); |
|
894 TInt dataLen = iLastDrawGc->ExternalizeClippingRegionL(bufWriteStream); |
|
895 |
|
896 // Setup the clipping region data header |
|
897 CWsClient::iCurrentCommand.iOpcode = EWsStoreClippingRegion; |
|
898 CWsClient::iCurrentCommand.iCmdLength = REINTERPRET_CAST(TInt16&,dataLen); |
|
899 |
|
900 // Store command header for clipping region data at current write position |
|
901 CommandBufferWrite(&CWsClient::iCurrentCommand,sizeof(TWsCmdHeaderBase)); |
|
902 |
|
903 // Update write position for command data |
|
904 iCurrentSegment->iCurrentCommandBufferWritePos += dataLen; |
|
905 |
|
906 CleanupStack::PopAndDestroy(&bufWriteStream); |
|
907 } |
|
908 else |
|
909 { |
|
910 TUint16 opcode = CWsClient::iCurrentCommand.iOpcode; |
|
911 CWsClient::iCurrentCommand.iOpcode |= aOpcodeFlags; |
|
912 |
|
913 // ensure room in command buffer |
|
914 ExpandCommandBufferL(sizeof(TWsCmdHeaderBase) + CWsClient::iCurrentCommand.iCmdLength); |
|
915 |
|
916 // Store command header to current position |
|
917 CommandBufferWrite(&CWsClient::iCurrentCommand, sizeof(TWsCmdHeaderBase)); |
|
918 |
|
919 // If there's command data (other than header), store it |
|
920 if (CWsClient::iCurrentCommand.iCmdLength > 0) |
|
921 { |
|
922 CommandBufferWrite(aCmdData, CWsClient::iCurrentCommand.iCmdLength); |
|
923 } |
|
924 |
|
925 CWsClient::iCurrentCommand.iOpcode = opcode; |
|
926 } |
|
927 } |
|
928 |
|
929 |
|
930 /*------------------------------------------------------------------------------ |
|
931 Description: Stores graphics context information into the command buffer |
|
932 from the current write position. |
|
933 -----------------------------------------------------------------------------*/ |
|
934 void CWsRedrawMsgWindow::StoreAllGcAttributesL(CWsGc* aGc) |
|
935 { |
|
936 // In order for the externalize below to work correctly from |
|
937 // a non-zero position we have to create the header placeholder |
|
938 ExpandCommandBufferL(sizeof(TWsCmdHeaderBase)); |
|
939 |
|
940 // Externalise GC attribute data. We do this before writing the |
|
941 // header as we do not know the size of the data yet and it is |
|
942 // part of the header. |
|
943 TInt numOfBytesAdded = aGc->ExternalizeL(*CurrentDrawCommandBuffer(), |
|
944 CurrentCommandBufferWritePos() + sizeof(TWsCmdHeaderBase)); |
|
945 |
|
946 // Setup the header |
|
947 TWsCmdHeaderBase cmdHeader; |
|
948 cmdHeader.iCmdLength = (TInt16) numOfBytesAdded; // as calculated above |
|
949 cmdHeader.iOpcode = (TInt16) EWsStoreAllGcAttributes; |
|
950 |
|
951 // Store the header for the GC data into the space we created |
|
952 CommandBufferWrite(&cmdHeader, sizeof(TWsCmdHeaderBase)); |
|
953 |
|
954 // Update write position for command data |
|
955 iCurrentSegment->iCurrentCommandBufferWritePos += numOfBytesAdded; |
|
956 } |
|
957 |
|
958 /*------------------------------------------------------------------------------ |
|
959 Description: Loops through the whole of the current command buffer, processing |
|
960 each in turn. |
|
961 -----------------------------------------------------------------------------*/ |
|
962 void CWsRedrawMsgWindow::DrawCommandsL() |
|
963 { |
|
964 LOG_WINDOW_REDRAW_START(WsWin(), iGlobalRedrawRegion); |
|
965 WS_ASSERT_DEBUG(iMemoryLock > 0, EWsPanicMemoryLock); |
|
966 static TBuf8<EClientBufferMaxSize> buf; |
|
967 TInt regionCount = iRedrawSegments.Count(); |
|
968 |
|
969 for (TInt regionNum = 0; regionNum < regionCount; ++regionNum) |
|
970 { |
|
971 CRedrawSegment* segment = iRedrawSegments[regionNum]; |
|
972 LOG_REDRAW_SEGMENT(regionNum, segment->iRedrawSegmentType); |
|
973 if (segment->iRedrawSegmentType == ESegmentTypePendingRedraw) |
|
974 continue; |
|
975 |
|
976 // The amount of commands we process is given by the value of the |
|
977 // current write position rather than the size of the command buffer. |
|
978 // Note: the write position is incremented as each command is stored and |
|
979 // will typically be less than the buffer size. |
|
980 const TInt length = segment->iCurrentCommandBufferWritePos; |
|
981 |
|
982 // need to draw this region? |
|
983 const TRegion * localDrawRegion = 0; |
|
984 if (length) |
|
985 localDrawRegion = ReadRegion(regionNum); |
|
986 if (localDrawRegion) |
|
987 { |
|
988 CPlaybackGc::Instance()->SetTargetRegion(localDrawRegion); |
|
989 LOG_REDRAW_SEGMENT_REGION(localDrawRegion) |
|
990 |
|
991 TWsCmdHeaderBase header; |
|
992 TInt pos = 0; // Set to first command position in buffer |
|
993 CBufSeg* drawCmdBuffer = segment->iDrawCommands; |
|
994 |
|
995 #ifdef _DEBUG |
|
996 // Read the first command header. The associated opcode must always be |
|
997 // EWsStoreAllGcAttributes as this is always the first stored item. |
|
998 drawCmdBuffer->Read(pos,&header,sizeof(TWsCmdHeaderBase)); |
|
999 WS_ASSERT_DEBUG(header.iOpcode == EWsStoreAllGcAttributes, EWsPanicDrawCommandsBufferCorrupt); |
|
1000 #endif |
|
1001 |
|
1002 // Read through remaining commands |
|
1003 while (pos < length) |
|
1004 { |
|
1005 // Get header of command |
|
1006 drawCmdBuffer->Read(pos, &header, sizeof(TWsCmdHeaderBase)); |
|
1007 pos += sizeof(TWsCmdHeaderBase); |
|
1008 |
|
1009 switch(header.iOpcode) |
|
1010 { |
|
1011 case EWsStoreAllGcAttributes: |
|
1012 { |
|
1013 // Header indicates command encapsulates gc data |
|
1014 CPlaybackGc::Instance()->Reset(); |
|
1015 |
|
1016 // Read gc data |
|
1017 CPlaybackGc::Instance()->InternalizeL(*drawCmdBuffer,pos); |
|
1018 |
|
1019 } |
|
1020 break; |
|
1021 case EWsStoreClippingRegion: |
|
1022 { |
|
1023 // Clipping region data read in from current position via stream |
|
1024 RBufReadStream bufReadStream; |
|
1025 bufReadStream.Open(*drawCmdBuffer,pos); |
|
1026 CleanupClosePushL(bufReadStream); |
|
1027 CPlaybackGc::Instance()->InternalizeClippingRegionL(bufReadStream); |
|
1028 CleanupStack::PopAndDestroy(&bufReadStream); |
|
1029 } |
|
1030 break; |
|
1031 default: |
|
1032 { |
|
1033 // Another type of command. Read it. |
|
1034 CWsClient::iCurrentCommand.iCmdLength = header.iCmdLength; |
|
1035 drawCmdBuffer->Read(pos,buf,header.iCmdLength); |
|
1036 |
|
1037 TInt opcode = header.iOpcode; |
|
1038 |
|
1039 // Drawing command? |
|
1040 if (opcode & EWsGcOpFlagDrawOp) |
|
1041 { |
|
1042 opcode &= ~EWsGcOpFlagDrawOp; |
|
1043 } |
|
1044 if (opcode > -1) |
|
1045 { |
|
1046 LOG_PLAYBACK_GC_COMMAND(opcode, buf.Ptr()) |
|
1047 CPlaybackGc::Instance()->CommandL(static_cast<TWsGcOpcodes>(opcode),buf); |
|
1048 } |
|
1049 } |
|
1050 break; |
|
1051 } |
|
1052 pos += header.iCmdLength; // Move on, header indicates length |
|
1053 } |
|
1054 DEBUGOSB // per-redraw-segment debug osb updates |
|
1055 } |
|
1056 } |
|
1057 LOG_WINDOW_REDRAW_END(WsWin()); |
|
1058 } |
|
1059 |
|
1060 /*------------------------------------------------------------------------------ |
|
1061 Description: Called when the currently stored graphics commands |
|
1062 are no longer required. |
|
1063 -----------------------------------------------------------------------------*/ |
|
1064 void CWsRedrawMsgWindow::DiscardStoredCommands() |
|
1065 { |
|
1066 iCurrentSegment = NULL; |
|
1067 if (iRedrawSegments.Count() > 0) |
|
1068 { |
|
1069 // First of all, if we have any redraws pending, update the screen with |
|
1070 // whatever commands we have before we throw them away: |
|
1071 if (iFlags & EPendingScheduledDraw) |
|
1072 { |
|
1073 Screen()->DoRedrawNow(); |
|
1074 } |
|
1075 |
|
1076 // for all regions or just Partial Redraw regions > index 0: delete bitmaps and draw commands |
|
1077 iRedrawSegments.ResetAndDestroy(); |
|
1078 |
|
1079 iLastDrawGc = NULL; |
|
1080 } |
|
1081 } |
|
1082 |
|
1083 void CWsRedrawMsgWindow::CreateNewSegmentL(const TRect& aRect, TRedrawSegmentType aNewRedrawRegionType) |
|
1084 { |
|
1085 CWsRedrawMsgWindow::CRedrawSegment* newRegion = CWsRedrawMsgWindow::CRedrawSegment::NewLC(aRect, aNewRedrawRegionType); |
|
1086 |
|
1087 iRedrawSegments.AppendL(newRegion); |
|
1088 iCurrentSegment = newRegion; |
|
1089 CleanupStack::Pop(newRegion); |
|
1090 |
|
1091 // Set iLastDrawGc to NULL. This will cause all GC attributes to be stored |
|
1092 // in redraw store when the window receives the next command |
|
1093 iLastDrawGc = NULL; |
|
1094 } |
|
1095 |
|
1096 static TInt FindBitmapByHandle(const TInt* aKey, const CFbsBitmap& aBitmap) |
|
1097 { // compare handles |
|
1098 return *aKey - aBitmap.Handle(); |
|
1099 } |
|
1100 |
|
1101 static TInt InsertBitmapByHandle(const CFbsBitmap& aFirst, const CFbsBitmap& aSecond) |
|
1102 { |
|
1103 return aFirst.Handle() - aSecond.Handle(); |
|
1104 } |
|
1105 |
|
1106 void CWsRedrawMsgWindow::AddFbsBitmapsL(TInt aHandle, TInt aMaskHandle) |
|
1107 { |
|
1108 iCurrentSegment->AddFbsBitmapL(aHandle, this); |
|
1109 if (aMaskHandle) |
|
1110 { |
|
1111 iCurrentSegment->AddFbsBitmapL(aMaskHandle, this); |
|
1112 } |
|
1113 } |
|
1114 |
|
1115 void CWsRedrawMsgWindow::CRedrawSegment::AddFbsBitmapL(TInt aHandle, CWsRedrawMsgWindow* aWindow) |
|
1116 { |
|
1117 if (iFbsBitmapArray.FindInOrder(aHandle, &FindBitmapByHandle) >= 0) |
|
1118 { |
|
1119 // Bitmap already in the store |
|
1120 return; |
|
1121 } |
|
1122 |
|
1123 CFbsBitmap* bitmap = new(ELeave) CFbsBitmap; |
|
1124 CleanupStack::PushL(bitmap); |
|
1125 if (bitmap->Duplicate(aHandle)!=KErrNone) |
|
1126 aWindow->OwnerPanic(EWservPanicBitmap); |
|
1127 iFbsBitmapArray.InsertInOrderL(bitmap, TLinearOrder<CFbsBitmap>(InsertBitmapByHandle)); |
|
1128 CleanupStack::Pop(bitmap); |
|
1129 } |
|
1130 |
|
1131 void CWsRedrawMsgWindow::AddWsBitmapsL(TInt aHandle, TInt aMaskHandle) |
|
1132 { |
|
1133 if (iWsWin->WsOwner() == NULL) |
|
1134 Panic(EWsPanicDrawCommandsInvalidState); |
|
1135 DWsBitmap * bmp = static_cast<DWsBitmap*>(iWsWin->WsOwner()->HandleToObj(aHandle, WS_HANDLE_BITMAP)); |
|
1136 if (!bmp) |
|
1137 OwnerPanic(EWservPanicBitmap); |
|
1138 iCurrentSegment->AddWsBitmapL(bmp); |
|
1139 if (aMaskHandle) |
|
1140 { |
|
1141 bmp = static_cast<DWsBitmap*>(iWsWin->WsOwner()->HandleToObj(aMaskHandle, WS_HANDLE_BITMAP)); |
|
1142 if (!bmp) |
|
1143 OwnerPanic(EWservPanicBitmap); |
|
1144 iCurrentSegment->AddWsBitmapL(bmp); |
|
1145 } |
|
1146 } |
|
1147 |
|
1148 void CWsRedrawMsgWindow::CRedrawSegment::AddWsBitmapL(DWsBitmap* bitmap) |
|
1149 { |
|
1150 iWsBitmapArray.AppendL(bitmap); |
|
1151 bitmap->IncRefCount(); |
|
1152 } |
|
1153 |
|
1154 void CWsRedrawMsgWindow::AddWsFontL(TInt aHandle) |
|
1155 { |
|
1156 if (iWsWin->WsOwner()==NULL) |
|
1157 Panic(EWsPanicDrawCommandsInvalidState); |
|
1158 TDblQueIter<CWsFbsFont> iter(CWsFontCache::List()); |
|
1159 CWsFbsFont* font=NULL; |
|
1160 while((font=iter++)!=NULL) |
|
1161 { |
|
1162 if (font->Handle()==aHandle) |
|
1163 break; |
|
1164 } |
|
1165 if (font) |
|
1166 { |
|
1167 iCurrentSegment->iWsFontArray.AppendL(font); |
|
1168 ++(font->iCount); |
|
1169 } |
|
1170 } |
|
1171 |
|
1172 void CWsRedrawMsgWindow::CRedrawSegment::AddDrawerL(TGraphicDrawerId aDrawerId) |
|
1173 { |
|
1174 TInt error = iDrawerArray.InsertInOrder(aDrawerId, TLinearOrder<TGraphicDrawerId>(TGraphicDrawerId::Compare)); |
|
1175 if (error != KErrAlreadyExists && error != KErrNone) |
|
1176 { |
|
1177 User::Leave(error); |
|
1178 } |
|
1179 } |
|
1180 |
|
1181 TBool CWsRedrawMsgWindow::CRedrawSegment::ContainsDrawers(const TArray<TGraphicDrawerId>& aDrawers,const TRegion& aRegion) const |
|
1182 { |
|
1183 TBool result = EFalse; |
|
1184 if (iDrawerArray.Count() > 0) |
|
1185 { |
|
1186 STACK_REGION tempRegion; |
|
1187 tempRegion.Intersection(iRegion, aRegion); |
|
1188 if (tempRegion.CheckError() || (tempRegion.Count() > 0) ) |
|
1189 { // regions do intersect, (presumed if region had an error); so check for a matching Id |
|
1190 const TInt drawersCount = aDrawers.Count(); |
|
1191 for (TInt idx = 0; idx < drawersCount; ++idx) |
|
1192 { // (iDrawerArray is kept sorted) |
|
1193 if (KErrNotFound != iDrawerArray.FindInOrder(aDrawers[idx], TLinearOrder<TGraphicDrawerId>(TGraphicDrawerId::Compare))) |
|
1194 { |
|
1195 result = ETrue; |
|
1196 break; |
|
1197 } |
|
1198 |
|
1199 const TInt count = iDrawerArray.Count(); |
|
1200 for(TInt i = 0; i < count; i++) |
|
1201 { |
|
1202 const CWsGraphicDrawer* drawer = CWsTop::WindowServer()->ResolveGraphic(iDrawerArray[i]); |
|
1203 if(drawer && drawer->Contains(aDrawers)) |
|
1204 { |
|
1205 result = ETrue; |
|
1206 break; |
|
1207 } |
|
1208 } |
|
1209 } |
|
1210 } |
|
1211 tempRegion.Close(); |
|
1212 } |
|
1213 return result; |
|
1214 } |
|
1215 |
|
1216 inline TBool CWsRedrawMsgWindow::NoBuffer() const |
|
1217 { |
|
1218 return (iRedrawSegments.Count() == 0); |
|
1219 } |
|
1220 |
|
1221 void CWsRedrawMsgWindow::ClientExposing() |
|
1222 { |
|
1223 Invalidate(); |
|
1224 } |
|
1225 |
|
1226 /*------------------------------------------------------------------------------ |
|
1227 Description: If a complete set of drawing commands have been stored |
|
1228 this method attempts to draw ALL the commands via DrawCommandsL(). |
|
1229 It also draws the window in the background colour if the window is |
|
1230 opaque. |
|
1231 -----------------------------------------------------------------------------*/ |
|
1232 void CWsRedrawMsgWindow::DrawWindow() |
|
1233 { |
|
1234 iFlags &= ~EPendingScheduledDraw; |
|
1235 // This is a happy window - it can draw itself whenever we ask. |
|
1236 if(iFlags&EBackgroundClear) |
|
1237 { |
|
1238 DrawBackgroundColor(iGlobalRedrawRegion); |
|
1239 } |
|
1240 // If valid commands have been stored, draw them. |
|
1241 if (iRedrawSegments.Count() > 0) |
|
1242 { |
|
1243 Lock(); |
|
1244 TRAP_IGNORE(DrawCommandsL()); |
|
1245 Unlock(); |
|
1246 } |
|
1247 } |
|
1248 |
|
1249 void CWsRedrawMsgWindow::RemoveFromRedrawQueueIfEmpty() |
|
1250 { |
|
1251 if (iInvalid.Count()==0) |
|
1252 { |
|
1253 iInvalid.Clear(); // Ensures heap cell is freed, otherwise may be left as an empty cell |
|
1254 iWsWin->WsOwner()->RedrawQueue()->RemoveInvalid(this); |
|
1255 } |
|
1256 } |
|
1257 |
|
1258 TBool CWsRedrawMsgWindow::NeedsRedraw() const |
|
1259 // If iInvalid has an persistant error it will not be reported as needing a redraw, |
|
1260 // this is needed as otherwise cases where validation of a window results |
|
1261 // in iInvalid having an error will get into an endless cycle of redraws. |
|
1262 // The down side of this is that sometimes a window will not be sent a redraw |
|
1263 // message when it needs it, some things can't be perfect! |
|
1264 // |
|
1265 { |
|
1266 if ((!iWsWin->IsVisible()) || iInvalid.IsEmpty()) |
|
1267 return EFalse; |
|
1268 |
|
1269 TRect nextRedrawRect; |
|
1270 return GetRedrawRect(nextRedrawRect); |
|
1271 } |
|
1272 |
|
1273 TBool CWsRedrawMsgWindow::GetRedrawRect(TRect &aRect) const |
|
1274 { |
|
1275 if (iWsWin->ClientSetInvisible()) |
|
1276 return EFalse; |
|
1277 |
|
1278 if(InRedraw()) |
|
1279 { |
|
1280 aRect = iRedrawRect; |
|
1281 return (!aRect.IsEmpty()); |
|
1282 } |
|
1283 else if(iInvalid.CheckError()) |
|
1284 { |
|
1285 if (iFlags & EStoringEntireWindow || iWsWin->VisibleRegion().CheckError()) |
|
1286 { |
|
1287 aRect = iWsWin->AbsRect(); |
|
1288 } |
|
1289 else |
|
1290 { |
|
1291 aRect = iWsWin->VisibleRegion().BoundingRect(); |
|
1292 } |
|
1293 if (!(iFlags & EStoringEntireWindow)) |
|
1294 iWsWin->ClipRectToViewport(aRect); |
|
1295 aRect.Move(-iWsWin->Origin()); |
|
1296 return (!aRect.IsEmpty()); |
|
1297 } |
|
1298 else if(iInvalid.Count()) |
|
1299 { |
|
1300 if (iFlags & EStoringEntireWindow) |
|
1301 { |
|
1302 aRect = iInvalid.BoundingRect(); |
|
1303 } |
|
1304 else |
|
1305 { |
|
1306 RWsRegion region; |
|
1307 region.Copy(iInvalid); |
|
1308 region.Offset(iWsWin->Origin()); |
|
1309 region.Intersect(iWsWin->VisibleRegion()); |
|
1310 if (region.CheckError()) |
|
1311 { |
|
1312 aRect = iInvalid.BoundingRect(); |
|
1313 aRect.Move(iWsWin->Origin()); |
|
1314 } |
|
1315 else |
|
1316 { |
|
1317 aRect = region.BoundingRect(); |
|
1318 } |
|
1319 region.Close(); |
|
1320 iWsWin->ClipRectToViewport(aRect); |
|
1321 aRect.Move(-iWsWin->Origin()); |
|
1322 } |
|
1323 return (!aRect.IsEmpty()); |
|
1324 } |
|
1325 else |
|
1326 { |
|
1327 return EFalse; |
|
1328 } |
|
1329 } |
|
1330 |
|
1331 void CWsRedrawMsgWindow::ClipInvalidRegion(const TRect &aRect) |
|
1332 { |
|
1333 if (iInvalid.Count()>0) |
|
1334 { |
|
1335 iInvalid.ClipRect(aRect); |
|
1336 RemoveFromRedrawQueueIfEmpty(); |
|
1337 } |
|
1338 } |
|
1339 |
|
1340 void CWsRedrawMsgWindow::EndRedraw() |
|
1341 { |
|
1342 ++iCount; |
|
1343 if(!InRedraw()) |
|
1344 OwnerPanic(EWservPanicDrawCommandsInvalidState); |
|
1345 if (iCurrentSegment) |
|
1346 { |
|
1347 iCurrentSegment->iDrawCommands->Compress(); |
|
1348 if (iAtomicity==ENoAtomicity) |
|
1349 { |
|
1350 ScheduleUpdateOfSegment(iCurrentSegment); |
|
1351 } |
|
1352 else if(iAtomicity==ESegment) |
|
1353 { |
|
1354 PromoteLastPendingSegment(); |
|
1355 ScheduleUpdateOfSegment(iCurrentSegment); |
|
1356 } |
|
1357 else if(iAtomicity==EWindow) |
|
1358 { |
|
1359 //only promote all pending segments when there are no invalid regions left in the window. |
|
1360 STACK_REGION regionAwaitingRedraws; |
|
1361 regionAwaitingRedraws.Copy(WsWin()->VisibleRegion()); |
|
1362 regionAwaitingRedraws.Offset(-WsWin()->Origin()); |
|
1363 regionAwaitingRedraws.Intersect(iInvalid); |
|
1364 if(regionAwaitingRedraws.IsEmpty()) |
|
1365 PromoteAndUpdateAllPendingSegments(); |
|
1366 regionAwaitingRedraws.Close(); |
|
1367 } |
|
1368 } |
|
1369 |
|
1370 iCurrentSegment = NULL; |
|
1371 iFlags&=~(ENoRepeatRedraw|EBeginEndRedraw); |
|
1372 } |
|
1373 |
|
1374 void CWsRedrawMsgWindow::ScheduleUpdateOfSegment(CRedrawSegment* aSegment) |
|
1375 { |
|
1376 // Schedule an update of the area of the screen we just drew to: |
|
1377 iFlags |= EPendingScheduledDraw; |
|
1378 if(iWsWin->VisibleRegion().Count() || iWsWin->VisibleRegion().CheckError()) |
|
1379 { |
|
1380 STACK_REGION draw; //### in low memory where VisibleRegion() is intact we can degrade much better than this! |
|
1381 draw.Copy(aSegment->iRegion); |
|
1382 draw.Offset(iWsWin->Origin()); |
|
1383 draw.Intersect(iWsWin->VisibleRegion()); |
|
1384 if(!draw.CheckError()) |
|
1385 Screen()->AddRedrawRegion(draw); |
|
1386 else |
|
1387 Screen()->AddRedrawRegion(iWsWin->VisibleRegion()); |
|
1388 draw.Close(); |
|
1389 } |
|
1390 } |
|
1391 |
|
1392 void CWsRedrawMsgWindow::ValidateRect(const TRect *aRect) |
|
1393 { |
|
1394 if (!WsWin()->BaseParent()) |
|
1395 OwnerPanic(EWservPanicParentDeleted); |
|
1396 if (aRect) |
|
1397 iRedrawRect = *aRect; |
|
1398 if (!iInvalid.IsEmpty()) |
|
1399 { |
|
1400 STACK_REGION validated; |
|
1401 validated.Copy(iInvalid); |
|
1402 if (aRect) |
|
1403 validated.ClipRect(iRedrawRect); |
|
1404 |
|
1405 if (iInvalid.CheckError()) |
|
1406 { |
|
1407 iInvalid.Copy(iWsWin->VisibleRegion()); |
|
1408 iInvalid.Offset(-iWsWin->Origin()); |
|
1409 } |
|
1410 iInvalid.SubRegion(validated); |
|
1411 validated.Close(); |
|
1412 } |
|
1413 RemoveFromRedrawQueueIfEmpty(); |
|
1414 } |
|
1415 |
|
1416 TRgb CWsRedrawMsgWindow::BackColor() const |
|
1417 { |
|
1418 return(iBackColor); |
|
1419 } |
|
1420 |
|
1421 /** |
|
1422 This function used to be quite clever about what it invalidated and what it redrew by copying |
|
1423 rectangles of the screen around. This is a lot less subtle, and makes calling Scroll pretty much |
|
1424 pointless, but it IS functionally correct. |
|
1425 */ |
|
1426 void CWsRedrawMsgWindow::Scroll(const TRect &aClipRect, const TPoint &aOffset,const TRect &aRect) |
|
1427 { |
|
1428 TRect rect = aRect; |
|
1429 rect.Intersection(aClipRect); |
|
1430 Invalidate(&rect); |
|
1431 rect = aRect; |
|
1432 rect.Move(aOffset); |
|
1433 rect.Intersection(aClipRect); |
|
1434 Invalidate(&rect); |
|
1435 } |
|
1436 |
|
1437 void CWsRedrawMsgWindow::ClearRedrawStore(TBool aClearPendingRedraw) |
|
1438 { |
|
1439 if(aClearPendingRedraw && (iFlags & EPendingScheduledDraw)) |
|
1440 iFlags &= ~EPendingScheduledDraw; |
|
1441 |
|
1442 DiscardStoredCommands(); |
|
1443 Invalidate(); |
|
1444 } |
|
1445 |
|
1446 |
|
1447 void CWsRedrawMsgWindow::PrepareForResizeL(const TSize& aSize, TSize& /*aOldSize*/) |
|
1448 { |
|
1449 TBool anyIncreases(EFalse); |
|
1450 if (aSize.iWidth>iWsWin->Size().iWidth||aSize.iHeight>iWsWin->Size().iHeight) |
|
1451 { |
|
1452 anyIncreases = ETrue; |
|
1453 } |
|
1454 |
|
1455 TRect newWinRect(TPoint(0,0),aSize); |
|
1456 iInvalid.ClipRect(newWinRect); |
|
1457 if (anyIncreases) |
|
1458 { |
|
1459 // add new invalid region to iInvalid |
|
1460 iInvalid.AddRect(newWinRect); |
|
1461 QueueRedraw(); |
|
1462 iWsWin->WsOwner()->TriggerRedraw(); |
|
1463 } |
|
1464 } |
|
1465 |
|
1466 void CWsRedrawMsgWindow::Moved() |
|
1467 { |
|
1468 if (!(iFlags & EStoringEntireWindow)) |
|
1469 { |
|
1470 DiscardSegmentsOutsideViewport(); |
|
1471 } |
|
1472 if (iInvalid.Count()) |
|
1473 { |
|
1474 QueueRedraw(); |
|
1475 iWsWin->WsOwner()->TriggerRedraw(); |
|
1476 } |
|
1477 } |
|
1478 |
|
1479 TBool CWsRedrawMsgWindow::Contains(const TArray<TGraphicDrawerId>& aDrawers,const TRegion& aRegion) const |
|
1480 { |
|
1481 if (iRedrawSegments.Count() > 0) |
|
1482 { |
|
1483 // scan redraw store: calls Contains() on every region drawing commands are stored for, |
|
1484 // looking for a DrawWsGraphic command that intersects the aRegion |
|
1485 TBool contains = EFalse; |
|
1486 const TInt regionCount = iRedrawSegments.Count(); |
|
1487 // loop through regions, stops when a match is found |
|
1488 for (TInt regionNum = 0; (regionNum < regionCount) && !contains; ++regionNum) |
|
1489 { |
|
1490 contains = iRedrawSegments[regionNum]->ContainsDrawers(aDrawers, aRegion); |
|
1491 } |
|
1492 return contains; |
|
1493 } |
|
1494 else |
|
1495 { |
|
1496 return CWsWindowRedraw::Contains(aDrawers,aRegion); |
|
1497 } |
|
1498 } |
|
1499 |
|
1500 |
|
1501 void CWsRedrawMsgWindow::SetScope(TScope aScope) |
|
1502 { |
|
1503 if (aScope == EStoreEntireWindow) |
|
1504 { |
|
1505 if (!(iFlags & EStoringEntireWindow)) |
|
1506 { |
|
1507 iFlags |= EStoringEntireWindow; |
|
1508 Invalidate(); |
|
1509 } |
|
1510 } |
|
1511 else |
|
1512 { |
|
1513 if (iFlags & EStoringEntireWindow) |
|
1514 { |
|
1515 iFlags &= ~ EStoringEntireWindow; |
|
1516 DiscardSegmentsOutsideViewport(); |
|
1517 } |
|
1518 } |
|
1519 } |
|
1520 |
|
1521 /** |
|
1522 Removes all segments from the redraw store which are outside the viewport onto the window. |
|
1523 Note that this doesn't clip the regions of those segments which are partly outside, since |
|
1524 this wouldn't actually achieve anything useful. |
|
1525 |
|
1526 This function allocates memory so it is not suitable to run as part of ReleaseMemory. |
|
1527 */ |
|
1528 TBool CWsRedrawMsgWindow::DiscardSegmentsOutsideViewport() |
|
1529 { |
|
1530 TBool discarded = EFalse; |
|
1531 TInt count = iRedrawSegments.Count(); |
|
1532 STACK_REGION viewport; |
|
1533 CliWin()->SetClippedBaseArea(viewport); |
|
1534 viewport.Offset(-iWsWin->Origin()); |
|
1535 STACK_REGION intersect; |
|
1536 for (TInt idx = count - 1; idx >= 0; --idx) |
|
1537 { |
|
1538 CRedrawSegment * segment = iRedrawSegments[idx]; |
|
1539 intersect.Intersection(segment->iRegion, viewport); |
|
1540 if (!intersect.CheckError() && intersect.IsEmpty()) |
|
1541 { |
|
1542 iInvalid.Union(segment->iRegion); |
|
1543 delete segment; |
|
1544 iRedrawSegments.Remove(idx); |
|
1545 if (iCurrentSegment == segment) |
|
1546 iCurrentSegment = NULL; |
|
1547 discarded = ETrue; |
|
1548 } |
|
1549 } |
|
1550 intersect.Close(); |
|
1551 viewport.Close(); |
|
1552 return discarded; |
|
1553 } |
|
1554 |
|
1555 /** |
|
1556 Statements encapsulated in between Lock() and Unlock() is guaranteed to execute in an |
|
1557 atomic way without being interupted by a call to ReleaseMemory from CWsMemoryManager. |
|
1558 Locking will prevent memory belonging to this object to be freed during a |
|
1559 memory alloc/realloc originating from self. |
|
1560 */ |
|
1561 void CWsRedrawMsgWindow::Lock() |
|
1562 { |
|
1563 ++iMemoryLock; |
|
1564 } |
|
1565 |
|
1566 void CWsRedrawMsgWindow::Unlock() |
|
1567 { |
|
1568 --iMemoryLock; |
|
1569 WS_ASSERT_DEBUG(iMemoryLock >= 0, EWsPanicMemoryLock); |
|
1570 } |
|
1571 |
|
1572 TBool CWsRedrawMsgWindow::ReleaseMemory(MWsMemoryRelease::TMemoryReleaseLevel aLevel) |
|
1573 { |
|
1574 //When this function is called, wserv is in the middle of executing something. |
|
1575 //Therefore we can not safely do anything that alters the state of any shared |
|
1576 //resouces (like e.g. CScreenRedraw::iInvalid). |
|
1577 //In addition, we should refrain from anything that might try to allocate memory. |
|
1578 TBool released = EFalse; |
|
1579 //Don't release iRedrawSegments from this win if its currently being rendered, |
|
1580 //is releasing memory or is receiving drawcommands. |
|
1581 if (iMemoryLock == 0 && !iCurrentSegment) |
|
1582 { |
|
1583 Lock(); |
|
1584 switch (aLevel) |
|
1585 { |
|
1586 case MWsMemoryRelease::ELow: |
|
1587 break; |
|
1588 case MWsMemoryRelease::EMedium: |
|
1589 break; |
|
1590 case MWsMemoryRelease::EHigh: |
|
1591 //Only release memory from background windows. |
|
1592 if (iRedrawSegments.Count() > 0 && iWsWin->VisibleRegion().IsEmpty()) |
|
1593 { |
|
1594 ReleaseRedrawSegments(); |
|
1595 released = ETrue; |
|
1596 } |
|
1597 break; |
|
1598 } |
|
1599 Unlock(); |
|
1600 } |
|
1601 return released; |
|
1602 } |
|
1603 |
|
1604 void CWsRedrawMsgWindow::ReleaseRedrawSegments() |
|
1605 { |
|
1606 iLastDrawGc = NULL; |
|
1607 iCurrentSegment = NULL; |
|
1608 iRedrawSegments.ResetAndDestroy(); |
|
1609 |
|
1610 //The call to ResetAndDestroy just freed some memory so it should be |
|
1611 //possible to call Invalidate() now. |
|
1612 Invalidate(); |
|
1613 |
|
1614 //Releasing the same window over and over again could quickly end up in |
|
1615 //a never ending loop with a high-prio client before we find the window |
|
1616 //that has nicked all memory. So call accessed now to prevent that. |
|
1617 iWsWin->Accessed(); |
|
1618 } |
|
1619 |
|
1620 void CWsRedrawMsgWindow::VisibleRegionChange() |
|
1621 { |
|
1622 if (!iFlags & EStoringEntireWindow) |
|
1623 { |
|
1624 DiscardSegmentsOutsideViewport(); |
|
1625 } |
|
1626 if ((!iInvalid.IsEmpty()) && (!iWsWin->VisibleRegion().IsEmpty())) |
|
1627 { |
|
1628 STACK_REGION exposed; |
|
1629 exposed.Copy(iInvalid); |
|
1630 exposed.Offset(iWsWin->Origin()); |
|
1631 exposed.Intersect(iWsWin->VisibleRegion()); |
|
1632 if (!exposed.IsEmpty()) |
|
1633 { |
|
1634 QueueRedraw(); |
|
1635 } |
|
1636 exposed.Close(); |
|
1637 } |
|
1638 } |
|
1639 |
|
1640 TBool CWsRedrawMsgWindow::ReadyToDraw() const |
|
1641 { |
|
1642 //We are only ready to draw when we have a complete segment. |
|
1643 if (iWsWin->HasBeenDrawnToScreen()) |
|
1644 return ETrue; |
|
1645 |
|
1646 if (iRedrawSegments.Count() == 0) |
|
1647 return EFalse; |
|
1648 |
|
1649 if (iRedrawSegments.Count() > 1) |
|
1650 return ETrue; |
|
1651 |
|
1652 if (iRedrawSegments[0]->iRedrawSegmentType == ESegmentTypePendingRedraw) |
|
1653 return EFalse; |
|
1654 |
|
1655 return ETrue; |
|
1656 } |
|
1657 |
|
1658 TInt CWsRedrawMsgWindow::SizeInBytes() const |
|
1659 { |
|
1660 TInt size = sizeof(CWsRedrawMsgWindow); |
|
1661 for(TInt i = iRedrawSegments.Count()-1; i >= 0; i--) |
|
1662 { |
|
1663 size += iRedrawSegments[i]->SizeInBytes(); |
|
1664 } |
|
1665 size += iInvalid.Count() * sizeof(TRect); |
|
1666 size += iLocalRedrawRegion.Count() * sizeof(TRect); |
|
1667 return size; |
|
1668 } |
|
1669 |
|
1670 void CWsRedrawMsgWindow::PromoteLastPendingSegment() |
|
1671 { |
|
1672 if (iRedrawSegments.Count() > 0 && iRedrawSegments[iRedrawSegments.Count() - 1]->iRedrawSegmentType == ESegmentTypePendingRedraw) |
|
1673 { |
|
1674 CRedrawSegment * segment = iRedrawSegments[iRedrawSegments.Count() - 1]; |
|
1675 const TRect * rect = segment->iRegion.RectangleList(); |
|
1676 // when we get here there should only ever be one rectangle in the region, but we are playing safe |
|
1677 for (TInt r = 0; r < segment->iRegion.Count(); ++r) |
|
1678 { |
|
1679 SubtractRectFromSegmentArray(*rect); |
|
1680 ++rect; |
|
1681 } |
|
1682 segment->iRedrawSegmentType = ESegmentTypeRedraw; |
|
1683 } |
|
1684 } |
|
1685 |
|
1686 void CWsRedrawMsgWindow::PromoteAndUpdateAllPendingSegments() |
|
1687 { |
|
1688 for(TInt i =0; i<iRedrawSegments.Count(); i++) |
|
1689 { |
|
1690 CRedrawSegment * segment = iRedrawSegments[i]; |
|
1691 if (segment->iRedrawSegmentType == ESegmentTypePendingRedraw) |
|
1692 { |
|
1693 const TRect * rect = segment->iRegion.RectangleList(); |
|
1694 TInt totalRemovedSegments = 0; |
|
1695 for (TInt r = 0; r < segment->iRegion.Count(); ++r) |
|
1696 { |
|
1697 totalRemovedSegments += SubtractRectFromSegmentArray(*rect); |
|
1698 ++rect; |
|
1699 } |
|
1700 //we need to decrement the loop count to take into account any removed segments so we |
|
1701 //make sure we iterate over every segment in the array. |
|
1702 i-=totalRemovedSegments; |
|
1703 segment->iRedrawSegmentType = ESegmentTypeRedraw; |
|
1704 ScheduleUpdateOfSegment(segment); |
|
1705 } |
|
1706 } |
|
1707 } |