|
1 /* |
|
2 * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: The class to be used from server side for accessing registry. |
|
15 * Hit testing is done using this API. |
|
16 * Part of: Tactile Feedback. |
|
17 * |
|
18 */ |
|
19 |
|
20 |
|
21 #include <tactilefeedbacktrace.h> |
|
22 #include "tactilearearegistry.h" |
|
23 #include "OstTraceDefinitions.h" |
|
24 #ifdef OST_TRACE_COMPILER_IN_USE |
|
25 #include "tactilearearegistryTraces.h" |
|
26 #endif |
|
27 |
|
28 // ======== MEMBER FUNCTIONS ======== |
|
29 |
|
30 // --------------------------------------------------------------------------- |
|
31 // |
|
32 // --------------------------------------------------------------------------- |
|
33 // |
|
34 CTactileAreaRegistry::TTactilePenDownEvent::TTactilePenDownEvent(): |
|
35 iWindowGroupId( -1 ), iWindowHandle( 0 ) |
|
36 { |
|
37 } |
|
38 |
|
39 // --------------------------------------------------------------------------- |
|
40 // |
|
41 // --------------------------------------------------------------------------- |
|
42 // |
|
43 CTactileAreaRegistry::TTactilePenDownEvent::TTactilePenDownEvent( |
|
44 TInt aWindowGroupId, |
|
45 TUint32 aWindowHandle, |
|
46 TRect aFeedbackArea ): |
|
47 iWindowGroupId( aWindowGroupId ), |
|
48 iWindowHandle( aWindowHandle ), |
|
49 iFeedbackArea( aFeedbackArea ) |
|
50 { |
|
51 } |
|
52 |
|
53 // --------------------------------------------------------------------------- |
|
54 // |
|
55 // --------------------------------------------------------------------------- |
|
56 // |
|
57 void CTactileAreaRegistry::TTactilePenDownEvent::Reset() |
|
58 { |
|
59 iWindowGroupId = -1 ; |
|
60 iWindowHandle = 0; |
|
61 } |
|
62 |
|
63 // --------------------------------------------------------------------------- |
|
64 // Constructor. |
|
65 // --------------------------------------------------------------------------- |
|
66 // |
|
67 CTactileAreaRegistry::CTactileAreaRegistry() |
|
68 { |
|
69 } |
|
70 |
|
71 // --------------------------------------------------------------------------- |
|
72 // 2-phased constructor. |
|
73 // --------------------------------------------------------------------------- |
|
74 // |
|
75 EXPORT_C CTactileAreaRegistry* CTactileAreaRegistry::NewL() |
|
76 { |
|
77 CTactileAreaRegistry* self = new ( ELeave ) CTactileAreaRegistry; |
|
78 |
|
79 // We don't need ConstructL in this class for the moment, so we |
|
80 // can just return the created instance right away. |
|
81 return self; |
|
82 } |
|
83 |
|
84 // --------------------------------------------------------------------------- |
|
85 // Destructor. |
|
86 // --------------------------------------------------------------------------- |
|
87 // |
|
88 CTactileAreaRegistry::~CTactileAreaRegistry() |
|
89 { |
|
90 iChunkArray.Close(); |
|
91 iWgArray.Close(); |
|
92 iTactileSemaphore.Close(); |
|
93 } |
|
94 |
|
95 |
|
96 // --------------------------------------------------------------------------- |
|
97 // #1 Find the shared chunk created by client |
|
98 // #2 Open handle to the found chunk |
|
99 // #3 Store window group id and connection handle with the chunk, |
|
100 // and add chunk to array |
|
101 // #4 Open global semaphore in case it is not yet open |
|
102 // --------------------------------------------------------------------------- |
|
103 // |
|
104 EXPORT_C void CTactileAreaRegistry::HandleConnectL( |
|
105 const TTactileFeedbackConnectData& aData ) |
|
106 { |
|
107 TRACE3("CTactileAreaRegistry::HandleConnectL - wgId = %d, Chunk = %S", |
|
108 aData.iWindowGroupId, |
|
109 &aData.iChunkName ); |
|
110 |
|
111 // #1 |
|
112 TFindChunk findChunk( aData.iChunkName ); |
|
113 |
|
114 TFullName chunkFullName; |
|
115 |
|
116 TInt err = findChunk.Next( chunkFullName ); |
|
117 |
|
118 User::LeaveIfError( err ); |
|
119 |
|
120 // #2 |
|
121 TTactileAreaChunk newChunk; |
|
122 |
|
123 err = newChunk.iChunk.OpenGlobal( chunkFullName, ETrue ); |
|
124 |
|
125 User::LeaveIfError( err ); |
|
126 |
|
127 CleanupClosePushL( newChunk.iChunk ); |
|
128 |
|
129 // #3 |
|
130 newChunk.iWindowGroupId = aData.iWindowGroupId; |
|
131 |
|
132 newChunk.iConnectionHandle = |
|
133 ConnectionHandleByWgId( aData.iWindowGroupId ); |
|
134 |
|
135 iChunkArray.AppendL( newChunk ); |
|
136 |
|
137 CleanupStack::Pop(); // newChunk.iChunk |
|
138 |
|
139 // #4 Open global semaphore in case it is not yet open |
|
140 if ( !iTactileSemaphore.Handle() ) |
|
141 { |
|
142 err = iTactileSemaphore.OpenGlobal( KTouchFeedbackSemaphore ); |
|
143 User::LeaveIfError( err ); |
|
144 } |
|
145 } |
|
146 |
|
147 |
|
148 // --------------------------------------------------------------------------- |
|
149 // We handle diconnect by finding client's chunk, closing it and then |
|
150 // removing the entry from chunk array. |
|
151 // |
|
152 // #1 Find the correct entry |
|
153 // #2 Close chunk |
|
154 // #3 Remove entry from chunk array |
|
155 // --------------------------------------------------------------------------- |
|
156 // |
|
157 EXPORT_C void CTactileAreaRegistry::HandleDisconnect( |
|
158 const TTactileFeedbackDisconnectData& aData ) |
|
159 { |
|
160 // #1 |
|
161 for ( TInt i=0; i < iChunkArray.Count(); i++ ) |
|
162 { |
|
163 if ( iChunkArray[i].iWindowGroupId == aData.iWindowGroupId ) |
|
164 { |
|
165 // #2 |
|
166 iChunkArray[i].iChunk.Close(); |
|
167 |
|
168 // #3 |
|
169 iChunkArray.Remove( i ); |
|
170 break; |
|
171 } |
|
172 } |
|
173 } |
|
174 |
|
175 |
|
176 // --------------------------------------------------------------------------- |
|
177 // Here we just store information (e.g. connection handle) about the |
|
178 // newly created window group. |
|
179 // |
|
180 // Notice that we ignore possible error on purpose when adding new item to |
|
181 // the array. Firstly a failure should be almost impossible, and secondly |
|
182 // it would only cause feedback to be disabled for the newly created |
|
183 // window group. |
|
184 // --------------------------------------------------------------------------- |
|
185 // |
|
186 EXPORT_C void CTactileAreaRegistry::HandleWindowGroupCreated( |
|
187 TInt aIdentifier, TUint aConnectionHandle ) |
|
188 { |
|
189 TTactileWgroupItem newItem; |
|
190 |
|
191 newItem.iWindowGroupId = aIdentifier; |
|
192 newItem.iConnectionHandle = aConnectionHandle; |
|
193 |
|
194 iWgArray.Append( newItem ); |
|
195 } |
|
196 |
|
197 |
|
198 // --------------------------------------------------------------------------- |
|
199 // When window group is closed, we remove all information about it from |
|
200 // the array (we don't destroy chunks because it has been done based |
|
201 // on the disconnect request already). |
|
202 // --------------------------------------------------------------------------- |
|
203 // |
|
204 EXPORT_C void CTactileAreaRegistry::HandleWindowGroupClosed( |
|
205 TInt aIdentifier ) |
|
206 { |
|
207 // Loop down so that deleting of items won't mix up the array |
|
208 for ( TInt i = iWgArray.Count()-1; i >= 0; i-- ) |
|
209 { |
|
210 if ( iWgArray[i].iWindowGroupId == aIdentifier ) |
|
211 { |
|
212 iWgArray.Remove( i ); |
|
213 } |
|
214 } |
|
215 } |
|
216 |
|
217 |
|
218 // --------------------------------------------------------------------------- |
|
219 // This is the implementation of hit testing. |
|
220 // |
|
221 // It is assumed that this function is not called unless there is at least |
|
222 // one connected application, and unless iTactileSemaphore has been opened |
|
223 // succesfully. |
|
224 // |
|
225 // #1 Check that we have valid handle to global semaphore. |
|
226 // #2 Find correct chunk based on window group id. |
|
227 // #3 Do actual hit testing in a separate function. |
|
228 // #4 Return feedback type based on hit testing results. |
|
229 // --------------------------------------------------------------------------- |
|
230 // |
|
231 EXPORT_C TTouchLogicalFeedback CTactileAreaRegistry::HitTestPointerEvent( |
|
232 const TPointerEvent& aPointerEvent, |
|
233 TInt aWgIdentifier, |
|
234 TUint32 aWindowHandle ) |
|
235 { |
|
236 TTouchLogicalFeedback feedback(ETouchFeedbackNone); |
|
237 |
|
238 OstTrace0( TACTILE_PERFORMANCE, TACTILE_REGISTRY_HIT_TEST_1, "e_TACTILE_REGISTRY_HIT_TEST 1"); |
|
239 |
|
240 // #1 |
|
241 if ( iTactileSemaphore.Handle() && |
|
242 ( aPointerEvent.iType == TPointerEvent::EButton1Down || |
|
243 aPointerEvent.iType == TPointerEvent::EButton1Up ) ) |
|
244 { |
|
245 // We keep performance trace here instead of beginning of function |
|
246 // so that drag events won't confuse performance measurements. |
|
247 |
|
248 // #2 |
|
249 TInt chunkIndex = ChunkIndexByWindowGroupId( aWgIdentifier ); |
|
250 |
|
251 // #3 If we found the window group where pointer event is going to land, |
|
252 // then search for correct window (and area) in its shared chunk. |
|
253 if ( chunkIndex >= 0 && chunkIndex < iChunkArray.Count() ) |
|
254 { |
|
255 feedback = HitTestChunk( |
|
256 iChunkArray[chunkIndex].iChunk, |
|
257 aPointerEvent, |
|
258 aWgIdentifier, |
|
259 aWindowHandle ); |
|
260 } |
|
261 } |
|
262 |
|
263 OstTrace0( TACTILE_PERFORMANCE, TACTILE_REGISTRY_HIT_TEST_0, "e_TACTILE_REGISTRY_HIT_TEST 0"); |
|
264 |
|
265 // #4 |
|
266 return feedback; |
|
267 } |
|
268 |
|
269 // --------------------------------------------------------------------------- |
|
270 // In this function we do hit testing for one chunk. |
|
271 // |
|
272 // There is currently support for feedback on both |
|
273 // pen down and up -events. |
|
274 // |
|
275 // #1 Reset last pen down information in case this was a pen down -event. |
|
276 // #2 Call wait on global semaphore for mutual exlusion of shared memory. |
|
277 // #3 Start from the beginning of chunk, and read number of windows first. |
|
278 // #4 Go through all windows in a loop |
|
279 // --------------------------------------------------------------------------- |
|
280 // |
|
281 TTouchLogicalFeedback CTactileAreaRegistry::HitTestChunk( |
|
282 RChunk& aChunk, |
|
283 const TPointerEvent& aPointerEvent, |
|
284 TInt aWgIdentifier, |
|
285 TUint32 aWindowHandle ) |
|
286 { |
|
287 // #1 |
|
288 if ( aPointerEvent.iType == TPointerEvent::EButton1Down ) |
|
289 { |
|
290 iLastPenDown.Reset(); |
|
291 } |
|
292 |
|
293 TTouchLogicalFeedback feedback = ETouchFeedbackNone; |
|
294 |
|
295 // #2 Protect shared memory chunks so that nobody can modify |
|
296 // them in the middle of hit testing. |
|
297 iTactileSemaphore.Wait(); |
|
298 |
|
299 // #3 |
|
300 TInt* base = (TInt*) aChunk.Base(); |
|
301 |
|
302 TInt windowCount = *base; |
|
303 base++; |
|
304 |
|
305 TBool windowFound(EFalse); |
|
306 |
|
307 // #4 Iterate though windows |
|
308 for ( TInt i=0; i < windowCount && !windowFound; i++ ) |
|
309 { |
|
310 // Read handle identifier of this window |
|
311 TInt wsHandle = *base; |
|
312 base++; |
|
313 // Read number of areas registered to this window |
|
314 TInt areaCount = *base; |
|
315 base++; |
|
316 // Read offset to the area data of this window |
|
317 TInt offset = *base; |
|
318 base++; |
|
319 |
|
320 // Check if this window is the one where pointer event hit. |
|
321 if ( wsHandle == static_cast<TInt>( aWindowHandle ) ) |
|
322 { |
|
323 // Set pointer to the first area belonging to this window. |
|
324 TFeedbackChunkAreaEntry* entryPtr = |
|
325 reinterpret_cast<TFeedbackChunkAreaEntry*>( aChunk.Base() + offset ); |
|
326 |
|
327 TBool matchFound = EFalse; |
|
328 |
|
329 // Go through all areas and test each one against the pointer |
|
330 // event as long as a match is found. |
|
331 for ( TInt j = 0; j < areaCount && !matchFound; j++ ) |
|
332 { |
|
333 matchFound = HitTestRegistryEntry( aPointerEvent, |
|
334 *entryPtr, |
|
335 aWgIdentifier, |
|
336 aWindowHandle, |
|
337 feedback ); |
|
338 |
|
339 entryPtr++; |
|
340 } |
|
341 |
|
342 // No need to continue in the loop, |
|
343 // because window found already |
|
344 windowFound = ETrue; |
|
345 } |
|
346 } |
|
347 |
|
348 // Release the semaphore so that applications can do updates to |
|
349 // registry again. |
|
350 iTactileSemaphore.Signal(); |
|
351 |
|
352 return feedback; |
|
353 } |
|
354 |
|
355 |
|
356 // --------------------------------------------------------------------------- |
|
357 // Here we analyse the pointer event type against feedback area entry, that |
|
358 // is located in same window where the pointer event hit. |
|
359 // |
|
360 // This functionality is encapsulated into separate function mainly |
|
361 // because this is the only part of the implementation that needs |
|
362 // to be modified in case we'll have different kinds of down- and up event |
|
363 // combinations (there could be different types for e.g. in situations were |
|
364 // up event is / is not required to match down event for it to trigger |
|
365 // feedback). |
|
366 // |
|
367 // Here we also record details of pen down events (that trigger feedback), so |
|
368 // that we can check on pen up event in case it matches the same feedback |
|
369 // area where down -event landed. |
|
370 // |
|
371 // #1 First see if pointer event even hit the area |
|
372 // |
|
373 // #2 Currently pen down event always generates feedback |
|
374 // |
|
375 // #3 We trigger feedback on pen up event only if it matches the same |
|
376 // feedback area, where corresponding down event hit. |
|
377 // |
|
378 // --------------------------------------------------------------------------- |
|
379 // |
|
380 TBool CTactileAreaRegistry::HitTestRegistryEntry( |
|
381 const TPointerEvent& aPointerEvent, |
|
382 const TFeedbackChunkAreaEntry& aEntry, |
|
383 TInt aWgIdentifier, |
|
384 TUint32 aWindowHandle, |
|
385 TTouchLogicalFeedback& aFeedback ) |
|
386 { |
|
387 TBool matchFound = EFalse; |
|
388 |
|
389 // #1 |
|
390 if ( aEntry.iRect.Contains( aPointerEvent.iPosition) ) |
|
391 { |
|
392 TInt enablers = aEntry.iFeedbackType & ( KTactileVibraBitDown | |
|
393 KTactileAudioBitDown | |
|
394 KTactileVibraBitUp | |
|
395 KTactileAudioBitUp ); |
|
396 TInt feedbackDown = aEntry.iFeedbackType & 0x3FF; // the first 10 bits |
|
397 TInt feedbackUp = aEntry.iFeedbackType & (0x3FF << 10); // next 10 bits |
|
398 feedbackUp = feedbackUp >> 10; |
|
399 // #2 Pointer down on the area always triggers feedback |
|
400 if ( ( aPointerEvent.iType == TPointerEvent::EButton1Down )) |
|
401 { |
|
402 matchFound = ETrue; |
|
403 aFeedback = static_cast<TTouchLogicalFeedback>( feedbackDown ); |
|
404 iLastPenDown = |
|
405 TTactilePenDownEvent( |
|
406 aWgIdentifier, aWindowHandle, aEntry.iRect ); |
|
407 } |
|
408 // #3 |
|
409 else if ( ( aPointerEvent.iType == TPointerEvent::EButton1Up )) |
|
410 { |
|
411 if ( iLastPenDown.iWindowGroupId == aWgIdentifier && |
|
412 iLastPenDown.iWindowHandle == aWindowHandle && |
|
413 iLastPenDown.iFeedbackArea == aEntry.iRect ) |
|
414 { |
|
415 matchFound = ETrue; |
|
416 aFeedback = static_cast<TTouchLogicalFeedback>( feedbackUp ); |
|
417 |
|
418 // Can only match agains same pen down event once |
|
419 iLastPenDown.Reset(); |
|
420 } |
|
421 } |
|
422 if ( matchFound ) |
|
423 { |
|
424 aFeedback = static_cast<TTouchLogicalFeedback>(aFeedback | enablers); |
|
425 } |
|
426 } |
|
427 return matchFound; |
|
428 } |
|
429 |
|
430 |
|
431 // --------------------------------------------------------------------------- |
|
432 // Find the chunk of that application, where pointer event hit. |
|
433 // |
|
434 // #1 First determine the window server client connection handle (we use |
|
435 // this to indentify chunk instead of window group id, because this way |
|
436 // resolving works the same way also for additional window groups) |
|
437 // |
|
438 // #2 Go though the chunk array for finding out the correct one. |
|
439 // --------------------------------------------------------------------------- |
|
440 // |
|
441 TInt CTactileAreaRegistry::ChunkIndexByWindowGroupId( TInt aWgIdentifier ) const |
|
442 { |
|
443 TInt chunkIndex = KErrNotFound; |
|
444 |
|
445 // #1 |
|
446 TUint connectionHandle = ConnectionHandleByWgId( aWgIdentifier ); |
|
447 |
|
448 // #2 Iterate though chunks to find the correct one |
|
449 for ( TInt chunk = 0; chunk < iChunkArray.Count() && chunkIndex == KErrNotFound; chunk++ ) |
|
450 { |
|
451 if ( iChunkArray[chunk].iConnectionHandle == connectionHandle ) |
|
452 { |
|
453 chunkIndex = chunk; |
|
454 } |
|
455 } |
|
456 |
|
457 return chunkIndex; |
|
458 } |
|
459 |
|
460 |
|
461 // --------------------------------------------------------------------------- |
|
462 // Here we scan through the window group array, and return the corresponding |
|
463 // connection handle in case a match is found. |
|
464 // --------------------------------------------------------------------------- |
|
465 // |
|
466 TUint CTactileAreaRegistry::ConnectionHandleByWgId( TInt aWgIdentifier ) const |
|
467 { |
|
468 TUint connectionHandle = 0; |
|
469 |
|
470 for ( TInt i=0; i < iWgArray.Count() && !connectionHandle; i++ ) |
|
471 { |
|
472 if ( iWgArray[i].iWindowGroupId == aWgIdentifier ) |
|
473 { |
|
474 connectionHandle = iWgArray[i].iConnectionHandle; |
|
475 } |
|
476 } |
|
477 |
|
478 return connectionHandle; |
|
479 } |
|
480 |
|
481 |
|
482 |
|
483 |