28 #include "pixelformats.h" |
28 #include "pixelformats.h" |
29 #include "multitouch.h" |
29 #include "multitouch.h" |
30 |
30 |
31 #include "monitors.h" |
31 #include "monitors.h" |
32 |
32 |
|
33 #include <kernel/sproperty.h> |
|
34 |
33 //Define these so that emulator generates varying values for gce stride and offset. |
35 //Define these so that emulator generates varying values for gce stride and offset. |
34 //By default in emulator, stride is exactly right for display resolution and offset is zero |
36 //By default in emulator, stride is exactly right for display resolution and offset is zero |
35 //Setting these will identify code which incorrectly calculates these factors instead of requesting them |
37 //Setting these will identify code which incorrectly calculates these factors instead of requesting them |
36 //Note that multiples of 4 bytes are preferred for various reasons. |
38 //Note that multiples of 4 bytes are preferred for various reasons. |
37 //[3/5/07 The Secure presentation burffer ignores stride extra because it uses a windows bitmap header to render.] |
39 //[3/5/07 The Secure presentation burffer ignores stride extra because it uses a windows bitmap header to render.] |
178 DWinsGuiPowerHandler(); |
180 DWinsGuiPowerHandler(); |
179 TBool ProcessEvent(const TRawEvent* aEvent); |
181 TBool ProcessEvent(const TRawEvent* aEvent); |
180 TBool ProcessEventDfc(const TRawEvent* aEvent); |
182 TBool ProcessEventDfc(const TRawEvent* aEvent); |
181 TBool iStandby; |
183 TBool iStandby; |
182 }; |
184 }; |
|
185 |
|
186 class DWinsGuiRotateHandler |
|
187 { |
|
188 public: |
|
189 static DWinsGuiRotateHandler* New(); |
|
190 TBool ProcessEventDfc(const TRawEvent* aEvent); |
|
191 |
|
192 private: |
|
193 RPropertyRef iOrientationProperty; |
|
194 }; |
183 |
195 |
184 static DWinsGuiPowerHandler* WinsGuiPowerHandler; |
196 static DWinsGuiPowerHandler* WinsGuiPowerHandler; |
|
197 |
|
198 static DWinsGuiRotateHandler* WinsGuiRotateHandler; |
|
199 |
185 |
200 |
186 _LIT(KWinsGuiName, "WinsGui"); |
201 _LIT(KWinsGuiName, "WinsGui"); |
187 |
202 |
188 DWinsGuiPowerHandler* DWinsGuiPowerHandler::New() |
203 DWinsGuiPowerHandler* DWinsGuiPowerHandler::New() |
189 { |
204 { |
323 |
338 |
324 // Path through |
339 // Path through |
325 return EFalse; |
340 return EFalse; |
326 } |
341 } |
327 |
342 |
|
343 DWinsGuiRotateHandler* DWinsGuiRotateHandler::New() |
|
344 { |
|
345 DWinsGuiRotateHandler* self = new DWinsGuiRotateHandler(); |
|
346 if (!self) |
|
347 { |
|
348 __KTRACE_OPT(KEXTENSION,Kern::Printf("Failed to alloc DWinsGuiRotateHandler")); |
|
349 return NULL; |
|
350 } |
|
351 |
|
352 // Publish startup mode property |
|
353 _LIT_SECURITY_POLICY_PASS(readPolicy); |
|
354 _LIT_SECURITY_POLICY_C1(writePolicy, ECapabilityWriteDeviceData); |
|
355 |
|
356 TInt r = self->iOrientationProperty.Attach(KUidSystemCategory, KSystemEmulatorOrientationKey); |
|
357 if (r!=KErrNone) |
|
358 { |
|
359 delete self; |
|
360 __KTRACE_OPT(KEXTENSION,Kern::Printf("DWinsGuiRotateHandler RPropertyRef::Attach Failed Err:%d", r)); |
|
361 return NULL; |
|
362 } |
|
363 |
|
364 r = self->iOrientationProperty.Define(RProperty::EInt, readPolicy, writePolicy); |
|
365 |
|
366 return self; |
|
367 } |
|
368 |
|
369 TBool DWinsGuiRotateHandler::ProcessEventDfc(const TRawEvent* aEvent) |
|
370 { |
|
371 //Obtain rotation from ScanCode. |
|
372 TInt rotation = aEvent->ScanCode() - ESpecialKeyBase; |
|
373 TInt r; |
|
374 //Check this is a valid rotation and publish property. |
|
375 if((rotation >= EEmulatorFlipRestore) && (rotation <= EEmulatorFlipRight)) |
|
376 { |
|
377 if (aEvent->Type() == TRawEvent::EKeyDown) |
|
378 { |
|
379 Wins::Self()->WakeupEvent(); |
|
380 __KTRACE_OPT(KEXTENSION,Kern::Printf("Orientation change (%d) event:%x", rotation, aEvent->ScanCode())); |
|
381 r = iOrientationProperty.Set(rotation); |
|
382 __KTRACE_OPT(KEXTENSION, if (r != KErrNone) {Kern::Printf("RProperty::Set Failed Err:%d", r);}); |
|
383 } |
|
384 // Swallow event |
|
385 return ETrue; |
|
386 } |
|
387 return EFalse; |
|
388 } |
|
389 |
|
390 |
328 class EventQ |
391 class EventQ |
329 { |
392 { |
330 enum {ESize = 16}; |
393 enum {ESize = 16}; |
331 public: |
394 public: |
332 EventQ(); |
395 EventQ(); |
1263 return KErrArgument; |
1329 return KErrArgument; |
1264 |
1330 |
1265 switch (rotation) |
1331 switch (rotation) |
1266 { |
1332 { |
1267 case 0: |
1333 case 0: |
1268 iScreens[0]->iScreenRotation = EEmulatorFlipRestore; |
1334 for (i = 0; i < screens; ++i) |
|
1335 { |
|
1336 iScreens[i]->iScreenRotation = EEmulatorFlipRestore; |
|
1337 iScreens[i]->iRotateBuffer = NULL; |
|
1338 } |
1269 break; |
1339 break; |
1270 case 90: |
1340 case 90: |
1271 iScreens[0]->iScreenRotation = EEmulatorFlipRight; |
1341 for (i = 0; i < screens; ++i) |
|
1342 { |
|
1343 iScreens[i]->iScreenRotation = EEmulatorFlipRight; |
|
1344 iScreens[i]->iRotateBuffer = (DWORD*)malloc(iScreens[i]->iScreenWidth*iScreens[i]->iScreenHeight*4); |
|
1345 if(!iScreens[i]->iRotateBuffer) |
|
1346 return KErrNoMemory; |
|
1347 } |
1272 break; |
1348 break; |
1273 case 180: |
1349 case 180: |
1274 iScreens[0]->iScreenRotation = EEmulatorFlipInvert; |
1350 for (i = 0; i < screens; ++i) |
|
1351 { |
|
1352 iScreens[i]->iScreenRotation = EEmulatorFlipInvert; |
|
1353 iScreens[i]->iRotateBuffer = (DWORD*)malloc(iScreens[i]->iScreenWidth*iScreens[i]->iScreenHeight*4); |
|
1354 if(!iScreens[i]->iRotateBuffer) |
|
1355 return KErrNoMemory; |
|
1356 } |
1275 break; |
1357 break; |
1276 case 270: |
1358 case 270: |
1277 iScreens[0]->iScreenRotation = EEmulatorFlipLeft; |
1359 for (i = 0; i < screens; ++i) |
|
1360 { |
|
1361 iScreens[i]->iScreenRotation = EEmulatorFlipLeft; |
|
1362 iScreens[i]->iRotateBuffer = (DWORD*)malloc(iScreens[i]->iScreenWidth*iScreens[i]->iScreenHeight*4); |
|
1363 if(!iScreens[i]->iRotateBuffer) |
|
1364 return KErrNoMemory; |
|
1365 } |
1278 break; |
1366 break; |
1279 default: |
1367 default: |
1280 r = KErrArgument; |
1368 r = KErrArgument; |
1281 } |
1369 } |
1282 if (r != KErrNone) |
1370 if (r != KErrNone) |
1283 return r; |
1371 return r; |
|
1372 |
|
1373 iInitialFlipMsg = TStdScanCode(ESpecialKeyBase + iScreens[0]->iScreenRotation); |
1284 |
1374 |
1285 beg = skipws(next); |
1375 beg = skipws(next); |
1286 |
1376 |
1287 //beg should now point to the keycode |
1377 //beg should now point to the keycode |
1288 TInt key = iKeyboard.GetEPOCKeyCode(TPtrC8((const TUint8*)beg, strlen(beg))); |
1378 TInt key = iKeyboard.GetEPOCKeyCode(TPtrC8((const TUint8*)beg, strlen(beg))); |
1289 if (key == KErrNotFound) |
1379 if (key == KErrNotFound) |
1290 return key; |
1380 return key; |
1291 iInitialFlipMsg = key; |
1381 //Currently no use for key |
1292 } |
1382 } |
1293 |
1383 |
1294 //EmulatorControl messages are a bit like virtual keys |
1384 //EmulatorControl messages are a bit like virtual keys |
1295 wsprintfA(property, "Configuration[%d]EmulatorControl",aId); |
1385 wsprintfA(property, "Configuration[%d]EmulatorControl",aId); |
1296 r = MultiProperty(&DWinsUi::DoDefineEmulatorControl, this, property); |
1386 r = MultiProperty(&DWinsUi::DoDefineEmulatorControl, this, property); |
1764 |
1854 |
1765 TBool DWinsUi::OnDigitizer(TInt aX, TInt aY) const |
1855 TBool DWinsUi::OnDigitizer(TInt aX, TInt aY) const |
1766 { |
1856 { |
1767 if (!iDigitizerEnabled) |
1857 if (!iDigitizerEnabled) |
1768 return EFalse; |
1858 return EFalse; |
1769 switch(CurrentFlipState[0]) |
1859 aX -= iDigitizerOffsetX; |
1770 { |
1860 aY -= iDigitizerOffsetY; |
1771 case EEmulatorFlipRestore: |
|
1772 { |
|
1773 aX -= iDigitizerOffsetX; |
|
1774 aY -= iDigitizerOffsetY; |
|
1775 break; |
|
1776 } |
|
1777 case EEmulatorFlipInvert: |
|
1778 { |
|
1779 aX -= systemIni->iScreens[0]->iScreenWidth - iDigitizerOffsetX - iDigitizerWidth; |
|
1780 aY -= systemIni->iScreens[0]->iScreenHeight - iDigitizerOffsetY - iDigitizerHeight; |
|
1781 break; |
|
1782 } |
|
1783 case EEmulatorFlipRight: |
|
1784 { |
|
1785 TInt oldY = aY; |
|
1786 aY = aX - (systemIni->iScreens[0]->iScreenHeight - iDigitizerOffsetY - iDigitizerHeight); |
|
1787 aX = oldY - iDigitizerOffsetX; |
|
1788 break; |
|
1789 } |
|
1790 case EEmulatorFlipLeft: |
|
1791 { |
|
1792 TInt oldY = aY; |
|
1793 aY = aX - iDigitizerOffsetY; |
|
1794 aX = oldY - (systemIni->iScreens[0]->iScreenWidth - iDigitizerOffsetX - iDigitizerWidth); |
|
1795 break; |
|
1796 } |
|
1797 } |
|
1798 return (TUint(aX) < TUint(iDigitizerWidth) && TUint(aY) < TUint(iDigitizerHeight)); |
1861 return (TUint(aX) < TUint(iDigitizerWidth) && TUint(aY) < TUint(iDigitizerHeight)); |
1799 } |
1862 } |
1800 |
1863 |
1801 LOCAL_C void addMouseEvent(TRawEvent::TType aType,TInt aXpos,TInt aYpos) |
1864 LOCAL_C void addMouseEvent(TRawEvent::TType aType,TInt aXpos,TInt aYpos) |
1802 // |
1865 // |
1888 { |
1952 { |
1889 InvalidateRect(TheWin[i], NULL, false); |
1953 InvalidateRect(TheWin[i], NULL, false); |
1890 SendMessage(TheWin[i], WM_FLIP_MESSAGE, systemIni->iScreens[i]->iScreenRotation,0); |
1954 SendMessage(TheWin[i], WM_FLIP_MESSAGE, systemIni->iScreens[i]->iScreenRotation,0); |
1891 } |
1955 } |
1892 |
1956 |
1893 //pass on the orientation key to the windows server |
1957 //Broadcast new orientation to Symbian OS |
1894 if (aSendFlipKey) |
1958 if (aSendFlipKey) |
1895 { |
1959 { |
1896 if (!WinsGuiPowerHandler->iStandby) |
1960 if (!WinsGuiPowerHandler->iStandby) |
1897 { |
1961 { |
1898 addKeyEvent(TRawEvent::EKeyDown, systemIni->iInitialFlipMsg); |
1962 addKeyEvent(TRawEvent::EKeyDown, systemIni->iInitialFlipMsg); |
1899 addKeyEvent(TRawEvent::EKeyUp, systemIni->iInitialFlipMsg); |
|
1900 } |
1963 } |
1901 else |
1964 else |
1902 { |
1965 { |
1903 //remember the flip message so we can send it to the window server when we come out of standby |
1966 //remember so we can broadcast new orientation to Symbian OS |
1904 SavedFlipMessage = systemIni->iInitialFlipMsg; |
1967 SavedFlipMessage = systemIni->iInitialFlipMsg; |
1905 } |
1968 } |
1906 } |
1969 } |
1907 } |
1970 } |
1908 /** |
1971 /** |
2133 LOCAL_C void MultiChildWndPointer(TUint aMessage,TInt aXpos,TInt aYpos, TInt aZ, TInt aPointerId) |
2196 LOCAL_C void MultiChildWndPointer(TUint aMessage,TInt aXpos,TInt aYpos, TInt aZ, TInt aPointerId) |
2134 // |
2197 // |
2135 // Handle a multi-touch pointer event in the Symbian OS screen window |
2198 // Handle a multi-touch pointer event in the Symbian OS screen window |
2136 // |
2199 // |
2137 { |
2200 { |
|
2201 TInt phoneX, phoneY; |
2138 TRawEvent::TType eventType=TRawEvent::ENone; |
2202 TRawEvent::TType eventType=TRawEvent::ENone; |
2139 CHAR buf[50]; |
2203 CHAR buf[50]; |
2140 |
2204 |
|
2205 systemIni->TranslateMouseCoords(CurrentFlipState[0], aXpos, aYpos, systemIni->iScreens[0]->iScreenWidth, systemIni->iScreens[0]->iScreenHeight, phoneX, phoneY); |
|
2206 |
2141 if (aZ <= TheMultiTouch->iZMaxRange) //negative |
2207 if (aZ <= TheMultiTouch->iZMaxRange) //negative |
2142 { |
2208 { |
2143 eventType = TRawEvent::EPointer3DOutOfRange; |
2209 eventType = TRawEvent::EPointer3DOutOfRange; |
2144 wsprintf((LPTSTR)buf, (LPCTSTR)TEXT("Out Of Range")); |
2210 wsprintf((LPTSTR)buf, (LPCTSTR)TEXT("Out Of Range")); |
2145 SendMessage(hwndStatus, SB_SETTEXT, aPointerId , (LPARAM)(buf)); |
2211 SendMessage(hwndStatus, SB_SETTEXT, aPointerId , (LPARAM)(buf)); |
2146 } |
2212 } |
2147 else |
2213 else |
2148 { |
2214 { |
2149 wsprintf((LPTSTR)buf, (LPCTSTR)TEXT("%d: %d,%d,%d"), aPointerId, aXpos,aYpos,aZ); |
2215 wsprintf((LPTSTR)buf, (LPCTSTR)TEXT("%d: %d,%d,%d"), aPointerId, phoneX,phoneY,aZ); |
2150 SendMessage(hwndStatus, SB_SETTEXT, aPointerId , (LPARAM)(buf)); |
2216 SendMessage(hwndStatus, SB_SETTEXT, aPointerId , (LPARAM)(buf)); |
2151 switch (aMessage) |
2217 switch (aMessage) |
2152 { |
2218 { |
2153 case WM_MOUSEMOVE: |
2219 case WM_MOUSEMOVE: |
2154 { |
2220 { |
2250 eventType=TRawEvent::EButton2Up; |
2316 eventType=TRawEvent::EButton2Up; |
2251 break; |
2317 break; |
2252 } |
2318 } |
2253 if (!WinsGuiPowerHandler->iStandby) |
2319 if (!WinsGuiPowerHandler->iStandby) |
2254 { |
2320 { |
2255 addMouseEvent(eventType, aXpos, aYpos); |
2321 TInt newX, newY; |
|
2322 systemIni->TranslateMouseCoords(CurrentFlipState[0], aXpos, aYpos, systemIni->iScreens[0]->iScreenWidth, systemIni->iScreens[0]->iScreenHeight, newX, newY); |
|
2323 addMouseEvent(eventType, newX, newY); |
2256 } |
2324 } |
2257 } |
2325 } |
2258 |
2326 |
2259 LOCAL_C void FrameWndPointer(TUint message,TInt aXpos,TInt aYpos, TInt aScreenNumber, TInt aPointerId = 0) |
2327 LOCAL_C void FrameWndPointer(TUint message,TInt aXpos,TInt aYpos, TInt aScreenNumber, TInt aPointerId = 0) |
2260 // |
2328 // |
2557 return EFalse; |
2626 return EFalse; |
2558 } |
2627 } |
2559 |
2628 |
2560 TInt frameOffset = masterIni->iBufferSet[screenNumber].iScreenBuffer.iDisplayBufferOffset; |
2629 TInt frameOffset = masterIni->iBufferSet[screenNumber].iScreenBuffer.iDisplayBufferOffset; |
2561 displayBuffer=LPVOID(frameOffset+(char*)displayBuffer); |
2630 displayBuffer=LPVOID(frameOffset+(char*)displayBuffer); |
2562 |
2631 |
2563 PAINTSTRUCT ps; |
|
2564 BeginPaint(hWnd, &ps); |
|
2565 |
|
2566 // Paint directly from the buffer to the window |
|
2567 LPBITMAPINFO info = (LPBITMAPINFO)&masterIni->iBufferSet[screenNumber].iInfo; |
2632 LPBITMAPINFO info = (LPBITMAPINFO)&masterIni->iBufferSet[screenNumber].iInfo; |
2568 WORD width = (WORD)info->bmiHeader.biWidth; |
2633 WORD width = (WORD)info->bmiHeader.biWidth; |
2569 WORD height = (WORD)-info->bmiHeader.biHeight; // stored -ve in info |
2634 WORD height = (WORD)-info->bmiHeader.biHeight; // stored -ve in info |
2570 SetDIBitsToDevice(ps.hdc, |
2635 LPVOID pDisplayBuffer; |
2571 0, 0, // Dst X, Y |
2636 |
|
2637 switch(CurrentFlipState[screenNumber]) |
|
2638 { |
|
2639 case EEmulatorFlipRestore: |
|
2640 { |
|
2641 //No rotation to do. |
|
2642 pDisplayBuffer = displayBuffer; |
|
2643 } |
|
2644 break; |
|
2645 |
|
2646 case EEmulatorFlipInvert: |
|
2647 { |
|
2648 //rotate display buffer: Map phone display onto window buffer which has been rotated |
|
2649 DWORD* pRotatedPtr = systemIni->iScreens[screenNumber]->iRotateBuffer; |
|
2650 for (int y = 0; y < height; y++) |
|
2651 for (int x = 0; x < width; x++) |
|
2652 *pRotatedPtr++ = *((DWORD*)(displayBuffer) + ((height - y - 1) * (width)) + (width - x -1)); |
|
2653 |
|
2654 pDisplayBuffer = systemIni->iScreens[screenNumber]->iRotateBuffer; |
|
2655 } |
|
2656 break; |
|
2657 |
|
2658 case EEmulatorFlipLeft: |
|
2659 { |
|
2660 //rotate display buffer: Map phone display onto window buffer which has been rotated |
|
2661 DWORD* pRotatedPtr = systemIni->iScreens[screenNumber]->iRotateBuffer; |
|
2662 |
|
2663 for (int y = 0; y < height; y++) |
|
2664 for (int x = 0; x < width; x++) |
|
2665 *pRotatedPtr++ = *((DWORD*)(displayBuffer) + (x * height) + (height - y -1)); |
|
2666 |
|
2667 pDisplayBuffer = systemIni->iScreens[screenNumber]->iRotateBuffer; |
|
2668 } |
|
2669 break; |
|
2670 |
|
2671 case EEmulatorFlipRight: |
|
2672 { |
|
2673 //rotate display buffer: Map phone display onto window buffer which has been rotated |
|
2674 DWORD* pRotatedPtr = systemIni->iScreens[screenNumber]->iRotateBuffer; |
|
2675 |
|
2676 for (int y = 0; y < height; y++) |
|
2677 for (int x = 0; x < width; x++) |
|
2678 *pRotatedPtr++ = *((DWORD*)(displayBuffer) + ((width - x - 1) * (height)) + y); |
|
2679 |
|
2680 pDisplayBuffer = systemIni->iScreens[screenNumber]->iRotateBuffer; |
|
2681 } |
|
2682 break; |
|
2683 |
|
2684 default: |
|
2685 { |
|
2686 Kern::Printf("Error PaintWindowFromBuffer - unknown orientation"); |
|
2687 return EFalse; |
|
2688 } |
|
2689 } |
|
2690 |
|
2691 PAINTSTRUCT ps; |
|
2692 BeginPaint(hWnd, &ps); |
|
2693 |
|
2694 SetDIBitsToDevice( ps.hdc, |
|
2695 0, 0, // Dst X, Y |
2572 width, height, // Src W, H |
2696 width, height, // Src W, H |
2573 0, 0, // Src X, Y |
2697 0, 0, // Src X, Y |
2574 0, // Src offset to first line |
2698 0, // Src offset to first line |
2575 height, // Src lines available |
2699 height, // Src lines available |
2576 displayBuffer, // Src pointer to pixels |
2700 pDisplayBuffer, // Src pointer to pixels |
2577 info, // Src info |
2701 info, // Src info |
2578 DIB_RGB_COLORS); |
2702 DIB_RGB_COLORS); |
2579 |
2703 |
2580 EndPaint(hWnd, &ps); |
2704 EndPaint(hWnd, &ps); |
2581 |
2705 |
2582 return TRUE; |
2706 return ETrue; |
2583 } |
2707 } |
2584 |
2708 |
2585 |
2709 |
2586 LOCAL_C void CalcTextPos(TInt aScreen, TInt& aX, TInt& aY) |
2710 LOCAL_C void CalcTextPos(TInt aScreen, TInt& aX, TInt& aY) |
2587 { |
2711 { |
4393 systemIni = masterIni->iSystemInis[0]; |
4515 systemIni = masterIni->iSystemInis[0]; |
4394 |
4516 |
4395 WinsGuiPowerHandler = DWinsGuiPowerHandler::New(); |
4517 WinsGuiPowerHandler = DWinsGuiPowerHandler::New(); |
4396 if (!WinsGuiPowerHandler) |
4518 if (!WinsGuiPowerHandler) |
4397 return KErrNoMemory; |
4519 return KErrNoMemory; |
4398 |
4520 |
|
4521 WinsGuiRotateHandler = DWinsGuiRotateHandler::New(); |
|
4522 if (!WinsGuiRotateHandler) |
|
4523 return KErrNoMemory; |
|
4524 |
4399 TheWin=new HWND[systemIni->iScreens.Count()]; |
4525 TheWin=new HWND[systemIni->iScreens.Count()]; |
4400 TheChildWin=new HWND[systemIni->iScreens.Count()]; |
4526 TheChildWin=new HWND[systemIni->iScreens.Count()]; |
4401 TheScreenBitmap=new HBITMAP[systemIni->iScreens.Count()]; |
4527 TheScreenBitmap=new HBITMAP[systemIni->iScreens.Count()]; |
4402 CurrentFlipState=new TEmulatorFlip[systemIni->iScreens.Count()]; |
4528 CurrentFlipState=new TEmulatorFlip[systemIni->iScreens.Count()]; |
4403 |
4529 |
4404 if(!TheWin || !TheChildWin || !TheScreenBitmap || !CurrentFlipState) |
4530 if(!TheWin || !TheChildWin || !TheScreenBitmap || !CurrentFlipState) |
4405 return KErrNoMemory; |
4531 return KErrNoMemory; |
4406 memset(CurrentFlipState,EEmulatorFlipRestore,systemIni->iScreens.Count()); |
|
4407 |
4532 |
4408 TBufferSet buffer; |
4533 TBufferSet buffer; |
4409 buffer.iDisplayDriverCount = 0; |
4534 buffer.iDisplayDriverCount = 0; |
4410 buffer.iDisplayState = ENormalResolution; |
4535 buffer.iDisplayState = ENormalResolution; |
4411 buffer.iDisplayBuffer = 0; |
4536 buffer.iDisplayBuffer = 0; |